Conversations API

Read the conversations between your advocates and prospects, including tags, the AI summary, the resolution, every message, and every beat — the system-recorded moments on the timeline, like a signed waiver or a blocked prospect.

Requires the conversations permission.

Endpoint Returns
GET /api/v1/conversations A paginated list of conversations, each with all of its messages and beats
GET /api/v1/conversations/:id One conversation with all of its messages and beats

List conversations

GET https://integrations.stokedhq.com/api/v1/conversations

With no filters, the list contains every conversation in your community — pending, active, and closed. This differs from the admin portal’s Conversations list, which hides closed conversations by default.

Parameters

Parameter Description
filter[status] One or more of pending, active, closed, separated by commas: filter[status]=pending,active
filter[created_since] Only conversations created at or after this time
filter[updated_since] Only conversations updated at or after this time. Use this for incremental syncing.
filter[advocate] Only conversations with this advocate, by advocate id
filter[prospect] Only conversations with this prospect, by prospect id
page[size] Conversations per page. Defaults to 50, maximum 200.
page[after] The cursor from a previous response’s links.next. See Pagination.
page[number] A page to jump to. Defaults to 1.

Time filters accept an ISO 8601 timestamp with a time zone (2026-09-01T00:00:00Z) or a date (2026-09-01, read as midnight UTC). Both are inclusive.

filter[advocate] and filter[prospect] can be combined with each other and with the other filters. An id that doesn’t belong to an advocate or prospect in your community returns a 400 error rather than an empty list. An advocate or prospect record links straight to its filtered list in relationships.conversations.links.related.

Any other parameter — including sort — returns a 400 error. Conversations always come back least recently updated first.

Request

GET /api/v1/conversations

cURL

curl -G "https://integrations.stokedhq.com/api/v1/conversations" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/vnd.api+json" \
--data-urlencode "filter[status]=active,closed" \
--data-urlencode "filter[updated_since]=2026-09-01T00:00:00Z"

Ruby

require "net/http"
require "json"
uri = URI("https://integrations.stokedhq.com/api/v1/conversations")
uri.query = URI.encode_www_form("filter[status]" => "active,closed", "filter[updated_since]" => "2026-09-01T00:00:00Z")
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer YOUR_API_KEY"
request["Accept"] = "application/vnd.api+json"
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }
raise "HTTP #{response.code}: #{response.body}" unless response.is_a?(Net::HTTPSuccess)
puts JSON.parse(response.body)["data"]

Python

import requests
response = requests.get(
"https://integrations.stokedhq.com/api/v1/conversations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Accept": "application/vnd.api+json",
},
params={"filter[status]": "active,closed", "filter[updated_since]": "2026-09-01T00:00:00Z"},
)
response.raise_for_status()
print(response.json()["data"])

C#

using System.Net.Http.Headers;
using System.Text.Json;
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "YOUR_API_KEY");
client.DefaultRequestHeaders.Accept.ParseAdd("application/vnd.api+json");
using var response = await client.GetAsync("https://integrations.stokedhq.com/api/v1/conversations?filter[status]=active,closed&filter[updated_since]=2026-09-01T00:00:00Z");
response.EnsureSuccessStatusCode();
using var document = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
Console.WriteLine(document.RootElement.GetProperty("data"));

JavaScript

