# Events

> Part of the Cowliss docs. Markdown for every page: append `index.md` to its URL. Site map: /llms.txt

<!-- GENERATED by apps/docs/scripts/generate-api-reference.ts. Do not edit. Regenerate with `pnpm -F cowliss-docs generate`. -->

These routes provide read views over your event history. Events are append-only and never rewritten. Ingestion writes are on the [Ingestion](/reference/api/ingestion/) page.

## GET /v1/events

List events, newest first

**Auth:** Session token.

**Query parameters**

```json
{
  "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**

```json
{
  "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](/api-conventions/#errors).

## GET /v1/events/stats

Event volume and per-app breakdown

**Auth:** Session token.

**Query parameters**

```json
{
  "days": 30
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `days` | integer | no | default `30`; 1 ≤ x ≤ 90 |

**Response 200**

```json
{
  "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](/api-conventions/#errors).

## GET /v1/events/stats/names

Per-event-name volume and last-seen

**Auth:** Session token.

**Query parameters**

```json
{
  "days": 30
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `days` | integer | no | default `30`; 1 ≤ x ≤ 90 |

**Response 200**

```json
{
  "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](/api-conventions/#errors).

## 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](/guides/keys/).

**Query parameters**

```json
{
  "name": "string"
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `name` | string \| string[] | yes |  |

**Response 200**

```json
{
  "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](/api-conventions/#errors).

## 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**

```json
{
  "days": 7,
  "listing": "listed"
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `days` | integer | no | default `7`; 1 ≤ x ≤ 14 |
| `listing` | "listed" \| "unlisted" \| "not_seen" | no |  |

**Response 200**

```json
{
  "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](/api-conventions/#errors).

## 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**

```json
{
  "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](/api-conventions/#errors).

## 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**

```json
{
  "n": 5
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `n` | integer | no | default `5`; 1 ≤ x ≤ 20 |

**Response 200**

```json
{
  "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](/api-conventions/#errors).

## GET /v1/events/{id}

Fetch one event

**Auth:** Session token.

**Response 200**

```json
{
  "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](/api-conventions/#errors).
