Wallet Transactions API
Read the history behind your advocates’ wallet balances: every credit, debit and reset, with the balance it left behind, who made it, and what earned it. The current balances are on the advocate record; this is how they got there.
Requires the wallet_transactions permission. The balances on the advocate record only need advocates, but the history also carries the reasons admins write, so it has a permission of its own.
| Endpoint | Returns |
|---|---|
GET /api/v1/wallet_transactions |
A paginated list of wallet transactions |
GET /api/v1/wallet_transactions/:id |
One wallet transaction |
List wallet transactions
GET https://integrations.stokedhq.com/api/v1/wallet_transactions
With no filters, the list contains every transaction on every wallet in your community, deactivated advocates included. Transactions are never edited after they are made, so updated_at is the time the transaction happened.
Parameters
| Parameter | Description |
|---|---|
filter[advocate] |
Only transactions on this advocate’s wallets, by advocate id |
filter[created_since] |
Only transactions made at or after this time |
filter[updated_since] |
The same as filter[created_since], since transactions never change. Use it for incremental syncing like on every other endpoint. |
page[size] |
Records 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. |
An advocate id that doesn’t belong to your community returns a 400 error rather than an empty list. Any other parameter returns 400. Transactions come back oldest first.
Request
cURL
curl -G "https://integrations.stokedhq.com/api/v1/wallet_transactions" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/vnd.api+json" \
--data-urlencode "filter[updated_since]=2026-09-01T00:00:00Z"
Ruby
require "net/http"
require "json"
uri = URI("https://integrations.stokedhq.com/api/v1/wallet_transactions")
uri.query = URI.encode_www_form("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/wallet_transactions",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Accept": "application/vnd.api+json",
},
params={"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/wallet_transactions?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/wallet_transactions");
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
{
"data": [
{
"type": "wallet_transactions",
"id": "01k5examplewallettxn000001",
"attributes": {
"kind": "credit",
"amount": 25.0,
"balance_after": 150.0,
"lifetime_after": 325.0,
"currency_code": "PTS",
"wallet_id": "01k5examplewallet000000001",
"reason": "Test ride completed",
"automatic": true,
"created_at": "2026-09-06T18:00:00Z",
"updated_at": "2026-09-06T18:00:00Z"
},
"relationships": {
"advocate": {
"data": {
"type": "advocates",
"id": "01k5exampleadv0cate0000001"
},
"links": {
"related": "https://integrations.stokedhq.com/api/v1/advocates/01k5exampleadv0cate0000001"
}
},
"performed_by": {
"data": null
},
"subject": {
"data": {
"type": "conversations",
"id": "01k5examplec0nversat10n001"
},
"links": {
"related": "https://integrations.stokedhq.com/api/v1/conversations/01k5examplec0nversat10n001"
}
}
},
"links": {
"self": "https://integrations.stokedhq.com/api/v1/wallet_transactions/01k5examplewallettxn000001"
}
},
{
"type": "wallet_transactions",
"id": "01k5examplewallettxn000002",
"attributes": {
"kind": "debit",
"amount": 50.0,
"balance_after": 100.0,
"lifetime_after": 325.0,
"currency_code": "PTS",
"wallet_id": "01k5examplewallet000000001",
"reason": "Redeemed for a Squad hoodie",
"automatic": false,
"created_at": "2026-09-12T10:00:00Z",
"updated_at": "2026-09-12T10:00:00Z"
},
"relationships": {
"advocate": {
"data": {
"type": "advocates",
"id": "01k5exampleadv0cate0000001"
},
"links": {
"related": "https://integrations.stokedhq.com/api/v1/advocates/01k5exampleadv0cate0000001"
}
},
"performed_by": {
"data": {
"type": "admins",
"id": "01k5exampleadm1n0000000001"
}
},
"subject": {
"data": null
}
},
"links": {
"self": "https://integrations.stokedhq.com/api/v1/wallet_transactions/01k5examplewallettxn000002"
}
}
],
"links": {
"self": "https://integrations.stokedhq.com/api/v1/wallet_transactions?filter%5Bupdated_since%5D=2026-09-01T00%3A00%3A00Z&page%5Bnumber%5D=1&page%5Bsize%5D=50",
"first": "https://integrations.stokedhq.com/api/v1/wallet_transactions?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/wallet_transactions?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": 2,
"total_pages": 1,
"wallets": [
{
"id": "01k5examplewallet000000001",
"name": "Squad Points",
"unit_name": "points",
"currency_code": "PTS"
}
]
}
}
Get a wallet transaction
GET https://integrations.stokedhq.com/api/v1/wallet_transactions/:id
Returns the same record as the list. An ID that doesn’t exist in your community returns 404.
cURL
curl "https://integrations.stokedhq.com/api/v1/wallet_transactions/01k5examplewallettxn000001" \
-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/wallet_transactions/01k5examplewallettxn000001")
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/wallet_transactions/01k5examplewallettxn000001",
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/wallet_transactions/01k5examplewallettxn000001");
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/wallet_transactions/01k5examplewallettxn000001");
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);
Attributes
Records with "type": "wallet_transactions".
| Attribute | Type | Description |
|---|---|---|
kind |
string | credit (points added), debit (points spent or removed), or reset (an admin set the balance and lifetime directly) |
amount |
number or null | How much was credited or debited, in whole units: 25.0 is 25 points, or $25.00 in a cash wallet. Always positive. null for a reset, which sets the totals instead. |
balance_after |
number | The wallet’s balance right after this transaction |
lifetime_after |
number | The wallet’s lifetime total right after this transaction. A debit leaves it unchanged. |
currency_code |
string | PTS for a points wallet, or an ISO 4217 code (USD, CAD) for a cash wallet |
wallet_id |
string | Which wallet: its entry in meta.wallets, and the same id as in the advocate’s wallets array. |
reason |
string | The reason shown to the advocate, such as Test ride completed. Free text written by an admin or by the reward that made the credit. |
automatic |
boolean | true when Stoked made the transaction itself (an auto-approved reward, for example), false when an admin did |
created_at |
timestamp | When the transaction happened |
updated_at |
timestamp | The same as created_at |
Every response carries the community’s wallets in its top-level meta.wallets: an array of {id, name, unit_name, currency_code}, one per wallet, in Settings order. Look up a transaction’s wallet_id there to show 25 Squad Points without fetching an advocate.
To rebuild a wallet’s balance on any date, take the balance_after of its last transaction before that date. For transactions made before this endpoint launched in October 2026, balance_after and lifetime_after were worked out afterwards by replaying the wallet’s history in order.
Relationships
| Relationship | Type | Description |
|---|---|---|
advocate |
advocates |
The advocate whose wallet it is |
performed_by |
admins |
The admin who made the transaction. null when automatic is true. |
subject |
conversations, private_events, prospects, … |
What the reward was for, when the transaction came from an approved reward (or its reversal) about a record the API serves: for a test ride reward, usually the conversation. null for transactions an admin entered by hand and for resets. |
A relationship carries only a type, id and link. Admins have no endpoint, so an admins identifier has no link. Following a link needs the matching permission: the link is a pointer, not a grant.
Keeping a copy in sync
- On the first run, follow
links.nextthrough everything and upsert each transaction byid - Remember the newest
updated_atyou received - On the next run, pass it as
filter[updated_since], followlinks.nextto the end, and upsert each transaction byid