# Card data API reference

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

Card data for built-in cards. This group has 22 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.

## Card data for built-in cards

### GET /api/card-types

**List Card Types**

Discover built-in cards from this environment's published catalogue, merged with the vendored floor. Refreshes and schema verification happen in the background; this route never calls the edge bucket. Each entry lists its current copy, its tier and the layouts that tier allows, and every served major.


Operation: listCardTypes.

- Authentication: projectBearer
- 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. |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' 'https://api.screenrig.ai/api/card-types'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Built-in card catalogue, ordered by id | CardCatalogue (application/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 /api/card-types/{id}

**Get Card Type**

The current major's self-contained JSON Schemas for placement params and card-data documents, feed schema, tier, allowed layouts and an example placement. Params marked dynamic come from the data document when a placement binds a data key.

A string param may carry {screen.name}, {screen.timezone} and {screen.field.NAME}, and a location param may be the string {screen.location}; the backend expands them for each screen. Replace example content values before publishing. Unknown ids answer card_not_found (404).


Operation: getCardType.

- Authentication: projectBearer
- 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. |
| id | path | required |  |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' 'https://api.screenrig.ai/api/card-types/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Card property schemas and placement example | CardDetails (application/json) |
| 404 | 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) |

### GET /api/cards

**List Cards**


Operation: listCards.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| project | query | optional |  |
| after | query | optional |  |

#### Request template

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

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Card resource | object (application/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) |

### POST /api/cards

**Create Card**


Operation: createCard.

- Authentication: projectBearer
- Request body: CardCreate

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| type | required | string |  |
| major | optional | integer | minimum: 1 |
| params | required | object |  |
| data | optional | object |  |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/cards'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 201 | Card resource | CardInstance (application/json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 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 /api/cards/{id}

**Get Card**

A global card UID, or a deprecated catalogue type segment. The latter adds Deprecation and Warning headers pointing to /api/card-types.


Operation: getCard.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |

#### Request template

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

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Card resource | inline (application/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) |

### PATCH /api/cards/{id}

**Patch Card**

If-Match optionally compares the card metadata revision (not its data revision); omission updates the current revision. Idempotency-Key binds the method, UID, params and If-Match for 24 hours. A retry replays the recorded result, including 204 after deletion; changed input returns idempotency_mismatch. Stale If-Match returns revision_conflict (412).

Keys are scoped to the card UID.


Operation: patchCard.

- Authentication: projectBearer
- Request body: object

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |
| Idempotency-Key | header | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| params | required | object |  |

#### Request template

~~~sh
curl --request PATCH --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Idempotency-Key: REQUEST_ID' --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/cards/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Card resource | CardInstance (application/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) |

### DELETE /api/cards/{id}

**Delete Card**

If-Match optionally compares the card metadata revision (not its data revision); omission updates the current revision. Idempotency-Key binds the method, UID, params and If-Match for 24 hours. A retry replays the recorded result, including 204 after deletion; changed input returns idempotency_mismatch. Stale If-Match returns revision_conflict (412).

Keys are scoped to the card UID.


Operation: deleteCard.

- Authentication: projectBearer
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |
| Idempotency-Key | header | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |

#### Request template

~~~sh
curl --request DELETE --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Idempotency-Key: REQUEST_ID' 'https://api.screenrig.ai/api/cards/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 204 | Deleted | no body |
| 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 /api/cards/{id}/data

**Get Instance Data**


Operation: getInstanceData.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |

#### Request template

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

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Card resource | CardDataEntry (application/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) |

### PUT /api/cards/{id}/data

**Put Instance Data**


Operation: putInstanceData.

- Authentication: projectBearer
- Request body: object

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |
| If-Match | header | optional |  |
| Idempotency-Key | header | optional |  |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| document | required | object |  |

#### Request template

~~~sh
curl --request PUT --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/cards/RESOURCE_ID/data'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Card resource | CardDataEntry (application/json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 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 /api/cards/{id}/kv-webhooks

**List Card KVWebhooks**

Lists the card's incoming webhooks, oldest first (at most 20). Secrets are never returned here.


Operation: listCardKVWebhooks.

- Authentication: projectBearer
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' 'https://api.screenrig.ai/api/cards/RESOURCE_ID/kv-webhooks'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The card's webhooks. | CardKVWebhookList (application/json) |
| 404 | card_not_found — the card is not in this project. | 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) |

### POST /api/cards/{id}/kv-webhooks

**Create Card KVWebhook**