const url = new URL("https://integrations.stokedhq.com/api/v1/conversations");
url.searchParams.set("filter[status]", "active,closed");
url.searchParams.set("filter[updated_since]", "2026-09-01T00:00:00Z");
const response = await fetch(url, {
headers: {
Authorization: "Bearer YOUR_API_KEY",
Accept: "application/vnd.api+json",
},
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
const { data } = await response.json();
console.log(data);

Response

Response conversations-index.json Download
{
"data": [
{
"type": "conversations",
"id": "01k5examplec0nversat10n001",
"attributes": {
"status": "closed",
"messages_count": 4,
"beats_count": 1,
"tags": [
"reason/test-ride"
],
"created_at": "2026-09-01T15:00:00Z",
"updated_at": "2026-09-06T17:30:00Z",
"activated_at": "2026-09-01T15:00:00Z",
"closed_at": "2026-09-06T17:30:00Z",
"last_message_at": "2026-09-01T15:20:00Z",
"first_advocate_reply_seconds": 300,
"summary": {
"who_and_what": "Mike asked Sarah about trying her cargo bike for school drop-off. They arranged a Saturday test ride.",
"status": "Closed after a test ride.",
"prospect_sentiment": 0.8,
"prospect_sentiment_level": "positive",
"advocate_sentiment": 0.6,
"advocate_sentiment_level": "positive"
},
"resolution": {
"close_reason": "Test ride completed",
"close_reason_id": "01k5examplecl0sereas0n0001",
"note": "Mike took the test ride and plans to order.",
"closed_at": "2026-09-06T17:30:00Z",
"closed_by": "admin"
}
},
"relationships": {
"advocate": {
"data": {
"type": "advocates",
"id": "01k5exampleadv0cate0000001"
},
"links": {
"related": "https://integrations.stokedhq.com/api/v1/advocates/01k5exampleadv0cate0000001"
}
},
"prospect": {
"data": {
"type": "prospects",
"id": "01k5examplepr0spect0000001"
},
"links": {
"related": "https://integrations.stokedhq.com/api/v1/prospects/01k5examplepr0spect0000001"
}
},
"messages": {
"data": [
{
"type": "messages",
"id": "900001"
},
{
"type": "messages",
"id": "900002"
},
{
"type": "messages",
"id": "900003"
},
{
"type": "messages",
"id": "900004"
}
]
},
"timeline": {
"data": [
{
"type": "messages",
"id": "900001"
},
{
"type": "messages",
"id": "900002"
},
{
"type": "beats",
"id": "01k5examplebeat00000000001"
},
{
"type": "messages",
"id": "900003"
},
{
"type": "messages",
"id": "900004"
}
]
}
},
"links": {
"self": "https://integrations.stokedhq.com/api/v1/conversations/01k5examplec0nversat10n001"
}
}
],
"included": [
{
"type": "messages",
"id": "900001",
"attributes": {
"content": "Hi Sarah! I'm thinking about a cargo bike for school drop-off. Could I try yours?",
"status": "active",
"source": "sms_relay",
"created_at": "2026-09-01T15:05:00Z",
"blocked_at": null,
"moderation": {
"flagged": false,
"categories": []
}
},
"relationships": {
"sender": {
"data": {
"type": "prospects",
"id": "01k5examplepr0spect0000001"
},
"links": {
"related": "https://integrations.stokedhq.com/api/v1/prospects/01k5examplepr0spect0000001"
}
}
}
},
{
"type": "messages",
"id": "900002",
"attributes": {
"content": "Absolutely! I'm free Saturday morning. It fits both of my kids plus groceries.",
"status": "active",
"source": "sms_relay",
"created_at": "2026-09-01T15:10:00Z",
"blocked_at": null,
"moderation": {
"flagged": false,
"categories": []
}
},
"relationships": {
"sender": {
"data": {
"type": "advocates",
"id": "01k5exampleadv0cate0000001"
},
"links": {
"related": "https://integrations.stokedhq.com/api/v1/advocates/01k5exampleadv0cate0000001"
}
}
}
},
{
"type": "messages",
"id": "900003",
"attributes": {
"content": "Hi both, Alex from the team here. Mike, remember to bring a helmet on Saturday!",
"status": "active",
"source": "web",
"created_at": "2026-09-01T15:15:00Z",
"blocked_at": null,
"moderation": {
"flagged": false,
"categories": []
},
"sender_name": "Alex Placeholder"
},
"relationships": {
"sender": {
"data": {
"type": "admins",
"id": "01k5exampleadm1n0000000001"
}
}
}
},
{
"type": "messages",
"id": "900004",
"attributes": {
"content": "Perfect, see you Saturday at 10. Thanks!",
"status": "active",
"source": "sms_relay",
"created_at": "2026-09-01T15:20:00Z",
"blocked_at": null,
"moderation": {
"flagged": false,
"categories": []
}
},
"relationships": {
"sender": {
"data": {
"type": "prospects",
"id": "01k5examplepr0spect0000001"
},
"links": {
"related": "https://integrations.stokedhq.com/api/v1/prospects/01k5examplepr0spect0000001"
}
}
}
},
{
"type": "beats",
"id": "01k5examplebeat00000000001",
"attributes": {
"kind": "waiver_signed",
"summary": "Mike Sample signed the Liability Waiver",
"created_at": "2026-09-01T15:12:00Z"
},
"relationships": {
"subject": {
"data": {
"type": "waiver_submissions",
"id": "01k5examplewa1vers1gn00001"
},
"links": {
"related": "https://integrations.stokedhq.com/api/v1/waiver_submissions/01k5examplewa1vers1gn00001"
}
}
}
}
],
"links": {
"self": "https://integrations.stokedhq.com/api/v1/conversations?filter%5Bupdated_since%5D=2026-09-01T00%3A00%3A00Z&page%5Bnumber%5D=1&page%5Bsize%5D=50",
"first": "https://integrations.stokedhq.com/api/v1/conversations?filter%5Bupdated_since%5D=2026-09-01T00%3A00%3A00Z&page%5Bnumber%5D=1&page%5Bsize%5D=50",
"prev": null,
"next": null,
"last": "https://integrations.stokedhq.com/api/v1/conversations?filter%5Bupdated_since%5D=2026-09-01T00%3A00%3A00Z&page%5Bnumber%5D=1&page%5Bsize%5D=50"
},
"meta": {
"page": 1,
"per_page": 50,
"total_count": 1,
"total_pages": 1,
"close_reasons": [
{
"id": "01k5examplecl0sereas0n0001",
"name": "Test ride completed",
"system": false
}
]
}
}

Every conversation on the page comes with all of its messages and beats:

  • relationships.timeline — the type and id of every message and beat in the conversation, oldest first, in the same order the admin portal timeline shows them. Walk this to render a transcript. See Beat attributes below.
  • relationships.messages — the type and id of each of its messages only, oldest first. Unchanged: it never includes beats.
  • A top-level included array with every message and beat on the page in full

A full sync is therefore a handful of list pages, not one request per conversation. Match a message to its conversation through relationships.messages or relationships.timeline; match a beat through relationships.timeline — there is no relationships.beats, so to collect just the beats, filter included on type == "beats".

The response’s top-level meta.close_reasons lists your community’s close reasons (id, name, and whether it is a built-in system reason), so you can key a resolution by close_reason_id without another request.


Get a conversation

GET https://integrations.stokedhq.com/api/v1/conversations/:id

Returns the same conversation record as the list, with its messages and beats in included. An ID that doesn’t exist in your community returns 404.

Request

GET /api/v1/conversations/01k5examplec0nversat10n001

cURL

curl "https://integrations.stokedhq.com/api/v1/conversations/01k5examplec0nversat10n001" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/vnd.api+json"

Ruby

require "net/http"
require "json"
uri = URI("https://integrations.stokedhq.com/api/v1/conversations/01k5examplec0nversat10n001")
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer YOUR_API_KEY"
request["Accept"] = "application/vnd.api+json"
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }
raise "HTTP #{response.code}: #{response.body}" unless response.is_a?(Net::HTTPSuccess)
puts JSON.parse(response.body)["data"]

Python

import requests
response = requests.get(
"https://integrations.stokedhq.com/api/v1/conversations/01k5examplec0nversat10n001",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Accept": "application/vnd.api+json",
},
)
response.raise_for_status()
print(response.json()["data"])

