Advocates API
Read your advocates: their status, tags, public profile, and, with an extra permission, their contact details, address, and custom fields.
Requires the advocates permission. Personal data also requires advocates:pii. See Personal data.
| Endpoint | Returns |
|---|---|
GET /api/v1/advocates |
A paginated list of advocates |
GET /api/v1/advocates/:id |
One advocate |
List advocates
GET https://integrations.stokedhq.com/api/v1/advocates
With no filters, the list contains every advocate in your community: pending, active, and inactive, including advocates who have been deleted or whose personal data has been erased. See Deleted and erased advocates.
Parameters
| Parameter | Description |
|---|---|
filter[status] |
One or more of pending, active, inactive, separated by commas: filter[status]=pending,active |
filter[created_since] |
Only advocates created at or after this time |
filter[updated_since] |
Only advocates updated at or after this time. Use this for incremental syncing. |
page[number] |
The page to return. Defaults to 1. |
page[size] |
Advocates per page. Defaults to 50, maximum 200. |
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.
Any other parameter — including sort — returns a 400 error. Advocates always come back least recently updated first.
Request
cURL
curl -G "https://integrations.stokedhq.com/api/v1/advocates" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/vnd.api+json" \
--data-urlencode "filter[status]=active"
Ruby
require "net/http"
require "json"
uri = URI("https://integrations.stokedhq.com/api/v1/advocates")
uri.query = URI.encode_www_form("filter[status]" => "active")
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/advocates",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Accept": "application/vnd.api+json",
},
params={"filter[status]": "active"},
)
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/advocates?filter[status]=active");
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/advocates");
url.searchParams.set("filter[status]", "active");
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
This key has both advocates and advocates:pii.
{
"data": [
{
"type": "advocates",
"id": "01k5exampleadv0cate0000001",
"attributes": {
"first_name": "Sarah",
"last_name": "Example",
"email": "sarah@example.com",
"phone_number": "+12025550100",
"handle": "sarah-e",
"profile_url": "https://example-bikes.stokedhq.com/sarah-e",
"status": "active",
"tags": [
"cargo-bike"
],
"headline": "Longtail owner, school-run veteran",
"description": "Ask me anything about carrying two kids and the groceries.",
"display_name": "Sarah",
"display_location": "Springfield, IL",
"address": {
"address_1": "123 Example St",
"address_2": null,
"city": "Springfield",
"state": "IL",
"postal_code": "62701",
"country_code": "USA"
},
"latitude": 39.8,
"longitude": -89.65,
"custom_fields": {
"bike_model": "Longtail",
"accessories": [
"Rain cover",
"Child seats"
]
},
"visible_on_map": true,
"conversations_count": 1,
"created_at": "2026-08-20T14:00:00Z",
"updated_at": "2026-09-01T15:00:00Z",
"deleted_at": null
},
"relationships": {
"conversations": {
"links": {
"related": "https://integrations.stokedhq.com/api/v1/conversations?filter%5Badvocate%5D=01k5exampleadv0cate0000001"
}
}
},
"links": {
"self": "https://integrations.stokedhq.com/api/v1/advocates/01k5exampleadv0cate0000001"
},
"meta": {
"pii": true
}
}
],
"links": {
"self": "https://integrations.stokedhq.com/api/v1/advocates?filter%5Bstatus%5D=active&page%5Bnumber%5D=1&page%5Bsize%5D=50",
"first": "https://integrations.stokedhq.com/api/v1/advocates?filter%5Bstatus%5D=active&page%5Bnumber%5D=1&page%5Bsize%5D=50",
"prev": null,
"next": null,
"last": "https://integrations.stokedhq.com/api/v1/advocates?filter%5Bstatus%5D=active&page%5Bnumber%5D=1&page%5Bsize%5D=50"
},
"meta": {
"page": 1,
"per_page": 50,
"total_count": 1,
"total_pages": 1
}
}
Get an advocate
GET https://integrations.stokedhq.com/api/v1/advocates/:id
Returns the same record as the list. An ID that doesn’t exist in your community returns 404.
The id is the same value a conversation gives you in relationships.advocate, and that relationship’s links.related is this URL.
Request
cURL
curl "https://integrations.stokedhq.com/api/v1/advocates/01k5exampleadv0cate0000001" \
-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/advocates/01k5exampleadv0cate0000001")
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/advocates/01k5exampleadv0cate0000001",
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/advocates/01k5exampleadv0cate0000001");
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/advocates/01k5exampleadv0cate0000001");
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
{
"data": {
"type": "advocates",
"id": "01k5exampleadv0cate0000001",
"attributes": {
"first_name": "Sarah",
"last_name": "Example",
"email": "sarah@example.com",
"phone_number": "+12025550100",
"handle": "sarah-e",
"profile_url": "https://example-bikes.stokedhq.com/sarah-e",
"status": "active",
"tags": [
"cargo-bike"
],
"headline": "Longtail owner, school-run veteran",
"description": "Ask me anything about carrying two kids and the groceries.",
"display_name": "Sarah",
"display_location": "Springfield, IL",
"address": {
"address_1": "123 Example St",
"address_2": null,
"city": "Springfield",
"state": "IL",
"postal_code": "62701",
"country_code": "USA"
},
"latitude": 39.8,
"longitude": -89.65,
"custom_fields": {
"bike_model": "Longtail",
"accessories": [
"Rain cover",
"Child seats"
]
},
"visible_on_map": true,
"conversations_count": 1,
"created_at": "2026-08-20T14:00:00Z",
"updated_at": "2026-09-01T15:00:00Z",
"deleted_at": null
},
"relationships": {
"conversations": {
"links": {
"related": "https://integrations.stokedhq.com/api/v1/conversations?filter%5Badvocate%5D=01k5exampleadv0cate0000001"
}
}
},
"links": {
"self": "https://integrations.stokedhq.com/api/v1/advocates/01k5exampleadv0cate0000001"
},
"meta": {
"pii": true
}
}
}
Personal data
Attributes marked advocates:pii below are only returned to a key that has the advocates:pii permission as well as advocates.
Without it, those attributes are left out of the response entirely rather than set to null, so null always means “no value” and never “not permitted”. Each record’s meta.pii tells you which kind of response you received.
The same advocate, read with a key that has only advocates:
{
"data": {
"type": "advocates",
"id": "01k5exampleadv0cate0000001",
"attributes": {
"handle": "sarah-e",
"profile_url": "https://example-bikes.stokedhq.com/sarah-e",
"status": "active",
"tags": [
"cargo-bike"
],
"headline": "Longtail owner, school-run veteran",
"description": "Ask me anything about carrying two kids and the groceries.",
"visible_on_map": true,
"conversations_count": 1,
"created_at": "2026-08-20T14:00:00Z",
"updated_at": "2026-09-01T15:00:00Z",
"deleted_at": null
},
"relationships": {
"conversations": {
"links": {
"related": "https://integrations.stokedhq.com/api/v1/conversations?filter%5Badvocate%5D=01k5exampleadv0cate0000001"
}
}
},
"links": {
"self": "https://integrations.stokedhq.com/api/v1/advocates/01k5exampleadv0cate0000001"
},
"meta": {
"pii": false
}
}
}
headline and description are not gated: they are text the advocate wrote for their public profile, already shown on your community site. Note that an advocate is free to write their own name into either one.
handle and profile_url are not gated either, because the profile page is public. Handles are usually built from the advocate’s name and city (naomi-in-alexandria), so treat them as identifying. Use id, which is a random identifier, to match advocates against your own records.
Advocate attributes
Records with "type": "advocates".
| Attribute | Type | Requires | Description |
|---|---|---|---|
first_name |
string | advocates:pii |
|
last_name |
string or null | advocates:pii |
|
email |
string or null | advocates:pii |
|
phone_number |
string or null | advocates:pii |
In E.164 format, for example +12025550100 |
handle |
string | The advocate’s unique handle, the last part of their profile URL. Usually built from their name and city. | |
profile_url |
string or null | The advocate’s public profile on your community site. | |
status |
string | pending, active, or inactive |
|
tags |
array of strings | Names of the advocate’s tags, in the order set in Settings | |
headline |
string or null | The headline on their public profile | |
description |
string or null | The bio on their public profile | |
display_name |
string | advocates:pii |
The name shown publicly: their custom display name, or their first name |
display_location |
string or null | advocates:pii |
The location shown publicly, for example “Springfield, IL” |
address |
object or null | advocates:pii |
The advocate’s address. null if none is on file. |
latitude |
number or null | advocates:pii |
Map coordinates of the address |
longitude |
number or null | advocates:pii |
|
custom_fields |
object | advocates:pii |
Your community’s custom fields. See The custom_fields object. |
visible_on_map |
boolean | Whether the advocate has chosen to appear on your community map | |
conversations_count |
integer | Number of conversations the advocate has been part of | |
created_at |
timestamp | When the advocate was added | |
updated_at |
timestamp | When the advocate last changed. See What changes updated_at. |
|
deleted_at |
timestamp or null | When the advocate was removed from your community. null for current advocates. Removed advocates stay in the API so your copy can mark them, and removing one updates updated_at. |
The address object
| Field | Type | Description |
|---|---|---|
address_1 |
string or null | Street address |
address_2 |
string or null | Apartment, suite, or unit |
city |
string or null | |
state |
string or null | State, province, or region |
postal_code |
string or null | |
country_code |
string | Three-letter ISO 3166-1 country code, for example USA |
The custom_fields object
One key per custom field defined in Settings > Advocate Custom Fields, named by the field’s identifier (the same name the CSV export uses after custom_field_). Every field is present for every advocate; the value is null when the advocate hasn’t answered.
Values are strings, formatted as they are in the CSV export (a checkbox is "Yes" or "No"), except multiple-select fields, which are arrays of the selected options — an empty array when unanswered.
Relationships
| Relationship | Description |
|---|---|
conversations |
links.related is the conversations list filtered to this advocate (filter[advocate]). Reading it requires the conversations permission. |
What changes updated_at
An advocate’s updated_at moves forward whenever anything in their API record changes:
- Their name, contact details, handle, or status changes
- Their profile, address, or map location is edited
- A custom field value changes, or a custom field is added, renamed, or deleted (every advocate carries a key for every field, so all of them change)
- A tag is added, removed, renamed, or reordered
- A conversation with them is started
- They are deleted, or their personal data is erased
It can also move when something outside the API record changes, such as their avatar. Updating by id makes the repeat harmless.
Deleted and erased advocates
Deleting an advocate in the admin portal keeps their record so their conversations still make sense, and the API keeps returning it. When an advocate’s personal data is erased (for example, after a privacy request), their record stays too, with the personal data overwritten: first_name becomes [redacted], other contact fields become null or a placeholder, and the address and custom field values are removed.
Erasure moves updated_at, so your next incremental sync receives the redacted record. Apply it to your copy like any other update so the personal data is removed from your systems too.