Creates a webhook that writes this card's data document: an outside system posts a JSON or form body to its url (POST /api/cards/{id}/kv-webhooks/{webhook_id}/write).

Without a mapping the posted object is the document; a mapping template builds it from the body and is checked when saved. signing true also requires a ScreenRig-Signature header. verify makes a vendor's own signature (GitHub, Stripe, Shopify, Slack, Linear, Vercel, Asana or any HMAC sender) authenticate a post in place of the secret, which keeps working beside it; it cannot be combined with signing, and its secret is sealed and never returned, only a hint of it. handshake names a vendor handshake the url answers (Microsoft Graph, monday.com, Trello or Azure Event Grid; verify slack and asana answer theirs).

The answer is the only place the secret and the signing secret appear (with rotate-secret and the update that turns signing on): store them in the sending system at once. A card has at most 20 webhooks (kv_webhook_limit_reached). Appends kv_webhook.created.

An exact retry with the same Idempotency-Key returns the same answer, secrets included, for twenty-four hours.


Operation: createCardKVWebhook.

- Authentication: projectBearer
- Request body: CardKVWebhookCreate

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| description | optional | string | maxLength: 200 |
| key_template | optional | string | Optional card data key template with {screen.field.NAME}. In keyed mode the mapping or posted body produces {documents:[{fields:{NAME:VALUE},document:{...}}]}; each output uses its fields to fill the template. Keys must be unique, valid and outside card-instance and feed namespaces. Every output is checked against the owning card before writes begin. An empty string restores the single-document mode on update. |
| mapping | optional | inline | The template, at most 64 KiB, checked when saved. |
| signing | optional | boolean | default: false |
| verify | optional | WebhookVerifyWrite | A vendor whose own signature authenticates a post in place of the secret, because these vendors cannot send ours. The header and scheme of each are the vendor's own and fixed: github sends X-Hub-Signature-256, sha256= and the hex HMAC-SHA256 of the body; stripe sends Stripe-Signature, t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<body>"> (several v1 while a secret is rolled, t within five minutes); shopify sends X-Shopify-Hmac-Sha256, the base64 HMAC-SHA256 of the body; slack sends X-Slack-Signature, v0= and the hex HMAC-SHA256 of "v0:<X-Slack-Request-Timestamp>:<body>" (within five minutes); linear sends Linear-Signature, the hex HMAC-SHA256 of the body, and the webhookTimestamp member of the body (Unix milliseconds) must be within a minute; vercel sends x-vercel-signature, the hex HMAC-SHA1 of the body; asana sends X-Hook-Signature, the hex HMAC-SHA256 of the body keyed by the X-Hook-Secret of Asana's handshake; generic_hmac is any sender that signs with HMAC-SHA256 in the header you name. A post that carries a valid signature is accepted without the secret, and the secret keeps working beside it. verify cannot be combined with signing. A post is judged on the body as it arrived, before it takes any rate budget. Slack and Asana also answer their handshakes (see WebhookHandshake). |
| handshake | optional | WebhookHandshake | A vendor handshake the URL answers, for a vendor with no signature scheme here. microsoft_graph answers a POST that carries a validationToken query parameter by echoing the decoded token as text/plain. monday answers a JSON POST of {"challenge": "..."} with the same JSON. trello answers its HEAD check with 200. azure_event_grid answers a SubscriptionValidationEvent (aeg-event-type: SubscriptionValidation) with {"validationResponse": "<validationCode>"}. verify.vendor slack answers Slack's signed url_verification challenge, and verify.vendor asana answers Asana's X-Hook-Secret handshake and keeps that secret to verify its events. A handshake is answered only for a webhook set up for that vendor, writes nothing, is never billed and spends no rate budget, and is recorded in the delivery log with outcome handshake. A disabled webhook answers none. Allowed: "microsoft_graph", "monday", "trello", "azure_event_grid" |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/cards/RESOURCE_ID/kv-webhooks'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 201 | Created, with its url, secret and, when it signs, its signing secret. | CardKVWebhookWithSecrets (application/json); headers: Cache-Control |
| 400 | invalid_request, including a mapping that does not compile (errors[0].field names the place in it). | Problem (application/problem+json) |
| 404 | card_not_found — the card is not in this project. | Problem (application/problem+json) |
| 409 | kv_webhook_limit_reached or idempotency_mismatch. | 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) |

### PATCH /api/cards/{id}/kv-webhooks/{webhook_id}

