Skip to main content
POST
Tell us what users do with the posts returned by Place Lookup. These events are how we learn which content earns attention and which drives conversions, which in turn is what lets us return better posts for your venues over time. Send one event per request. A valid event returns 202 Accepted.
The video events must include a slate_id, which comes from the Place Lookup response.

How a slate flows

1

Fetch the posts

Call GET /v1/place once for each set of posts you’re about to show. The response includes a slate_id.
2

Keep the slate_id

Store it alongside the posts in your page state. It belongs to the page, not to any individual post — every event from that set of posts carries the same slate_id.
3

Report interactions

As the user views, plays and moves through the posts, send events with the slate context below. Echo place_id, post_id and slate_id exactly as we served them.
4

Report conversions

Send conversion_cta_click when they tap your call to action, conversion when it goes through, and conversion_outcome once you know how it turned out.

Authentication

string
required
Bearer <your API key>.

Request body

object
required
The event itself. event_type selects which shape applies — the type-specific fields are listed under Event types.
string
ISO-8601 timestamp of when the event happened on your side.
string
Deduplicates retries: two requests with the same key are recorded once. Without it, a retried request is recorded as a second event. Max 200 characters.
string
Optional stable identifier for the user. Max 200 characters.
string
Your own environment label, e.g. staging. Lets us separate your test traffic from real traffic when both point at the production URL. Max 100 characters.

Event types

Each event carries the shared envelope described under Request body; the sections below list what event_data itself must look like for each event_type. Field descriptions are scoped to how they behave for that event.

post_impression — a post became visible

Send one whenever a post reaches 50% of its tile on screen for one continuous second — an IntersectionObserver at threshold: 0.5 plus a one-second timer is the usual implementation. Fire it for the inline tile, and again with surface: "expanded" when the post opens in the expanded player. Required event_data fields Optional event_data fields
event_data
Response — 202 Accepted:

post_video_play — playback started

Send one when a video’s playback begins, whether autoplayed or user-initiated. A separate post_video_end will report where the view stopped. Required event_data fields Optional event_data fields
event_data
Response — 202 Accepted:

post_video_end — playback stopped

Send one each time playback stops, not only when a video finishes — that’s the video reaching its end (ended), the viewer moving to another post (advanced), dismissing the expanded player (closed), or leaving the page (navigated_away). A pause isn’t a stop: hold the event while playback is paused, so a viewer who resumes is reported once when the view really ends, and one who never comes back is reported on unload as navigated_away. Required event_data fields Optional event_data fields
navigated_away fires while the page is unloading, so send it with navigator.sendBeacon() or fetch(..., { keepalive: true }). An ordinary request gets cancelled on unload.
Finished
Abandoned
Response — 202 Accepted:

post_advance — the user moved to another post

post_id and position describe the post being advanced from; to_position gives the destination. Applies on both surfaces — swiping inside the full-screen player counts too. Required event_data fields
tap is accepted for controls that don’t distinguish forward from back, but it carries no direction. Prefer next_click or prev_click whenever you know which control was used, and send to_position so the destination is explicit either way.
Optional event_data fields
event_data
Response — 202 Accepted:

conversion_cta_click — the user tapped your call to action

Send one when the user taps your call to action — “Book a Table”, or whatever the equivalent is on your surface. The slate fields are optional here because a call to action doesn’t have to sit on a post:
  • Tapped on a post — a button on a tile, or one inside the expanded player. Send post_id, slate_id, surface and position so the tap is attributed to the content that drove it.
  • Tapped on the page — a header, footer or sidebar button belonging to the place rather than to any one post. Send place_id and session_id on their own.
Required event_data fields Optional event_data fields
event_data
Response — 202 Accepted:

conversion — the conversion happened

Sent when a booking is made or an order is placed. conversion_id is what a later conversion_outcome matches on to settle this conversion, and session_id ties it back to the browsing that led to it. post_id and slate_id stay optional — a conversion needn’t originate from a particular post — but sending them is what attributes it to the content that drove it. Required event_data fields Optional event_data fields
event_data
Response — 202 Accepted:

conversion_outcome — the outcome of a conversion

Settles a conversion, and doubles as the correction channel: a no_show or cancelled reverses what an earlier conversion recorded. This event carries no session_id, post_id, slate_id or surface of its own — it reaches the browsing context through the conversion_id it settles. Required event_data fields Optional event_data fields
event_data
Response — 202 Accepted:
Fulfilment and revenue figures currently feed analytics on a best-effort path. Treat them as directional rather than a system of record until we tell you otherwise.

Response

boolean
Always true on a 202. The event has been validated and queued.

Errors

Retry 5xx responses and network failures with the same idempotency_key. A 422 isn’t worth retrying — it will fail identically until the payload changes.