# Service clients API reference

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

Service clients. This group has 9 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.

## Service clients

### GET /api/service-clients

**List Service Clients**

The project's service clients (spec: a customer's server, script or CI job owned by the project), newest first, live and revoked, with their public keys, secret ids and usage; never a secret or its digest. Needs the project capability.


Operation: listServiceClients.

- 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. |

#### Request template

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

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The service client. | ServiceClientList (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/service-clients

**Create Service Client**

Creates a service client owned by the project with a name, capabilities (omitted means all six; each must be one the manager holds, else 403), access (read, the default, or manage), and a public JWK key (OKP Ed25519, EC P-256 or RSA of at least 2048 bits) and/or secret true. The secret is returned once, here only. At most 50 live clients per organization.

Agents need identity scope, live identity Manage, their own Manage grant with the project capability on the project, and a live organization admin role; a person uses the dashboard session with project_id and a fresh proof (kind service_client_change, subject the client id, or the project id when creating) in the body. A service token is refused 403.

A member limited to screens does not connect agents, issue credentials or manage the project's people: outside_screen_scope (ask the project owner).


Operation: createServiceClient.

- Authentication: agentIdentityBearer OR dashboardSession
- Request body: ServiceClientChange

#### 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 body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| project_id | optional | string | Dashboard session only. |
| proof | optional | DashboardProof |  |
| name | optional | string | minLength: 1 maxLength: 80 |
| capabilities | optional | array |  |
| access | optional | string | Manage needs a person who can approve reviews (403 approver_required). Allowed: "read", "propose", "manage" |
| key | optional | object | A public JWK: OKP Ed25519, EC P-256 or RSA of at least 2048 bits. |
| secret | optional | boolean | Generate a secret (creation only). |
| upload_scope | optional | UploadScope | What an uploader may use (docs/uploaders.md): the playlists it changes pages of and the media folders it uploads into. Folders are exactly the listed ones, never their subfolders. An id that names no live object of the project when written is invalid_request at its position; a later deletion leaves a dead id, which grants nothing. The scope is read on each request, so a change applies to the next one. |

#### Request template

~~~sh
curl --request POST --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/service-clients'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 201 | The service client. | ServiceClientResult (application/json) |
| 402 | feature_not_in_plan — an upload scope makes an uploader, and the project's plan does not include uploaders; required_plan names the plan that does. Narrowing a scope and removing it are always allowed. | Problem (application/problem+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 /api/service-clients/{id}

**Get Service Client**

One of the project's service clients. Needs the project capability.


Operation: getServiceClient.

- 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. |
| 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/service-clients/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The service client. | ServiceClient (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/service-clients/{id}

**Update Service Client**

Renames a service client or changes its capabilities (within the manager's) or access. Narrowing either ends the client's current tokens within seconds.

Agents need identity scope, live identity Manage, their own Manage grant with the project capability on the project, and a live organization admin role; a person uses the dashboard session with project_id and a fresh proof (kind service_client_change, subject the client id, or the project id when creating) in the body. A service token is refused 403.

A member limited to screens does not connect agents, issue credentials or manage the project's people: outside_screen_scope (ask the project owner).


Operation: updateServiceClient.

- Authentication: agentIdentityBearer OR dashboardSession
- Request body: ServiceClientChange

#### 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 body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| project_id | optional | string | Dashboard session only. |
| proof | optional | DashboardProof |  |
| name | optional | string | minLength: 1 maxLength: 80 |
| capabilities | optional | array |  |
| access | optional | string | Manage needs a person who can approve reviews (403 approver_required). Allowed: "read", "propose", "manage" |
| key | optional | object | A public JWK: OKP Ed25519, EC P-256 or RSA of at least 2048 bits. |
| secret | optional | boolean | Generate a secret (creation only). |
| upload_scope | optional | UploadScope | What an uploader may use (docs/uploaders.md): the playlists it changes pages of and the media folders it uploads into. Folders are exactly the listed ones, never their subfolders. An id that names no live object of the project when written is invalid_request at its position; a later deletion leaves a dead id, which grants nothing. The scope is read on each request, so a change applies to the next one. |

#### Request template

~~~sh
curl --request PATCH --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/service-clients/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The service client. | ServiceClientResult (application/json) |
| 402 | feature_not_in_plan — an upload scope makes an uploader, and the project's plan does not include uploaders; required_plan names the plan that does. Narrowing a scope and removing it are always allowed. | Problem (application/problem+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) |

### POST /api/service-clients/{id}/keys

**Add Service Client Key**

Registers a second public JWK (key in the body) for a zero-downtime rotation; a client holds at most two.

Agents need identity scope, live identity Manage, their own Manage grant with the project capability on the project, and a live organization admin role; a person uses the dashboard session with project_id and a fresh proof (kind service_client_change, subject the client id, or the project id when creating) in the body. A service token is refused 403.

A member limited to screens does not connect agents, issue credentials or manage the project's people: outside_screen_scope (ask the project owner).


Operation: addServiceClientKey.

- Authentication: agentIdentityBearer OR dashboardSession
- Request body: ServiceClientChange

#### 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 body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| project_id | optional | string | Dashboard session only. |
| proof | optional | DashboardProof |  |
| name | optional | string | minLength: 1 maxLength: 80 |
| capabilities | optional | array |  |
| access | optional | string | Manage needs a person who can approve reviews (403 approver_required). Allowed: "read", "propose", "manage" |
| key | optional | object | A public JWK: OKP Ed25519, EC P-256 or RSA of at least 2048 bits. |
| secret | optional | boolean | Generate a secret (creation only). |
| upload_scope | optional | UploadScope | What an uploader may use (docs/uploaders.md): the playlists it changes pages of and the media folders it uploads into. Folders are exactly the listed ones, never their subfolders. An id that names no live object of the project when written is invalid_request at its position; a later deletion leaves a dead id, which grants nothing. The scope is read on each request, so a change applies to the next one. |

#### Request template

~~~sh
curl --request POST --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/service-clients/RESOURCE_ID/keys'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The service client. | ServiceClientResult (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) |

### POST /api/service-clients/{id}/keys/{kid}/remove

**Remove Service Client Key**

Removes a key; the client's current tokens end within seconds. A client keeps at least one key or secret.

Agents need identity scope, live identity Manage, their own Manage grant with the project capability on the project, and a live organization admin role; a person uses the dashboard session with project_id and a fresh proof (kind service_client_change, subject the client id, or the project id when creating) in the body. A service token is refused 403.

A member limited to screens does not connect agents, issue credentials or manage the project's people: outside_screen_scope (ask the project owner).


Operation: removeServiceClientKey.

- Authentication: agentIdentityBearer OR dashboardSession
- Request body: ServiceClientChange

#### 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 |  |
| kid | path | required |  |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| project_id | optional | string | Dashboard session only. |
| proof | optional | DashboardProof |  |
| name | optional | string | minLength: 1 maxLength: 80 |
| capabilities | optional | array |  |
| access | optional | string | Manage needs a person who can approve reviews (403 approver_required). Allowed: "read", "propose", "manage" |
| key | optional | object | A public JWK: OKP Ed25519, EC P-256 or RSA of at least 2048 bits. |
| secret | optional | boolean | Generate a secret (creation only). |
| upload_scope | optional | UploadScope | What an uploader may use (docs/uploaders.md): the playlists it changes pages of and the media folders it uploads into. Folders are exactly the listed ones, never their subfolders. An id that names no live object of the project when written is invalid_request at its position; a later deletion leaves a dead id, which grants nothing. The scope is read on each request, so a change applies to the next one. |

#### Request template

~~~sh
curl --request POST --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/service-clients/RESOURCE_ID/keys/KID/remove'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The service client. | ServiceClientResult (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) |

### POST /api/service-clients/{id}/secrets

**Add Service Client Secret**

Generates a second secret, returned once, for a zero-downtime rotation; a client holds at most two.

Agents need identity scope, live identity Manage, their own Manage grant with the project capability on the project, and a live organization admin role; a person uses the dashboard session with project_id and a fresh proof (kind service_client_change, subject the client id, or the project id when creating) in the body. A service token is refused 403.

A member limited to screens does not connect agents, issue credentials or manage the project's people: outside_screen_scope (ask the project owner).


Operation: addServiceClientSecret.

- Authentication: agentIdentityBearer OR dashboardSession
- Request body: ServiceClientChange

#### 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 body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| project_id | optional | string | Dashboard session only. |
| proof | optional | DashboardProof |  |
| name | optional | string | minLength: 1 maxLength: 80 |
| capabilities | optional | array |  |
| access | optional | string | Manage needs a person who can approve reviews (403 approver_required). Allowed: "read", "propose", "manage" |
| key | optional | object | A public JWK: OKP Ed25519, EC P-256 or RSA of at least 2048 bits. |
| secret | optional | boolean | Generate a secret (creation only). |
| upload_scope | optional | UploadScope | What an uploader may use (docs/uploaders.md): the playlists it changes pages of and the media folders it uploads into. Folders are exactly the listed ones, never their subfolders. An id that names no live object of the project when written is invalid_request at its position; a later deletion leaves a dead id, which grants nothing. The scope is read on each request, so a change applies to the next one. |

#### Request template

~~~sh
curl --request POST --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/service-clients/RESOURCE_ID/secrets'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The service client. | ServiceClientResult (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) |

### POST /api/service-clients/{id}/secrets/{secret_id}/remove

**Remove Service Client Secret**

Removes a secret; the client's current tokens end within seconds.

Agents need identity scope, live identity Manage, their own Manage grant with the project capability on the project, and a live organization admin role; a person uses the dashboard session with project_id and a fresh proof (kind service_client_change, subject the client id, or the project id when creating) in the body. A service token is refused 403.

A member limited to screens does not connect agents, issue credentials or manage the project's people: outside_screen_scope (ask the project owner).


Operation: removeServiceClientSecret.

- Authentication: agentIdentityBearer OR dashboardSession
- Request body: ServiceClientChange

#### 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 |  |
| secret_id | path | required |  |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| project_id | optional | string | Dashboard session only. |
| proof | optional | DashboardProof |  |
| name | optional | string | minLength: 1 maxLength: 80 |
| capabilities | optional | array |  |
| access | optional | string | Manage needs a person who can approve reviews (403 approver_required). Allowed: "read", "propose", "manage" |
| key | optional | object | A public JWK: OKP Ed25519, EC P-256 or RSA of at least 2048 bits. |
| secret | optional | boolean | Generate a secret (creation only). |
| upload_scope | optional | UploadScope | What an uploader may use (docs/uploaders.md): the playlists it changes pages of and the media folders it uploads into. Folders are exactly the listed ones, never their subfolders. An id that names no live object of the project when written is invalid_request at its position; a later deletion leaves a dead id, which grants nothing. The scope is read on each request, so a change applies to the next one. |

#### Request template

~~~sh
curl --request POST --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/service-clients/RESOURCE_ID/secrets/SECRET_ID/remove'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The service client. | ServiceClientResult (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) |

### POST /api/service-clients/{id}/revoke

**Revoke Service Client**

Revokes a service client permanently: its keys and secret digests are deleted, the record stays for audit, and its tokens end within seconds (propagation applied or pending).

Agents need identity scope, live identity Manage, their own Manage grant with the project capability on the project, and a live organization admin role; a person uses the dashboard session with project_id and a fresh proof (kind service_client_change, subject the client id, or the project id when creating) in the body. A service token is refused 403.

A member limited to screens does not connect agents, issue credentials or manage the project's people: outside_screen_scope (ask the project owner).


Operation: revokeServiceClient.

- Authentication: agentIdentityBearer OR dashboardSession
- Request body: ServiceClientChange

#### 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 body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| project_id | optional | string | Dashboard session only. |
| proof | optional | DashboardProof |  |
| name | optional | string | minLength: 1 maxLength: 80 |
| capabilities | optional | array |  |
| access | optional | string | Manage needs a person who can approve reviews (403 approver_required). Allowed: "read", "propose", "manage" |
| key | optional | object | A public JWK: OKP Ed25519, EC P-256 or RSA of at least 2048 bits. |
| secret | optional | boolean | Generate a secret (creation only). |
| upload_scope | optional | UploadScope | What an uploader may use (docs/uploaders.md): the playlists it changes pages of and the media folders it uploads into. Folders are exactly the listed ones, never their subfolders. An id that names no live object of the project when written is invalid_request at its position; a later deletion leaves a dead id, which grants nothing. The scope is read on each request, so a change applies to the next one. |

#### Request template

~~~sh
curl --request POST --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/service-clients/RESOURCE_ID/revoke'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The service client. | ServiceClientResult (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) |


## Sitemap

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