**Update Card KVWebhook**

Changes description, mapping (null removes it), enabled, signing, verify (null removes it; without a secret it keeps the stored one when the vendor is the same) and handshake (null removes it). Turning signing on answers the new signing_secret once; false turns it off. A disabled webhook answers every post like a wrong secret. Appends kv_webhook.updated.


Operation: updateCardKVWebhook.

- Authentication: projectBearer
- Request body: CardKVWebhookUpdate

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |
| webhook_id | path | required | The id of one of the application's key/value webhooks. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| description | optional | string | maxLength: 200 |
| key_template | optional | string | Optional card data key template with {screen.field.NAME}. In keyed mode the mapping or posted body produces {documents:[{fields:{NAME:VALUE},document:{...}}]}; each output uses its fields to fill the template. Keys must be unique, valid and outside card-instance and feed namespaces. Every output is checked against the owning card before writes begin. An empty string restores the single-document mode on update. |
| mapping | optional | inline | Replaces the template; null removes it. |
| enabled | optional | boolean |  |
| signing | optional | boolean |  |
| verify | optional | inline | Replaces the vendor verification; null removes it, and its secret with it. Without a secret it keeps the stored one when the vendor is the same, so a header or window can change alone; a different vendor needs its own secret. An asana verification takes a new handshake only after it is removed and set again. |
| handshake | optional | inline | Sets the handshake the URL answers; null removes it. |

#### Request template

~~~sh
curl --request PATCH --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/cards/RESOURCE_ID/kv-webhooks/WEBHOOK_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The webhook, with signing_secret when this request turned signing on. | CardKVWebhookWithSecrets (application/json); headers: Cache-Control |
| 400 | invalid_request, including a mapping that does not compile. | Problem (application/problem+json) |
| 404 | card_not_found, or not_found — the webhook is not on this card. | Problem (application/problem+json) |
| 409 | idempotency_mismatch. | 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) |

### DELETE /api/cards/{id}/kv-webhooks/{webhook_id}

**Delete Card KVWebhook**

Deletes the webhook. Its url stops working at once; the card keeps its document. Appends kv_webhook.deleted.


Operation: deleteCardKVWebhook.

- Authentication: projectBearer
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |
| webhook_id | path | required | The id of one of the application's key/value webhooks. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request DELETE --header 'Authorization: Bearer [SCREENRIG_TOKEN]' 'https://api.screenrig.ai/api/cards/RESOURCE_ID/kv-webhooks/WEBHOOK_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 204 | Deleted. | no body |
| 404 | card_not_found, or not_found — the webhook is not on this card. | 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) |

### POST /api/cards/{id}/kv-webhooks/{webhook_id}/rotate-secret

**Rotate Card KVWebhook Secret**

Replaces the webhook's secret and returns the new one once; with signing true it replaces the signing secret instead. The old one stops working at once. Appends kv_webhook.secret_rotated.


Operation: rotateCardKVWebhookSecret.

- Authentication: projectBearer
- Request body: CardKVWebhookRotate

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |
| webhook_id | path | required | The id of one of the application's key/value webhooks. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| signing | optional | boolean | default: false |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/cards/RESOURCE_ID/kv-webhooks/WEBHOOK_ID/rotate-secret'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The webhook with its new secret, or its new signing secret. | CardKVWebhookWithSecrets (application/json); headers: Cache-Control |
| 400 | invalid_request — signing true on a webhook that does not sign. | Problem (application/problem+json) |
| 404 | card_not_found, or not_found — the webhook is not on this card. | 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 /api/cards/{id}/kv-webhooks/{webhook_id}/deliveries

**List Card KVWebhook Deliveries**

The webhook's delivery log, newest first, at most 100, kept for seven days.

Each post that named this webhook is a row with its time, outcome (written, unchanged, refused, invalid or handshake), reason (credentials, signature, signature_missing, signature_invalid, timestamp_missing, timestamp_stale, secret_pending, handshake_invalid, disabled, body, mapping, card_data_invalid, rate_limited, payment_required, internal), a short detail such as the card data path that failed, the answer's status, the body size and the number of file parts a multipart post carried (ignored).

No body, header or credential is stored. This is where a project sees why a post got the 404 every refusal shares.


Operation: listCardKVWebhookDeliveries.

