> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useshipd.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Control-plane API

> Versioning, authentication, errors, pagination, retries, and compatibility for the shipd management API.

## Overview

The dashboard, `shipd` CLI, and automation clients share the same versioned API at `https://api.useshipd.com/api/v1`. RPC routes accept JSON with `POST` and return JSON.

> **[OpenAPI 3.1 document](https://api.useshipd.com/api/v1/openapi.json)**
>
> Every route, request body, and response schema: `https://api.useshipd.com/api/v1/openapi.json`. Generate a client from it or import it into your API tool.

Successful RPC responses use one envelope:

```json theme={null}
{ "data": { "id": "project_01", "name": "Web app" } }
```

## Versioning and compatibility

The major version is part of every public path. Within `/api/v1`, shipd may add optional request fields, response fields, routes, error codes, and enum values. It does not remove fields, change their meaning, or make an optional field required without a new major version.

Use the OpenAPI document as the machine-readable contract to generate clients and validate requests and responses.

When a route or authentication mode is deprecated, its reference entry names the replacement and earliest removal date. A replacement remains available for at least one published migration window.

## Authentication and route restrictions

Send credentials with `Authorization: Bearer …` or `x-api-key`. Do not put long-lived credentials in URLs.

| Credential | Intended use | Restrictions |
| - | - | - |
| Dashboard session | Interactive administration | Active organization, role, team, project, and environment access apply. |
| Management API key (`shipd_mgmt_…`) | Personal CLI and compatible automation | Bound to one organization, project, and environment. It cannot administer organization-wide keys, billing, members, teams, SSO, service accounts, audit, or snapshot-wide reads. |
| Service account token (`shipd_sa_…`) | CI and automation | Limited by its explicit scopes and optional project/environment binding. Approval review, SDK-key creation, service-account lifecycle, team rosters, device authorization, SSO, and SCIM settings require a user session. |
| SDK key (`shipd_sdk_…`) | Data-plane snapshot and event traffic | Valid only on `https://edge.useshipd.com/sdk/v1`; it is not a control-plane credential. |

Resource lookups are organization-scoped. Where a resource identifier would reveal access to another project or organization, the API returns the same `404 not_found` response used for a missing resource.

## Errors

Every non-success response has a stable, machine-readable envelope. Internal exceptions and stack traces are logged server-side and are not returned.

```json theme={null}
{
  "error": {
    "code": "validation_error",
    "message": "Request validation failed",
    "requestId": "req_01",
    "details": {}
  }
}
```

Branch on `error.code`, not on `message`. Common codes are `unauthenticated` (401), `forbidden` (403), `not_found` (404), `conflict` (409), `validation_error` (422), and `rate_limited` (429). Report `requestId` to support for any `500 internal`. See [Errors](/reference/errors) for every status, code, and CLI exit code.

## Pagination

Growing collections use cursor pagination. Send `limit` and, after the first page, the prior response's `Next-Cursor` header as `cursor`. Do not parse or construct string cursors. Audit and snapshot operational collections use monotonically ordered numeric cursors; treat those as opaque too.

```http theme={null}
POST /api/v1/rpc/evaluatedEntity/list
Content-Type: application/json

{ "projectId": "project_01", "limit": 100, "cursor": "W1wi…" }
```

If `Next-Cursor` is absent, the page is complete. Naturally bounded configuration collections may return arrays without a cursor.

## Safe retries

Retriable control-plane mutation routes accept `Idempotency-Key` with up to 256 characters. Generate a unique value per logical operation and reuse it only when retrying that operation. shipd stores the request hash and response for 24 hours, scoped to the authenticated principal and route. The OpenAPI operation declares the header when it is supported. Usage ingest instead requires an idempotency key on every event; evaluated-entity ingest is an upsert by environment and external id.

* A completed duplicate replays the original status and body with `Idempotency-Replayed: true`.
* Reusing a key with a different body returns `409 conflict`.
* A concurrent duplicate still being processed returns `409 conflict`; retry it with backoff and the same key.
* A server failure is not cached, so the same key can retry safely.

Read `Retry-After` before retrying `429`, `503`, or asynchronous `202` responses. Rate-limited responses also include `RateLimit-Limit`, `RateLimit-Remaining`, and the Unix timestamp `RateLimit-Reset`. Use exponential backoff with jitter when `Retry-After` is absent.

## Asynchronous operations

`snapshot/rebuild` queues rebuild work and returns `202`, `Location`, and `Retry-After`. SCIM connection decommissioning returns `202` with the current connection state while directory cleanup continues; poll the route named by `Location` after `Retry-After`. A completed SCIM decommission returns `200`.

## Conditional requests

Control-plane RPC results are private and use `Cache-Control: no-store`. Snapshot payloads on the data plane return an `ETag`; send it in `If-None-Match` to receive `304 Not Modified`. See [Edge API](/reference/edge-api).

## Bulk operations

| Operation | Maximum | Failure behavior |
| - | -: | - |
| Flag `diff` / `push` | 500 flags | Validation rejects the entire request. Apply is transactional: a conflict or invalid flag applies none of the batch. The response reports planned/applied changes and approval request IDs. |
| Evaluated-entity ingest | 100 entities | Validation rejects the entire request before writes. Duplicate identities coalesce by environment and external id. |
| Usage ingest | 500 events | Each event carries an idempotency key; accepted and duplicate counts are returned. Invalid input rejects the request. |
| Project team assignment | 100 teams | Assignment is atomic; cross-organization team IDs reject the request. |
| SDK events | 100 events and 256 KiB | Validation rejects malformed or oversized batches. Accepted identity events are queued before `202`. |

## Client consistency

The dashboard and CLI call these same `/api/v1/rpc` contracts through the shared API client. Automation should follow the same resource binding, approval, idempotency, error, and retry rules; no separate automation semantics exist.

Related: [Errors](/reference/errors) · [CLI](/reference/cli) · [Service account tokens](/reference/service-account-tokens) · [SDK keys, management API keys, and service account tokens](/concepts/keys) · [Audit log](/reference/audit-log).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.