# Project events API reference

> Canonical HTML: https://screenrig.ai/docs/api/events
> Source SHA-256: e0c997a2ab813502f0d99a41d2ed1d3d6b26dbaf1a3d7e514c1cce0278ff8422

Durable project events. This group has 2 operations, each with its authentication, parameters, request body fields, request template and responses. The [API overview](https://screenrig.ai/docs/api.md) covers services, authentication, requests and retries, problem codes and versioning.

Replace uppercase placeholders. Save a schema-valid JSON body in request.json when required. Cookie examples use a local cookie jar from an authenticated session.

## Durable project events

### GET /api/events

**List Events**

Lists durable project events ascending by sequence.

Paging is contiguous: pass the returned next_cursor as after to continue exactly where the page stopped. next_cursor is the cursor of the last returned event while newer events already exist, and null at the end of the history. limit defaults to 50 and accepts 1-200; a value outside that range is refused with invalid_request and an errors[] member naming limit.

Offset paging is not supported. source=log lists the Player log instead: the condition reports Players send (runtime.reported) ascending by receipt time, filtered by screen_id and severity over a window from to (default the last 24 hours, at most 31 days).

Its cursors are opaque: lg1_ or ev1_ by where the server keeps the Player log, so pass back the one a page returned; a cursor of the other kind is refused with invalid_request naming the field. Player log records have no sequence, are not on GET /stream/project and are not delivered to webhooks.

A record is acknowledged when the Player log queue accepts it and is kept for the project's event retention. A poll with the last cursor and the default window follows the log. Player log records are not in the default (source=project) list. from and to are refused with invalid_request unless source is log.

The project list takes screen_id, playlist_id and application_id to read the events of one screen, playlist or application, all scoped to the project of the credential (an id names which events to read, never whose): screen_id keeps the events whose resource is that screen; playlist_id the events whose resource is that playlist plus the events that name it as details.playlist_id (a screen taking it); application_id the events whose resource is that application plus those whose resource carries it as application_id (its releases and key/value entries).

A value that is not an id of its kind is refused with invalid_request naming the field; playlist_id and application_id are refused with source=log, whose records belong to a screen only.

Both sources take resource_id, type and severity (a minimum), combined with each other, with the id filters and with paging; a Player log record is a runtime.reported record whose resource is its screen, so a resource_id that is not that screen or a type that excludes runtime.reported answers an empty page. before (or order=desc) pages newest first: next_cursor is then the cursor of the oldest returned item and passed back as before continues to older items.

Without from, a newest-first source=log page reaches past the default 24 hours to the oldest record the project's event retention keeps, at most 31 days below the page's upper edge per request. A newest-first source=log page may hold fewer than limit records, even none, with a next_cursor; null still ends it, at the end of the retained log.

A caller limited to screens (docs/screen-scope.md) reads a filtered feed: events of screens inside its scope, of the screen folders it can see (its own, the folders above and inside; screen.moved only when the destination is inside its scope), of campaigns, playlists, media, applications and cards, and playlist_change reviews.

Every other event is left out, so a page can hold fewer events than limit, even none, with a next_cursor; keep paging with it. source=log needs a screen_id inside its scope. Events about the project itself are not sent.


Operation: listEvents.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| after | query | optional | A cursor from a previous response: ev1_ for project events (and the event streams). The Player log (source=log) pages with lg1_ or ev1_ cursors by where the server keeps it; treat them as opaque and pass back the one a page returned. A cursor of another kind is refused with invalid_request. |
| before | query | optional | Pages newest first, strictly older than this cursor: a next_cursor of a newest-first page, or any item cursor (ev1_ for project events; for source=log the opaque cursor its items carry). Empty or absent with order=desc starts at the newest item. |
| order | query | optional | asc pages oldest first with after; desc pages newest first with before. before implies desc; order=asc with before is refused. |
| limit | query | optional | Page size. A value outside 1-200 is refused with invalid_request and an errors[] member whose field is limit. |
| source | query | optional | project lists the sequenced project events. log lists the Player log: Player condition reports (runtime.reported), ascending by receipt time. |
| resource_id | query | optional | Only items whose resource.id is this id (a screen, playlist, media, webhook, agent or application id). With source=log it is the screen of the record. |
| type | query | optional | An exact event type such as playlist.updated, or a family prefix ending in a dot such as playlist. for every playlist.* type. |
| screen_id | query | optional | Only the events of this screen: with source=project those whose resource is the screen, with source=log its Player log records. Another id kind is refused with invalid_request. |
| playlist_id | query | optional | With source=project, only the events of this playlist: those whose resource is the playlist and those naming it as details.playlist_id. Refused with source=log. |
| application_id | query | optional | With source=project, only the events of this application: those whose resource is the application and those whose resource carries it as application_id (its releases and key/value entries). Refused with source=log. |
| severity | query | optional | Minimum severity: info is every item, warning keeps warnings and errors, error keeps errors. Both sources. |
| from | query | optional | With source=log, the start of the window (inclusive). Defaults to 24 hours before to; a newest-first page without from reaches back to the project's event retention instead. |
| to | query | optional | With source=log, the end of the window (exclusive). Defaults to now. The window is at most 31 days. |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/events'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Durable events (source=project) or Player log records (source=log) | EventList (application/json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /stream/project

**Stream Events**

Replayable project listen stream. Player log records (runtime.reported) are read with GET /api/events?source=log and are not on this stream. Opening this request costs 1 credit as the listen subscribe. Credit headers are omitted. Later billed events cost 1 credit each; screen.*, runtime.*, alert_mute.*, and application.event are exempt.

Heartbeats and the retry preamble never charge. stream.cursor and stream.resync_required are billed. Debit idempotency is sse-event:<project_id>:<sequence>, so reconnect replay is free. An unpaid billed event is not written and the stream closes.

A stream opened with a dashboard session bills nothing (neither the open nor its events), like the dashboard's API requests; it still takes one of the project's stream slots. A caller limited to screens (docs/screen-scope.md) receives the same filtered feed as GET /api/events: no frame is sent for a dropped event and stream.cursor keeps the position moving.

The scope is read when the stream opens and the folder tree again on a move or folder event; a narrowed scope applies to an open stream when it reconnects.


Operation: streamEvents.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| after | query | optional | A cursor from a previous response: ev1_ for project events (and the event streams). The Player log (source=log) pages with lg1_ or ev1_ cursors by where the server keeps it; treat them as opaque and pass back the one a page returned. A cursor of another kind is refused with invalid_request. |
| Last-Event-ID | header | optional | Durable cursor used when after is absent. |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/stream/project'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Replayable SSE stream. The query cursor takes precedence over Last-Event-ID. Invalid, future, or pre-retention cursors receive stream.resync_required at the current head and close. No ScreenRig-Credits-* headers. | string (text/event-stream) |
| 402 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |


## Sitemap

See the [public documentation sitemap](https://screenrig.ai/sitemap.md).