- Authentication: projectBearer
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |
| webhook_id | path | required | The id of one of the application's key/value webhooks. |
| limit | query | optional |  |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' 'https://api.screenrig.ai/api/cards/RESOURCE_ID/kv-webhooks/WEBHOOK_ID/deliveries'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The deliveries. | KVWebhookDeliveryList (application/json) |
| 404 | card_not_found, or not_found — the webhook is not on this card. | 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) |

### POST /api/cards/{id}/kv-webhooks/{webhook_id}/write

**Receive Card KVWebhook**

The url an outside system posts a card's data document to. It authenticates with the webhook's own secret in the Authorization header: Bearer <secret>, or HTTP Basic with the webhook id as the username.

When the webhook was created with verify, a valid signature in that vendor's own header (see WebhookVerifyWrite) authenticates the post in place of the secret, which keeps working beside it.

When the webhook signs, ScreenRig-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>" under the signing secret> is also required, and t must be within five minutes of the server's clock.

An unknown card or webhook, a wrong or missing secret or signature, a stale timestamp, a disabled webhook and every GET answer the same 404 not_found; the project sees the reason in the delivery log.

A webhook set up for a vendor handshake answers it first, before any credential is asked for, to a request that is that handshake: Slack's url_verification challenge (signed, answered with the challenge), Microsoft Graph's validationToken (echoed as text/plain), Asana's X-Hook-Secret (echoed, and kept sealed as the signing secret of its events), monday.com's challenge, Azure Event Grid's SubscriptionValidationEvent (answered with its validationResponse), and Trello's HEAD check (the head operation of this url).

A handshake writes no card data, is never billed and spends no rate budget, and is recorded in the delivery log with outcome handshake.

The body, at most 1 MiB, is JSON, application/x-www-form-urlencoded (a repeated name is an array) or multipart/form-data (each text field is body.<field>, a repeated name is an array, and file parts are ignored and counted in the delivery log). With no mapping the object is the document; with one, the template builds it from body and query.

The document is checked as the card's data (card_data_invalid names the path), and one equal to the current document is not written (status unchanged). An accepted post is one billed API request and appends the card data events. Idempotency-Key (8 to 200 characters) deduplicates a sender's retries for twenty-four hours.

At most 120 posts per minute per webhook; each also counts toward the organization's and the card's write rates (PUT /api/card-data/{key}).


Operation: receiveCardKVWebhook.

- Authentication: kvWebhookBearer OR kvWebhookBasic
- Request body: object

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |
| webhook_id | path | required | The id of one of the application's key/value webhooks. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |
| ScreenRig-Signature | header | optional | t=<unix seconds>,v1=<hex HMAC-SHA256 over "<t>.<raw body>">; required when the webhook signs. |

#### Request template

~~~sh
curl --request POST --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/cards/RESOURCE_ID/kv-webhooks/WEBHOOK_ID/write'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Whether the card's document changed, or the answer to a vendor's handshake. | CardKVWebhookWriteResult (application/json); headers: RateLimit-Policy, RateLimit, Cache-Control |
| 400 | invalid_request — the body is too large, empty, not JSON or a form, or the mapping failed on it. | Problem (application/problem+json) |
| 402 | payment_required — the project is deleted. A post is never refused for the project's balance. | Problem (application/problem+json) |
| 404 | not_found — the one answer to every refused post and every GET. | Problem (application/problem+json) |
| 409 | idempotency_mismatch, or card_data_invalid — the card refused the document; errors[0].field names the path. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 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) |

### HEAD /api/cards/{id}/kv-webhooks/{webhook_id}/write

**Head Card KVWebhook**

The check Trello makes of a new webhook. It answers 200 with no body for a webhook whose handshake is trello, and the one 404 not_found for every other webhook, address and request, so a link previewer learns nothing. It writes nothing, is never billed and spends no rate budget; the answered check is in the delivery log with outcome handshake.

Every GET answers the 404 and reads nothing.


Operation: headCardKVWebhook.

- Authentication: none
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |
| webhook_id | path | required | The id of one of the application's key/value webhooks. |

#### Request template

~~~sh
curl --request HEAD 'https://api.screenrig.ai/api/cards/RESOURCE_ID/kv-webhooks/WEBHOOK_ID/write'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The webhook answers Trello's handshake. | no body; headers: Cache-Control |
| 404 | not_found — the one answer to every other request. | 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 /api/card-data

**List Card Data**

Lists the project's card data documents, at most 200 in ascending key order. next_cursor is the last returned key; pass it back as after for the next page, null when there are no more. Card data documents hold the dynamic properties of card placements (menu sections, ticker items, KPI tiles); the placement's data key names one.


