Events
These routes provide read views over your event history. Events are append-only and never rewritten. Ingestion writes are on the Ingestion page.
GET /v1/events
Section titled “GET /v1/events”List events, newest first
Auth: Session token.
Query parameters
{ "cursor": "string", "limit": 50, "direction": "desc", "appId": "string", "sourceId": "string", "event": "string", "profileId": "string"}| Field | Type | Required | Constraints |
|---|---|---|---|
cursor |
string | no | |
limit |
integer | no | default 50; 1 ≤ x ≤ 100 |
direction |
“asc” | “desc” | no | default "desc" |
appId |
string | no | length 0–200 |
sourceId |
string | no | length 1–200 |
event |
string | no | length 1–200 |
profileId |
string | no | length 1–200 |
Response 200
{ "data": [ { "id": "string", "orgId": "string", "appId": "string", "sourceId": "string", "profileId": "string", "event": "string", "properties": { "key": {} }, "timestamp": "2026-01-15T09:30:00.000Z", "receivedAt": "2026-01-15T09:30:00.000Z", "messageId": "string", "client": "string", "sourceName": "string", "keyId": "string" } ], "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object[] | yes | |
data[] |
object | yes | |
data[].id |
string | yes | |
data[].orgId |
string | yes | |
data[].appId |
string | yes | |
data[].sourceId |
string | yes | |
data[].profileId |
string | yes | |
data[].event |
string | yes | |
data[].properties |
record<string, any> | yes | |
data[].timestamp |
string (date-time) | yes | |
data[].receivedAt |
string (date-time) | yes | |
data[].messageId |
string | null | yes | |
data[].client |
string | null | yes | |
data[].sourceName |
string | null | yes | |
data[].keyId |
string | null | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
GET /v1/events/stats
Section titled “GET /v1/events/stats”Event volume and per-app breakdown
Auth: Session token.
Query parameters
{ "days": 30}| Field | Type | Required | Constraints |
|---|---|---|---|
days |
integer | no | default 30; 1 ≤ x ≤ 90 |
Response 200
{ "data": { "days": 1, "total": 42, "volume": [ { "day": "string", "count": 42, "apps": { "key": 42 } } ], "byApp": [ { "appId": "string", "name": "string", "count": 42 } ] }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.days |
integer | yes | 1 ≤ x ≤ 90 |
data.total |
integer | yes | 0 ≤ x ≤ ∞ |
data.volume |
object[] | yes | |
data.volume[] |
object | yes | |
data.volume[].day |
string | yes | |
data.volume[].count |
integer | yes | 0 ≤ x ≤ ∞ |
data.volume[].apps |
record<string, integer> | yes | |
data.byApp |
object[] | yes | |
data.byApp[] |
object | yes | |
data.byApp[].appId |
string | yes | |
data.byApp[].name |
string | yes | |
data.byApp[].count |
integer | yes | 0 ≤ x ≤ ∞ |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
GET /v1/events/stats/names
Section titled “GET /v1/events/stats/names”Per-event-name volume and last-seen
Auth: Session token.
Query parameters
{ "days": 30}| Field | Type | Required | Constraints |
|---|---|---|---|
days |
integer | no | default 30; 1 ≤ x ≤ 90 |
Response 200
{ "data": { "days": 1, "names": [ { "event": "string", "count": 42, "lastSeenAt": "2026-01-15T09:30:00.000Z" } ] }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.days |
integer | yes | 1 ≤ x ≤ 90 |
data.names |
object[] | yes | |
data.names[] |
object | yes | |
data.names[].event |
string | yes | |
data.names[].count |
integer | yes | 0 ≤ x ≤ ∞ |
data.names[].lastSeenAt |
string (date-time) | null | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
GET /v1/apps/{appId}/events/seen
Section titled “GET /v1/apps/{appId}/events/seen”Whether an app has ever received these event names
A name may be a pattern where * matches any run of characters against the whole name. A pattern reports the latest timestamp among matching events, or null if the app has never received one.
Auth: Session token, or an org pipeline key.
Query parameters
{ "name": "string"}| Field | Type | Required | Constraints |
|---|---|---|---|
name |
string | string[] | yes |
Response 200
{ "data": { "names": [ { "name": "string", "lastSeenAt": "2026-01-15T09:30:00.000Z" } ] }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.names |
object[] | yes | |
data.names[] |
object | yes | |
data.names[].name |
string | yes | |
data.names[].lastSeenAt |
string (date-time) | null | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
GET /v1/apps/{id}/event-names
Section titled “GET /v1/apps/{id}/event-names”The event names an app receives, against its cow.json list
Every name the app received in the last 30 days, with its count over days, a daily count for the last 14 days, when it was last seen and the property keys it carried. When the app’s cow.json has an events list, each name is listed (with the entry that matches it), unlisted (add it to events and push), or not_seen (listed, nothing in 30 days); an entry nothing ever matched comes back as its own not_seen row. With no list, listing is null throughout. The tag changes nothing about how an event is stored or used.
Auth: Session token.
Query parameters
{ "days": 7, "listing": "listed"}| Field | Type | Required | Constraints |
|---|---|---|---|
days |
integer | no | default 7; 1 ≤ x ≤ 14 |
listing |
“listed” | “unlisted” | “not_seen” | no |
Response 200
{ "data": { "days": 1, "events": [ "string" ], "pushedAt": "2026-01-15T09:30:00.000Z", "counts": { "all": 42, "listed": 42, "unlisted": 42, "notSeen": 42 }, "names": [ { "event": "string", "listing": "listed", "entry": "string", "count": 42, "series": [ 42 ], "lastSeenAt": "2026-01-15T09:30:00.000Z", "propertyKeys": [ "string" ] } ] }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.days |
integer | yes | 1 ≤ x ≤ 14 |
data.events |
string[] | null | yes | |
data.pushedAt |
string (date-time) | null | yes | |
data.counts |
object | yes | |
data.counts.all |
integer | yes | 0 ≤ x ≤ ∞ |
data.counts.listed |
integer | yes | 0 ≤ x ≤ ∞ |
data.counts.unlisted |
integer | yes | 0 ≤ x ≤ ∞ |
data.counts.notSeen |
integer | yes | 0 ≤ x ≤ ∞ |
data.names |
object[] | yes | |
data.names[] |
object | yes | |
data.names[].event |
string | yes | |
data.names[].listing |
“listed” | “unlisted” | “not_seen” | null | yes | |
data.names[].entry |
string | null | yes | |
data.names[].count |
integer | yes | 0 ≤ x ≤ ∞ |
data.names[].series |
integer[] | yes | |
data.names[].lastSeenAt |
string (date-time) | null | yes | |
data.names[].propertyKeys |
string[] | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
GET /v1/events/{id}/effects
Section titled “GET /v1/events/{id}/effects”What one event caused
The automation executions the event started, directly or through a segment it moved its user into; the waits it answered; the segments it moved its user into or out of; and the send each of those executions made next. An event recorded before causes were kept lists none of them.
Auth: Session token.
Response 200
{ "data": { "started": [ { "executionId": "string", "appId": "string", "automationKey": "string", "status": "running", "startedAt": "2026-01-15T09:30:00.000Z", "viaSegment": { "id": "string", "name": "string" }, "nextDelivery": { "id": "string", "step": "string", "channel": "email", "status": "sent", "createdAt": "2026-01-15T09:30:00.000Z" } } ], "resumed": [ { "executionId": "string", "appId": "string", "automationKey": "string", "resumedAt": "2026-01-15T09:30:00.000Z", "nextDelivery": { "id": "string", "step": "string", "channel": "email", "status": "sent", "createdAt": "2026-01-15T09:30:00.000Z" } } ], "segments": [ { "segmentId": "string", "segmentName": "string", "direction": "entered", "at": "2026-01-15T09:30:00.000Z" } ], "matchingAutomations": [ "string" ] }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.started |
object[] | yes | |
data.started[] |
object | yes | |
data.started[].executionId |
string | yes | |
data.started[].appId |
string | yes | |
data.started[].automationKey |
string | yes | |
data.started[].status |
“running” | “waiting” | “completed” | “failed” | “cancelled” | yes | |
data.started[].startedAt |
string (date-time) | yes | |
data.started[].viaSegment |
object | null | yes | |
data.started[].viaSegment.id |
string | yes | |
data.started[].viaSegment.name |
string | yes | |
data.started[].nextDelivery |
object | null | yes | |
data.started[].nextDelivery.id |
string | yes | |
data.started[].nextDelivery.step |
string | yes | |
data.started[].nextDelivery.channel |
“email” | “webhook” | yes | |
data.started[].nextDelivery.status |
“sent” | “delivered” | “bounced” | “complained” | “failed” | “skipped_disabled” | “skipped_paused” | “skipped_suspended” | “skipped_suppressed” | “skipped_quota” | “skipped_unknown_purpose” | “skipped_frequency_cap” | “skipped_consent” | “skipped_domain” | “skipped_ssrf” | “skipped_spam” | “would_send” | “would_skip_disabled” | “would_skip_paused” | “would_skip_suspended” | “would_skip_suppressed” | “would_skip_quota” | “would_skip_unknown_purpose” | “would_skip_frequency_cap” | “would_skip_consent” | “would_skip_domain” | “would_skip_ssrf” | “would_skip_spam” | yes | |
data.started[].nextDelivery.createdAt |
string (date-time) | yes | |
data.resumed |
object[] | yes | |
data.resumed[] |
object | yes | |
data.resumed[].executionId |
string | yes | |
data.resumed[].appId |
string | yes | |
data.resumed[].automationKey |
string | yes | |
data.resumed[].resumedAt |
string (date-time) | yes | |
data.resumed[].nextDelivery |
object | null | yes | |
data.resumed[].nextDelivery.id |
string | yes | |
data.resumed[].nextDelivery.step |
string | yes | |
data.resumed[].nextDelivery.channel |
“email” | “webhook” | yes | |
data.resumed[].nextDelivery.status |
“sent” | “delivered” | “bounced” | “complained” | “failed” | “skipped_disabled” | “skipped_paused” | “skipped_suspended” | “skipped_suppressed” | “skipped_quota” | “skipped_unknown_purpose” | “skipped_frequency_cap” | “skipped_consent” | “skipped_domain” | “skipped_ssrf” | “skipped_spam” | “would_send” | “would_skip_disabled” | “would_skip_paused” | “would_skip_suspended” | “would_skip_suppressed” | “would_skip_quota” | “would_skip_unknown_purpose” | “would_skip_frequency_cap” | “would_skip_consent” | “would_skip_domain” | “would_skip_ssrf” | “would_skip_spam” | yes | |
data.resumed[].nextDelivery.createdAt |
string (date-time) | yes | |
data.segments |
object[] | yes | |
data.segments[] |
object | yes | |
data.segments[].segmentId |
string | yes | |
data.segments[].segmentName |
string | yes | |
data.segments[].direction |
“entered” | “exited” | yes | |
data.segments[].at |
string (date-time) | yes | |
data.matchingAutomations |
string[] | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
GET /v1/events/{id}/around
Section titled “GET /v1/events/{id}/around”The same user’s events around one event
Up to n events before it and n after it, each side oldest first, across every profile merged into the user.
Auth: Session token.
Query parameters
{ "n": 5}| Field | Type | Required | Constraints |
|---|---|---|---|
n |
integer | no | default 5; 1 ≤ x ≤ 20 |
Response 200
{ "data": { "before": [ { "id": "string", "orgId": "string", "appId": "string", "sourceId": "string", "profileId": "string", "event": "string", "properties": { "key": {} }, "timestamp": "2026-01-15T09:30:00.000Z", "receivedAt": "2026-01-15T09:30:00.000Z", "messageId": "string", "client": "string" } ], "after": [ { "id": "string", "orgId": "string", "appId": "string", "sourceId": "string", "profileId": "string", "event": "string", "properties": { "key": {} }, "timestamp": "2026-01-15T09:30:00.000Z", "receivedAt": "2026-01-15T09:30:00.000Z", "messageId": "string", "client": "string" } ] }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.before |
object[] | yes | |
data.before[] |
object | yes | |
data.before[].id |
string | yes | |
data.before[].orgId |
string | yes | |
data.before[].appId |
string | yes | |
data.before[].sourceId |
string | yes | |
data.before[].profileId |
string | yes | |
data.before[].event |
string | yes | |
data.before[].properties |
record<string, any> | yes | |
data.before[].timestamp |
string (date-time) | yes | |
data.before[].receivedAt |
string (date-time) | yes | |
data.before[].messageId |
string | null | yes | |
data.before[].client |
string | null | yes | |
data.after |
object[] | yes | |
data.after[] |
object | yes | |
data.after[].id |
string | yes | |
data.after[].orgId |
string | yes | |
data.after[].appId |
string | yes | |
data.after[].sourceId |
string | yes | |
data.after[].profileId |
string | yes | |
data.after[].event |
string | yes | |
data.after[].properties |
record<string, any> | yes | |
data.after[].timestamp |
string (date-time) | yes | |
data.after[].receivedAt |
string (date-time) | yes | |
data.after[].messageId |
string | null | yes | |
data.after[].client |
string | null | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
GET /v1/events/{id}
Section titled “GET /v1/events/{id}”Fetch one event
Auth: Session token.
Response 200
{ "data": { "id": "string", "orgId": "string", "appId": "string", "sourceId": "string", "profileId": "string", "event": "string", "properties": { "key": {} }, "timestamp": "2026-01-15T09:30:00.000Z", "receivedAt": "2026-01-15T09:30:00.000Z", "messageId": "string", "client": "string", "sourceName": "string", "keyId": "string" }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.id |
string | yes | |
data.orgId |
string | yes | |
data.appId |
string | yes | |
data.sourceId |
string | yes | |
data.profileId |
string | yes | |
data.event |
string | yes | |
data.properties |
record<string, any> | yes | |
data.timestamp |
string (date-time) | yes | |
data.receivedAt |
string (date-time) | yes | |
data.messageId |
string | null | yes | |
data.client |
string | null | yes | |
data.sourceName |
string | null | yes | |
data.keyId |
string | null | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.