C#

using System.Net.Http.Headers;
using System.Text.Json;
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "YOUR_API_KEY");
client.DefaultRequestHeaders.Accept.ParseAdd("application/vnd.api+json");
using var response = await client.GetAsync("https://integrations.stokedhq.com/api/v1/conversations/01k5examplec0nversat10n001");
response.EnsureSuccessStatusCode();
using var document = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
Console.WriteLine(document.RootElement.GetProperty("data"));

JavaScript

const url = new URL("https://integrations.stokedhq.com/api/v1/conversations/01k5examplec0nversat10n001");
const response = await fetch(url, {
headers: {
Authorization: "Bearer YOUR_API_KEY",
Accept: "application/vnd.api+json",
},
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
const { data } = await response.json();
console.log(data);

Response

Response conversations-show.json Download
{
"data": {
"type": "conversations",
"id": "01k5examplec0nversat10n001",
"attributes": {
"status": "closed",
"messages_count": 4,
"beats_count": 1,
"tags": [
"reason/test-ride"
],
"created_at": "2026-09-01T15:00:00Z",
"updated_at": "2026-09-06T17:30:00Z",
"activated_at": "2026-09-01T15:00:00Z",
"closed_at": "2026-09-06T17:30:00Z",
"last_message_at": "2026-09-01T15:20:00Z",
"first_advocate_reply_seconds": 300,
"summary": {
"who_and_what": "Mike asked Sarah about trying her cargo bike for school drop-off. They arranged a Saturday test ride.",
"status": "Closed after a test ride.",
"prospect_sentiment": 0.8,
"prospect_sentiment_level": "positive",
"advocate_sentiment": 0.6,
"advocate_sentiment_level": "positive"
},
"resolution": {
"close_reason": "Test ride completed",
"close_reason_id": "01k5examplecl0sereas0n0001",
"note": "Mike took the test ride and plans to order.",
"closed_at": "2026-09-06T17:30:00Z",
"closed_by": "admin"
}
},
"relationships": {
"advocate": {
"data": {
"type": "advocates",
"id": "01k5exampleadv0cate0000001"
},
"links": {
"related": "https://integrations.stokedhq.com/api/v1/advocates/01k5exampleadv0cate0000001"
}
},
"prospect": {
"data": {
"type": "prospects",
"id": "01k5examplepr0spect0000001"
},
"links": {
"related": "https://integrations.stokedhq.com/api/v1/prospects/01k5examplepr0spect0000001"
}
},
"messages": {
"data": [
{
"type": "messages",
"id": "900001"
},
{
"type": "messages",
"id": "900002"
},
{
"type": "messages",
"id": "900003"
},
{
"type": "messages",
"id": "900004"
}
]
},
"timeline": {
"data": [
{
"type": "messages",
"id": "900001"
},
{
"type": "messages",
"id": "900002"
},
{
"type": "beats",
"id": "01k5examplebeat00000000001"
},
{
"type": "messages",
"id": "900003"
},
{
"type": "messages",
"id": "900004"
}
]
}
},
"links": {
"self": "https://integrations.stokedhq.com/api/v1/conversations/01k5examplec0nversat10n001"
}
},
"included": [
{
"type": "messages",
"id": "900001",
"attributes": {
"content": "Hi Sarah! I'm thinking about a cargo bike for school drop-off. Could I try yours?",
"status": "active",
"source": "sms_relay",
"created_at": "2026-09-01T15:05:00Z",
"blocked_at": null,
"moderation": {
"flagged": false,
"categories": []
}
},
"relationships": {
"sender": {
"data": {
"type": "prospects",
"id": "01k5examplepr0spect0000001"
},
"links": {
"related": "https://integrations.stokedhq.com/api/v1/prospects/01k5examplepr0spect0000001"
}
}
}
},
{
"type": "messages",
"id": "900002",
"attributes": {
"content": "Absolutely! I'm free Saturday morning. It fits both of my kids plus groceries.",
"status": "active",
"source": "sms_relay",
"created_at": "2026-09-01T15:10:00Z",
"blocked_at": null,
"moderation": {
"flagged": false,
"categories": []
}
},
"relationships": {
"sender": {
"data": {
"type": "advocates",
"id": "01k5exampleadv0cate0000001"
},
"links": {
"related": "https://integrations.stokedhq.com/api/v1/advocates/01k5exampleadv0cate0000001"
}
}
}
},
{
"type": "messages",
"id": "900003",
"attributes": {
"content": "Hi both, Alex from the team here. Mike, remember to bring a helmet on Saturday!",
"status": "active",
"source": "web",
"created_at": "2026-09-01T15:15:00Z",
"blocked_at": null,
"moderation": {
"flagged": false,
"categories": []
},
"sender_name": "Alex Placeholder"
},
"relationships": {
"sender": {
"data": {
"type": "admins",
"id": "01k5exampleadm1n0000000001"
}
}
}
},
{
"type": "messages",
"id": "900004",
"attributes": {
"content": "Perfect, see you Saturday at 10. Thanks!",
"status": "active",
"source": "sms_relay",
"created_at": "2026-09-01T15:20:00Z",
"blocked_at": null,
"moderation": {
"flagged": false,
"categories": []
}
},
"relationships": {
"sender": {
"data": {
"type": "prospects",
"id": "01k5examplepr0spect0000001"
},
"links": {
"related": "https://integrations.stokedhq.com/api/v1/prospects/01k5examplepr0spect0000001"
}
}
}
},
{
"type": "beats",
"id": "01k5examplebeat00000000001",
"attributes": {
"kind": "waiver_signed",
"summary": "Mike Sample signed the Liability Waiver",
"created_at": "2026-09-01T15:12:00Z"
},
"relationships": {
"subject": {
"data": {
"type": "waiver_submissions",
"id": "01k5examplewa1vers1gn00001"
},
"links": {
"related": "https://integrations.stokedhq.com/api/v1/waiver_submissions/01k5examplewa1vers1gn00001"
}
}
}
}
]
}

Conversation attributes

Records with "type": "conversations".

Attribute Type Description
status string pending (the prospect hasn’t verified their contact details yet), active, or closed
messages_count integer Number of messages in the conversation
beats_count integer Number of beats in the conversation
tags array of strings The tags applied to the conversation. Each tag is given as its path: the tag’s group, a slash, then the tag’s name, for example model/q45. A tag that was never put in a group is in the general group, so it reads general/vip.
created_at timestamp When the prospect started the conversation
updated_at timestamp When the conversation last changed. See What changes updated_at.
activated_at timestamp or null When the conversation became active
closed_at timestamp or null When the conversation was closed. null while it is open. A few conversations closed before April 2025 have a closed_at but no resolution, because closing did not record a reason yet.
last_message_at timestamp or null When the most recent message was sent
first_advocate_reply_seconds integer or null How long the advocate took to first reply to the prospect, in active-hours seconds. null until the advocate has replied. See First reply time.
summary object or null The AI-generated summary and sentiment. null until one has been generated, or when AI summaries are turned off for the community.
resolution object or null How the conversation was most recently closed. null if it has never been closed with a reason.

The summary object

Field Type Description
who_and_what string Who is talking and what about
status string Where the conversation stands now
prospect_sentiment number or null The prospect’s overall tone across the conversation so far, from -1.0 (negative) to 1.0 (positive). null if the prospect hasn’t said enough to judge.
prospect_sentiment_level string or null positive (score of 0.33 or more), neutral, or negative (score of -0.33 or less). The same bands the admin portal shows. null when the score is.
advocate_sentiment number or null The advocate’s overall tone, on the same scale
advocate_sentiment_level string or null The advocate’s band, on the same scale

The summary is regenerated about ten minutes after the last message in a burst, so a brand-new conversation has no summary and a busy one can lag by a few messages. Sentiment describes the whole conversation to date, not the latest message.

The resolution object

Field Type Description
close_reason string The name of the close reason that was selected. Close reasons are defined by your community in Settings, so the names are yours: don’t hard-code the example’s.
close_reason_id string The close reason’s id. Stable across renames, so key on this. The list’s meta.close_reasons maps ids to current names.
note string or null The optional note entered when closing
closed_at timestamp When this resolution was recorded
closed_by string Who closed it: admin, advocate, prospect, or system

First reply time

first_advocate_reply_seconds is the time from the prospect’s first message to the advocate’s first reply after it, for this one conversation. It is measured exactly as the Reply Speed report measures a first reply, so the two agree:

  • Seconds are active-hours seconds, not wall-clock seconds. The clock only runs during your community’s active hours, read in the advocate’s own time zone. To get the wall-clock figure, subtract the two messages’ created_at values yourself.
  • A message the prospect wrote before verifying starts the clock at activated_at, when the advocate could first see it.
  • When the message and the reply both fall inside the same off-hours stretch, wall-clock seconds are used.
  • An opening message from the advocate, before the prospect has written, is not a reply.

The value is worked out when you request it, from your community’s current active hours and the advocate’s current time zone. Changing either changes the value without moving the conversation’s updated_at.

Relationships

Relationship Type Description
advocate advocates The advocate in the conversation. links.related is their Advocates API URL.
prospect prospects The prospect in the conversation. links.related is their Prospects API URL.
messages messages Every message, oldest first. The records are in the response’s included array.
timeline messages, beats Every message and beat in the conversation, oldest first (by created_at, then id) — the same order the admin portal timeline shows. Walk this one to render a transcript; the records are in included.

A conversation never contains the advocate’s or prospect’s details, only their type, id, and link. Fetching the link requires the advocates or prospects permission; a key with only conversations gets a 403 there.

What changes updated_at

A conversation’s updated_at moves forward whenever anything in its API record changes:

  • A message arrives, or a message’s status changes (for example, it is flagged or blocked)
  • A beat is recorded (a waiver is signed, a prospect is blocked or unblocked, someone opts in or out of SMS, an offer is viewed or clicked, a private event is scheduled, cancelled, or completed)
  • The AI summary is created, updated, or removed. Turning AI summaries off for the community deletes its summaries, so every conversation that had one shows as updated once and its summary becomes null.
  • The conversation is activated, closed, resolved, or reopened
  • A tag is added, removed, renamed, or reordered. Renaming a tag, moving it to another group, or renaming its group changes its path and marks the conversation as updated, so filter[updated_since] picks it up.

Message attributes

Records with "type": "messages". Messages appear in the included array of a list page or a single conversation, never on their own.

Attribute Type Description
content string The text of the message
status string active for a normal message. Otherwise pending (still being processed), flagged (held for moderation review), blocked (stopped by moderation), or failed.
source string How the message was sent: sms_relay (by text message) or web (from a Stoked site)
created_at timestamp When the message was sent
blocked_at timestamp or null When the message was blocked. null for messages that weren’t blocked.
moderation object or null The automated moderation result: flagged (boolean) and categories (array of strings, empty when nothing was found). null if the message hasn’t been checked yet.
sender_name string Only present when the sender is not the advocate or prospect: the admin’s name, or System for automated messages

Flagged and blocked messages are included so your copy matches the full transcript. Check status and blocked_at if you only want messages the other person actually received.

Every message is checked by OpenAI’s moderation model as it arrives. moderation.categories uses that model’s category names, for example harassment or self-harm. A message with flagged: true also has status: "flagged"; the categories tell you why.

Relationships

Relationship Type Description
sender advocates, prospects, or admins Who sent the message. null for automated system messages. Compare the id with the conversation’s advocate and prospect to tell who is speaking. Advocate and prospect senders carry a links.related URL; admins don’t, as there is no admins endpoint.

Beat attributes

Records with "type": "beats". A beat is a system-recorded moment on the conversation timeline — something that happened rather than something someone said: a waiver signed, a prospect blocked, an SMS opt-out, an offer viewed. See Conversation Beats for what these look like in the admin portal.

Beats appear in the included array of a list page or a single conversation, never on their own, and only from when beats shipped in the product (May 2026) forward. A waiver signed before then is an ordinary message on the timeline, not a beat.

Attribute Type Description
kind string What happened. See Beat kinds below.
summary string The human-readable sentence the admin timeline shows, for example “Mike Sample signed the Test Ride Waiver.” Never gated by a :pii permission — same rule as message content.
created_at timestamp When the event happened

Beat kinds

Kind What happened
waiver_signed A prospect signed a waiver
prospect_blocked_by_advocate The advocate blocked the prospect
prospect_unblocked_by_advocate The advocate unblocked the prospect
prospect_blocked_by_admin An admin blocked the prospect
prospect_unblocked_by_admin An admin unblocked the prospect
prospect_sms_opted_out The prospect opted out of SMS, for example by texting STOP
prospect_sms_opted_in The prospect opted back into SMS
advocate_sms_opted_out The advocate opted out of SMS
advocate_sms_opted_in The advocate opted back into SMS
offer_viewed The prospect opened a shared offer page from this conversation
offer_clicked The prospect clicked the offer page’s Shop button
private_event_scheduled A private event (for example a test ride) was scheduled
private_event_cancelled A private event was cancelled
private_event_completion_recorded A private event was marked complete

This list grows over time as new kinds of beats ship. Treat kind as open-ended: tolerate a value you don’t recognize instead of erroring, and fall back to summary for anything you don’t special-case.

Relationships

Relationship Type Description
subject varies Only present when the beat’s subject has its own endpoint. A waiver_signed beat points at its waiver submission; prospect_sms_opted_out / prospect_sms_opted_in point at the prospect; advocate_sms_opted_out / advocate_sms_opted_in and prospect_blocked_by_advocate / prospect_unblocked_by_advocate point at the advocate (the one who opted out, blocked or unblocked). Every other kind carries no subject — read summary instead.

links.related on subject is a pointer, not a grant: following it needs the matching permission (waivers, prospects, or advocates), the same as any other relationship link in this API.

Beats about private events and offers don’t have their own endpoint yet, so summary is currently the only detail available for them. A details object (event dates, completion status) and a private events endpoint are planned.


© 2024-2026 Stoked — Real conversations. Real trust.