Operation: listCardData.

- Authentication: projectBearer
- 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 |  |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' 'https://api.screenrig.ai/api/card-data'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Card data documents | CardDataList (application/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 /api/card-data/{key}

**Get Card Data**

Returns the document and its revision. ETag is the revision, for If-Match on the next write.


Operation: getCardData.

- 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. |
| key | path | required | Card data key. May not start with feed:. |

#### Request template

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

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Card data document | CardDataEntry (application/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) |

### PUT /api/card-data/{key}

**Put Card Data**

Creates or replaces a card data document. When card placements in the project's playlists bind the key, the document must hold only each bound card's dynamic properties and validate against them; otherwise the write is refused with card_data_invalid naming the card and the property path, and nothing changes. A key no placement binds accepts any JSON object.

Every string that begins with media: is a media reference, media:<id>, for a card to draw (a photo); on any key it must name a ready, approved image in this project, at most 256 distinct media per document, or the write is refused with card_media_invalid naming the property path and media_id.

Screens whose content reads the key may then fetch those media from GET /media/{media_id}. Screens showing a placement that reads the key receive screen.card_data_changed and refetch it in place; the project log records kv.updated with a card_data_entry resource, which never reaches a Player.

Limits, quota, metering, revisions and idempotency are the application K/V store's.

Agent and webhook writes are also rate limited, per organization by plan (Standard and Plus 60 a minute, Education and Premium 600, Enterprise 3,000) and to 60 a minute per key on every plan; a write over a limit is refused with rate_limited and Retry-After, and is not metered.


Operation: putCardData.

- Authentication: projectBearer
- Request body: CardDataWrite

#### 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. |
| key | path | required | Card data key. May not start with feed:. |
| If-Match | header | optional | Optional positive resource revision. Omit to create or overwrite a key; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| document | required | object | The card data document: a JSON object of at most 1 MiB. When a card placement binds the key it holds only that card's dynamic properties. A string that begins with media: is a media reference, media:<id>, naming a ready, approved image in this project. |
| expires_at | optional | string | Absolute expiry; omitted data never expires. format: "date-time" |
| ttl_seconds | optional | integer | TTL from the first committed write. Mutually exclusive with expires_at. A retry preserves the original expiry. Omitting both clears expiry. Size it to the writer's schedule: when a card's document expires with no write to renew it while a screen shows the card, the card goes blank and the card_data_stale notification condition tells the project, so a job that stopped is noticed. minimum: 1 maximum: 31536000 |

#### Request template

~~~sh
curl --request PUT --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/card-data/KEY'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Card data document | CardDataEntry (application/json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 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) |

### DELETE /api/card-data/{key}

**Delete Card Data**

Deletes a card data document. A card bound to the key is hidden on glass until a document is written again.


Operation: deleteCardData.

- Authentication: projectBearer
- 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. |
| key | path | required | Card data key. May not start with feed:. |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request DELETE --header 'Authorization: Bearer [SCREENRIG_TOKEN]' 'https://api.screenrig.ai/api/card-data/KEY'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 204 | Deleted | no body |
| 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 /api/feeds/{family}

**Get Feed**

Reads a shared third-party feed document: the same document a built-in card placement with the same params reads (docs/feeds.md).

Query parameters normalize into the card's feed key, named by the ScreenRig-Feed-Key header: weather, airquality and alerts take lat and lon (rounded to a 0.25 degree cell); earthquakes lat, lon, radius_km (10 to 20000, default 500), min_magnitude (0 to 9, default 2.5) and hours (1 to 168, default 24); cyclones basins (comma separated at, ep, cp, wp; default all) and optionally lat, lon and radius_km (the storms near that place); tides station (a NOAA tide prediction station id such as 9414290) or lat and lon (the nearest station within 100 km, else invalid_request); launches no parameters (the next launches worldwide); rates base (an ECB reference currency, default EUR); crypto symbols (CoinGecko ids or tickers, comma separated, at most 20) and currency (default usd); headlines url (an http or https RSS or Atom URL); cap (screenrig.cap/v1) the CAP alerts matching a screen: url (repeatable, up to 5 CAP alert or ATOM/RSS index URLs), public (also read the public feeds covering lat and lon; default when no url), lat and lon, geocode (repeatable NAME:VALUE area codes), min_severity, max_severity, min_urgency, min_certainty, event, exclude_event, sender, status (default Actual), scope (default Public), language (repeatable, in preference order) and area_unknown (hide or show); drugalerts (screenrig.drugalerts/v1) the recent recall reports of a country: country (us, ca or gb, required) and category (drug, the default, or food); respiratory (screenrig.respiratory/v1) the weekly flu, COVID-19 and RSV activity of a place: country (us or gb, required) and region (a US state, territory or DC by name or postal code, or England or a UKHSA region; the nation when empty); almanac (screenrig.almanac/v1) the observances, On This Day events, sun and moon of a date for the today card: country (an ISO 3166-1 alpha-2 code, for its public holidays), language (an On This Day edition: ar, bs, cs, de, en, es, fr, it, pt, ru, sv, tr, uk or zh; default en), optionally lat and lon (rounded to a 0.1 degree cell, for the sun) and date (a fixed day; default the days around today); outdoorsafety (screenrig.outdoorsafety/v1) the heat for the outdoor-safety card, an estimate of the wet bulb globe temperature, from lat and lon (the weather's 0.25 degree cell); it reports no lightning; departures (screenrig.departures/v1) the next departures at one to four stops of one open-data provider: provider (tfl, entur or digitransit) and stops (comma separated ids, at most 4: TfL StopPoint ids, Entur NSR stop place or quay ids, Digitransit gtfsIds), merged into one board in leaving order and polled every minute; bikeshare (screenrig.bikeshare/v1) the GBFS bike share stations nearest lat and lon (rounded to a 0.001 degree cell, about 100 m), each with its distance from that cell, from system (a listed system id) or, without it, the nearest listed system that covers the place (invalid_request at system where none does).

The request never calls the provider. 200 is the document (schema, fetchedAt, attribution and the family's fields, as the cards contract defines them); its ETag is the sha256 of the bytes and If-None-Match answers 304; Cache-Control max-age runs to the next scheduled fetch. 202 means no document yet: the read registered demand, and the feed worker fetches the key within about a minute (Retry-After).

An API read keeps a key fetched for 24 hours. An organization registers at most 50 new keys a day across its projects (rate_limited past it). A family with no source on this deployment answers not_found.


Operation: getFeed.

- 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. |
| family | path | required | The feed family. |
| lat | query | optional |  |
| lon | query | optional |  |
| radius_km | query | optional |  |
| min_magnitude | query | optional | earthquakes: the least magnitude. |
| hours | query | optional | earthquakes: the window in hours. |
| basins | query | optional | cyclones: comma separated at, ep, cp, wp. |
| station | query | optional | tides: a NOAA tide prediction station id, such as 9414290. Without it, lat and lon pick the nearest station within 100 km. |
| base | query | optional |  |
| symbols | query | optional |  |
| currency | query | optional |  |
| url | query | optional | headlines: the feed URL. cap: repeat up to 5 times. |
| public | query | optional |  |
| geocode | query | optional | cap: NAME:VALUE, repeatable (UGC:WAC033). |
| min_severity | query | optional |  |
| max_severity | query | optional |  |
| min_urgency | query | optional |  |
| min_certainty | query | optional |  |
| event | query | optional | cap: repeatable; * matches any run of characters. |
| exclude_event | query | optional |  |
| sender | query | optional |  |
| status | query | optional |  |
| scope | query | optional |  |
| language | query | optional |  |
| area_unknown | query | optional |  |
| country | query | optional | drugalerts: us, ca or gb; respiratory: us or gb (both require it). almanac: the ISO 3166-1 alpha-2 country whose public holidays the document lists. |
| category | query | optional | drugalerts: drug (default) or food. |
| region | query | optional | respiratory: a US state, territory or DC by name or postal code, or England or a UKHSA region; the nation when empty. |
| date | query | optional | almanac: one fixed day instead of the days around today. |
| provider | query | optional | departures: the transit provider of the stops. |
| stops | query | optional | departures: one to four stop ids, comma separated (TfL 940GZZLUOXC, Entur NSR:StopPlace:337 or NSR:Quay:568, Digitransit HSL:1040129). |
| system | query | optional | bikeshare: a listed GBFS system id; without it the nearest listed system that covers lat and lon. |
| If-None-Match | header | optional |  |

#### Request template

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

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Feed document | FeedDocument (application/json); headers: ETag, ScreenRig-Feed-Key |
| 202 | No document yet; the feed worker fetches the key on its next pass. | FeedPending (application/json); headers: Retry-After, ScreenRig-Feed-Key |
| 304 | The document matches If-None-Match. | no body |
| 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).
