Skip to content

CodeEscrows

Developers

Automate deposits from CI, read agreement, verification and release status, receive signed webhooks and, for partners, onboard client agreements in bulk. Authenticate with an organisation API key (Settings → API keys) sent as a bearer token.

Quick start

List agreements (curl)
curl -s https://staging.codeescrows.com/api/v1/agreements \
  -H "Authorization: Bearer $CODEESCROWS_API_KEY"

Create a client

The SDK uses the platform fetch and has no dependencies.

import { CodeEscrowsClient } from "@codeescrows/sdk";

const client = new CodeEscrowsClient({
  baseUrl: "https://staging.codeescrows.com",
  apiKey: process.env.CODEESCROWS_API_KEY!, // ce_live_…
});

List agreements (all pages)

Iterates every agreement your organisation is a party to.

Scopes: agreements:read

for await (const agreement of client.agreements.listAll({ status: "active" })) {
  console.log(agreement.public_id, agreement.status, agreement.health_score);
}

Follow a deposit to sealed

Polls a deposit until it is sealed and prints its receipt verification link.

Scopes: deposits:read

const deposit = await client.deposits.waitUntilSettled(depositId);
if (deposit.state === "sealed" && deposit.receipt) {
  console.log("Verify:", new URL(deposit.receipt.verify_path, client.baseUrl).toString());
}

Upload a deposit

Starts an upload, PUTs each part to its presigned URL with its SHA-256, then completes. Use the codeescrows CLI for large archives.

Scopes: deposits:write

import { createHash } from "node:crypto";
import { readFile } from "node:fs/promises";

const archive = await readFile("product.tar.gz");
const checksum = createHash("sha256").update(archive).digest("base64");
const session = await client.deposits.create({
  agreement_id: agreementId,
  filename: "product.tar.gz",
  content_type: "application/gzip",
  size: archive.byteLength,
  parts: [{ part_number: 1, checksum_sha256: checksum }],
});
const [part] = session.upload.parts;
const put = await fetch(part.url, { method: "PUT", headers: part.headers, body: archive });
await client.deposits.complete(session.deposit.id, [
  { part_number: 1, etag: put.headers.get("etag")! },
]);

Watch release requests

Lists open release requests and their objection deadlines.

Scopes: releases:read

const { data } = await client.releases.list({ state: "objection_window" });
for (const r of data) console.log(r.agreement_id, r.state, r.objection_deadline_at);

Handle errors

Every failure is a CodeEscrowsError with a stable code and the request id.

import { CodeEscrowsError } from "@codeescrows/sdk";

try {
  await client.agreements.get(agreementId);
} catch (error) {
  if (error instanceof CodeEscrowsError) {
    // e.g. not_found, api_key_scope, rate_limited (see retryAfterSeconds)
    console.error(error.status, error.code, error.requestId);
  }
  throw error;
}

Register a webhook endpoint

The signing secret is returned once: store it with your receiver.

Scopes: webhooks:manage

const endpoint = await client.webhooks.endpoints.create({
  url: "https://hooks.example.com/codeescrows",
  events: ["deposit.sealed", "release.submitted"],
});
saveSecret(endpoint.secret); // whsec… shown once
await client.webhooks.endpoints.test(endpoint.id); // queues a webhook.ping

Verify a webhook delivery

Check X-CodeEscrows-Signature over the raw body before trusting the event (Web Crypto; any runtime).

import { verifyWebhookSignature, WEBHOOK_SIGNATURE_HEADER } from "@codeescrows/sdk";

export async function POST(request: Request) {
  const body = await request.text(); // raw body, before JSON.parse
  const ok = await verifyWebhookSignature({
    secret: process.env.CODEESCROWS_WEBHOOK_SECRET!,
    header: request.headers.get(WEBHOOK_SIGNATURE_HEADER),
    body,
  });
  if (!ok) return new Response("invalid signature", { status: 400 });
  const event = JSON.parse(body);
  // handle event.type idempotently (event.id may be delivered more than once)
  return new Response(null, { status: 204 });
}

Replay failed webhook deliveries

Finds dead deliveries in the 30-day log and queues each again (same event id).

Scopes: webhooks:manage

for await (const delivery of client.webhooks.deliveries.listAll({ status: "dead" })) {
  await client.webhooks.deliveries.replay(delivery.id);
}

Partner: import client agreements

Dry run, then commit drafts, then (separately) invite the counterparties. Needs a partner:write key.

Scopes: partner:write

import { readFile } from "node:fs/promises";

const batch = await client.partners.imports.validate({
  target_org_id: clientOrgId,
  csv: await readFile("escode-export.csv", "utf8"),
  preset: "escode",
});
console.log(batch.valid_count, "valid,", batch.error_count, "invalid");
const committed = await client.partners.imports.commit(batch.id); // drafts only, nothing sent
await client.partners.imports.sendInvitations(committed.id); // explicit step

curl: download the OpenAPI document

Public, no key needed; generate clients from it or import it into your API tool.

curl -s "https://staging.codeescrows.com/api/v1/openapi.json" -o codeescrows-openapi.json

curl: list agreements

Any HTTP client works: send the key as a bearer token.

Scopes: agreements:read

curl -s "https://staging.codeescrows.com/api/v1/agreements?limit=20" \
  -H "Authorization: Bearer $CODEESCROWS_API_KEY"

MCP: add to Claude Code

Read-only MCP server for AI assistants. Create a key with mcp:read plus the read scopes the tools need.

Scopes: mcp:read, agreements:read, deposits:read, verifications:read, releases:read

claude mcp add --transport http codeescrows https://staging.codeescrows.com/api/mcp \
  --header "Authorization: Bearer $CODEESCROWS_API_KEY"

MCP: Claude Desktop configuration

claude_desktop_config.json entry using the mcp-remote bridge to pass the API key header.

Scopes: mcp:read, agreements:read, deposits:read, verifications:read, releases:read

{
  "mcpServers": {
    "codeescrows": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://staging.codeescrows.com/api/mcp", "--header", "Authorization:${CODEESCROWS_AUTH}"],
      "env": { "CODEESCROWS_AUTH": "Bearer ce_live_…" }
    }
  }
}

Verifying webhooks

Every delivery carries X-CodeEscrows-Signature with a timestamp and an HMAC-SHA256 of the raw body. Verify it in constant time and refuse timestamps older than five minutes before parsing the payload.

Node.js / TypeScript
import { createHmac, timingSafeEqual } from "node:crypto";

// header: X-CodeEscrows-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256(secret, `${t}.${rawBody}`)>
// During a secret rotation the header carries one v1= per valid secret.
export function verifyCodeEscrowsSignature(
  rawBody: string,
  header: string | null,
  secret: string,
  toleranceSeconds = 300,
): boolean {
  if (!header) return false;
  const parts = header.split(",").map((p) => p.trim().split("="));
  const t = Number(parts.find(([k]) => k === "t")?.[1]);
  const candidates = parts.filter(([k]) => k === "v1").map(([, v]) => v ?? "");
  if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
  const expected = Buffer.from(
    createHmac("sha256", secret).update(`${t}.${rawBody}`, "utf8").digest("hex"),
  );
  return candidates.some((sig) => {
    const given = Buffer.from(sig);
    return given.length === expected.length && timingSafeEqual(given, expected);
  });
}
Event types (25)
  • agreement.activated — agreement.activated
  • agreement.amended — agreement.amended
  • agreement.released — agreement.released
  • agreement.resumed — agreement.resumed
  • agreement.sent_for_signature — agreement.sent_for_signature
  • agreement.suspended — agreement.suspended
  • agreement.terminated — agreement.terminated
  • deposit.failed — deposit.failed
  • deposit.sealed — deposit.sealed
  • party.accepted — party.accepted
  • party.removed — party.removed
  • party.signed — party.signed
  • party.withdrawn — party.withdrawn
  • release.approved — release.approved
  • release.delivered — release.delivered
  • release.denied — release.denied
  • release.disputed — release.disputed
  • release.expired — release.expired
  • release.objected — release.objected
  • release.submitted — release.submitted
  • release.withdrawn — release.withdrawn
  • verification.completed — verification.completed
  • verification.failed — verification.failed
  • verification.order_delivered — verification.order_delivered
  • webhook.ping — webhook.ping

Partner API

Partner organisations issue keys with the partner:read or partner:write scope to list client organisations, create clients (their owner consents by email), import draft agreements from CSV (Escode and EscrowTech presets) and read the informational revenue-share report. Imports never send anything: drafts are created, invitations are a separate call.

Import (curl)
# Dry run (nothing is created or sent), then commit the drafts
curl -s -X POST https://staging.codeescrows.com/api/v1/partners/imports \
  -H "Authorization: Bearer $CODEESCROWS_PARTNER_KEY" -H "content-type: application/json" \
  -d "$(jq -n --arg csv "$(cat agreements.csv)" --arg org "$CLIENT_ORG_ID" \
        '{target_org_id: $org, preset: "escode", csv: $csv}')"
curl -s -X POST https://staging.codeescrows.com/api/v1/partners/imports/$IMPORT_ID/commit \
  -H "Authorization: Bearer $CODEESCROWS_PARTNER_KEY"

API reference CodeEscrows API 1.0 · 39 operations

Machine-readable OpenAPI 3.1 document (no key needed): /api/v1/openapi.json. Generate clients from it or import it into your API tool.

Agreements

GET/api/v1/agreementsList agreements

Agreements the key's organisation owns or is a party to, newest first. A key restricted to agreements sees only those (pages may then hold fewer items than `limit`). **Scope:** `agreements:read` — Read agreements. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified. **Pagination:** cursor — pass `next_cursor` as `cursor` until it is null.

API-key scopes: agreements:read

Parameters

  • status (query)
  • type (query)
  • role (query)
  • limit (query) — Page size (1–100, default 50)
  • cursor (query) — `next_cursor` of the previous page

Responses

  • 200 A page of agreements
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
GET/api/v1/agreements/{agreement_id}Get an agreement

Role-appropriate view: private notes and other parties' contact details are hidden. **Scope:** `agreements:read` — Read agreements. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified.

API-key scopes: agreements:read

Parameters

  • agreement_id (path, required) — Agreement id

Responses

  • 200 The agreement
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
GET/api/v1/agreements/{agreement_id}/partiesList an agreement's parties

**Scope:** `agreements:read` — Read agreements. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified.

API-key scopes: agreements:read

Parameters

  • agreement_id (path, required) — Agreement id

Responses

  • 200 The parties (one page)
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support

Deposits

GET/api/v1/agreements/{agreement_id}/deposits/{deposit_id}/certificateGet a Deposit Certificate

Receipt (statement, DSSE envelope, public key) and RFC 3161 timestamps as JSON; `?format=pdf` answers a time-limited link to the PDF (`SignedLink`), `&redirect=1` a 302. **Scope:** `deposits:read` — Read deposits, receipts and manifests. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified.

API-key scopes: deposits:read

Parameters

  • agreement_id (path, required) — Agreement id
  • deposit_id (path, required) — Deposit id
  • format (query)
  • redirect (query)

Responses

  • 200 Certificate facts
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 409 Conflicting state (specific code, e.g. `certificate_not_ready`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
GET/api/v1/depositsList an agreement's deposits

**Scope:** `deposits:read` — Read deposits, receipts and manifests. **Auth:** API key only (`Authorization: Bearer ce_…`). **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified. **Pagination:** cursor — pass `next_cursor` as `cursor` until it is null.

API-key scopes: deposits:read

Parameters

  • limit (query)
  • cursor (query)
  • state (query)
  • agreement_id (query, required)

Responses

  • 200 A page of deposits
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
POST/api/v1/depositsStart a deposit upload

Creates a `pending_upload` deposit and its multipart plan; PUT each part to its presigned URL, then call `complete`. The key's organisation must be the agreement's depositor. **Scope:** `deposits:write` — Upload deposits (and follow their status). **Auth:** API key only (`Authorization: Bearer ce_…`). **Rate limit:** 60 upload sessions/hour per key, within the 600/min per-key budget.

API-key scopes: deposits:write

Request body: object

Responses

  • 201 Upload session
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 402 The organisation's plan does not include this feature (`payment_required`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 409 Conflicting state (specific code, e.g. `certificate_not_ready`)
  • 413 Request body too large (`payload_too_large`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
GET/api/v1/deposits/{deposit_id}Get a deposit

State, sizes, Merkle root, receipt (id + verify path) and the Level 1 summary. **Scope:** `deposits:read` — Read deposits, receipts and manifests. **Auth:** API key only (`Authorization: Bearer ce_…`). **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified.

API-key scopes: deposits:read

Parameters

  • deposit_id (path, required) — Deposit id

Responses

  • 200 The deposit
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
POST/api/v1/deposits/{deposit_id}/abortAbort a deposit upload

**Scope:** `deposits:write` — Upload deposits (and follow their status). **Auth:** API key only (`Authorization: Bearer ce_…`). **Rate limit:** 120 writes/min per key, within the 600/min per-key budget.

API-key scopes: deposits:write

Parameters

  • deposit_id (path, required) — Deposit id

Request body: object

Responses

  • 200 The deposit, now failed
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 409 Conflicting state (specific code, e.g. `certificate_not_ready`)
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
POST/api/v1/deposits/{deposit_id}/completeComplete a deposit upload

Completes the multipart upload; the pipeline (scan → seal → attest → notify) starts. **Scope:** `deposits:write` — Upload deposits (and follow their status). **Auth:** API key only (`Authorization: Bearer ce_…`). **Rate limit:** 120 writes/min per key, within the 600/min per-key budget.

API-key scopes: deposits:write

Parameters

  • deposit_id (path, required) — Deposit id

Request body: object

Responses

  • 202 Accepted; follow `status_url`
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 409 Conflicting state (specific code, e.g. `certificate_not_ready`)
  • 413 Request body too large (`payload_too_large`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
POST/api/v1/deposits/{deposit_id}/partsPresign more upload parts

**Scope:** `deposits:write` — Upload deposits (and follow their status). **Auth:** API key only (`Authorization: Bearer ce_…`). **Rate limit:** 2,000 part presigns/min per key, within the 600/min per-key budget.

API-key scopes: deposits:write

Parameters

  • deposit_id (path, required) — Deposit id

Request body: object

Responses

  • 200 Presigned part URLs
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 409 Conflicting state (specific code, e.g. `certificate_not_ready`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support

Meta

GET/api/v1/openapi.jsonGet this OpenAPI document

The OpenAPI 3.1 description of API v1 (this document). Public: no key needed. Cacheable (`ETag`, `If-None-Match` → 304). **Auth:** none (public). **Rate limit:** 120 requests/min per IP (no key needed).

Responses

  • 200 The OpenAPI document
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
  • 503 Feature not configured on this deployment

Partners

GET/api/v1/partners/clientsList client organisations

Client organisations of the key's (partner) organisation, pending and active links, with agreement count, deposit freshness and fee standing once the client consented. Every read of client data is audited in the client's chain. **Scope:** `partner:read` or `partner:write` — Partner API: list client organisations and the revenue-share report. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified.

API-key scopes: partner:readpartner:write

Responses

  • 200 Client organisations
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
POST/api/v1/partners/clientsCreate a client organisation

Creates the client organisation with no members, invites `owner_email` as its owner and records a **pending** link: nothing of the client is visible until its owner consents. The key's creator must be an owner or admin; plan feature `partner`. `accept_url` is shown once. **Scope:** `partner:write` — Partner API: create client organisations and import draft agreements (never sends anything). **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 120 writes/min per key, within the 600/min per-key budget.

API-key scopes: partner:write

Request body: object

Responses

  • 201 The client, the owner invitation and its accept URL
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 402 The organisation's plan does not include this feature (`payment_required`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 409 Conflicting state (specific code, e.g. `certificate_not_ready`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
POST/api/v1/partners/clients/{org_id}/agreementsCreate a draft agreement for a client

One **draft** agreement owned by a linked client (active link with `manage_agreements`); same body as creating an agreement in the app. The answer is the agreement service's camelCase view (as built); read it with getAgreement for the `Agreement` shape. Nothing is sent; invite parties afterwards. 404 without such a link, 403 `partner_scope` for a view-only link. **Scope:** `partner:write` — Partner API: create client organisations and import draft agreements (never sends anything). **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 120 writes/min per key, within the 600/min per-key budget.

API-key scopes: partner:write

Parameters

  • org_id (path, required) — Client organisation id

Request body: object

Responses

  • 201 The draft agreement
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 402 The organisation's plan does not include this feature (`payment_required`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
GET/api/v1/partners/importsList import batches

The partner organisation's import batches, newest first (without row reports). **Scope:** `partner:read` or `partner:write` — Partner API: list client organisations and the revenue-share report. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified.

API-key scopes: partner:readpartner:write

Responses

  • 200 Import batches
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
POST/api/v1/partners/importsValidate an import (dry run)

Parses, maps (preset + overrides) and validates a CSV of agreements for a client (or the partner itself) and stores the row report as a `validated` batch. **Nothing is created or sent** until commit. Body up to 1.2 MB (CSV ≤ 1 MB). **Scope:** `partner:write` — Partner API: create client organisations and import draft agreements (never sends anything). **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 120 writes/min per key, within the 600/min per-key budget.

API-key scopes: partner:write

Request body: object

Responses

  • 201 The batch with its row report
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 402 The organisation's plan does not include this feature (`payment_required`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 413 Request body too large (`payload_too_large`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
GET/api/v1/partners/imports/{import_id}Get an import batch

The batch with its row report (own organisation's batches only). **Scope:** `partner:read` or `partner:write` — Partner API: list client organisations and the revenue-share report. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified.

API-key scopes: partner:readpartner:write

Parameters

  • import_id (path, required) — Import batch id

Responses

  • 200 The batch
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
POST/api/v1/partners/imports/{import_id}/commitCommit an import

Creates one **draft** agreement per valid row (idempotent by row hash, also across batches); the client link is re-checked. Nothing is sent. 409 `import_not_validated` for a batch that is not `validated`. **Scope:** `partner:write` — Partner API: create client organisations and import draft agreements (never sends anything). **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 120 writes/min per key, within the 600/min per-key budget.

API-key scopes: partner:write

Parameters

  • import_id (path, required) — Import batch id

Responses

  • 200 The batch with created / skipped / failed counts
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 402 The organisation's plan does not include this feature (`payment_required`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 409 Conflicting state (specific code, e.g. `certificate_not_ready`)
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
POST/api/v1/partners/imports/{import_id}/discardDiscard an import

Drops a `validated` (uncommitted) batch. **Scope:** `partner:write` — Partner API: create client organisations and import draft agreements (never sends anything). **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 120 writes/min per key, within the 600/min per-key budget.

API-key scopes: partner:write

Parameters

  • import_id (path, required) — Import batch id

Responses

  • 200 The batch, now `discarded`
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 409 Conflicting state (specific code, e.g. `certificate_not_ready`)
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
POST/api/v1/partners/imports/{import_id}/invitationsInvite the counterparties of an import

Explicit step after commit: invites each committed draft's counterparty by email (the normal party invitation). Rows without an email are skipped. 409 `import_not_committed` before commit. **Scope:** `partner:write` — Partner API: create client organisations and import draft agreements (never sends anything). **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 120 writes/min per key, within the 600/min per-key budget.

API-key scopes: partner:write

Parameters

  • import_id (path, required) — Import batch id

Responses

  • 200 The batch with invited / skipped / failed counts
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 402 The organisation's plan does not include this feature (`payment_required`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 409 Conflicting state (specific code, e.g. `certificate_not_ready`)
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
GET/api/v1/partners/revenueGet the revenue-share report

Revenue share of one **closed** month (default: the last closed month) under the published partner terms. **Informational, not a payout.** `?format=csv` answers a `text/csv` attachment instead. The key's creator must be an owner, admin or billing member. 400 `period_not_closed` for an open month. **Scope:** `partner:read` or `partner:write` — Partner API: list client organisations and the revenue-share report. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified.

API-key scopes: partner:readpartner:write

Parameters

  • period (query) — Closed month `YYYY-MM` (default: the last closed month)
  • format (query) — `csv` answers a `text/csv` attachment instead of JSON

Responses

  • 200 The report (JSON)
  • 400 Malformed JSON (`malformed_json`)
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support

Releases

GET/api/v1/agreements/{agreement_id}/releasesList an agreement's release requests

**Scope:** `releases:read` — Read release requests. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified. **Pagination:** cursor — pass `next_cursor` as `cursor` until it is null.

API-key scopes: releases:read

Parameters

  • agreement_id (path, required) — Agreement id
  • state (query)
  • limit (query)
  • cursor (query)

Responses

  • 200 A page of release requests
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
GET/api/v1/agreements/{agreement_id}/releases/{release_id}Get a release request

**Scope:** `releases:read` — Read release requests. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified.

API-key scopes: releases:read

Parameters

  • agreement_id (path, required) — Agreement id
  • release_id (path, required) — Release request id

Responses

  • 200 The release request
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
GET/api/v1/agreements/{agreement_id}/releases/{release_id}/certificateGet a Release Certificate

Certificate facts once the package is delivered; `?format=pdf` a link to the signed PDF. 409 `certificate_not_ready` before delivery. **Scope:** `releases:read` — Read release requests. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified.

API-key scopes: releases:read

Parameters

  • agreement_id (path, required) — Agreement id
  • release_id (path, required) — Release request id
  • format (query)
  • redirect (query)
  • addendum (query)

Responses

  • 200 Certificate facts
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 409 Conflicting state (specific code, e.g. `certificate_not_ready`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
GET/api/v1/releasesList release requests across agreements

**Scope:** `releases:read` — Read release requests. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified. **Pagination:** cursor — pass `next_cursor` as `cursor` until it is null.

API-key scopes: releases:read

Parameters

  • state (query)
  • limit (query)
  • cursor (query)

Responses

  • 200 A page of release requests
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support

Verifications

GET/api/v1/agreements/{agreement_id}/deposits/{deposit_id}/verificationsList a deposit's Level 2 verifications

**Scope:** `verifications:read` — Read verification status (summaries and counts, never findings). **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified.

API-key scopes: verifications:read

Parameters

  • agreement_id (path, required) — Agreement id
  • deposit_id (path, required) — Deposit id
  • limit (query)

Responses

  • 200 Runs, newest first
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
GET/api/v1/agreements/{agreement_id}/deposits/{deposit_id}/verifications/{verification_id}Get a Level 2 verification

**Scope:** `verifications:read` — Read verification status (summaries and counts, never findings). **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified.

API-key scopes: verifications:read

Parameters

  • agreement_id (path, required) — Agreement id
  • deposit_id (path, required) — Deposit id
  • verification_id (path, required) — Level 2 verification id

Responses

  • 200 The run
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
GET/api/v1/agreements/{agreement_id}/deposits/{deposit_id}/verifications/{verification_id}/reportGet a Level 2 verification report

Hashes, timestamps and the caller's projection (JSON); `?format=pdf` answers a 15-minute link to the signed PDF (`SignedLink`). 409 `report_not_ready` until the run finished. **Scope:** `verifications:read` — Read verification status (summaries and counts, never findings). **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified.

API-key scopes: verifications:read

Parameters

  • agreement_id (path, required) — Agreement id
  • deposit_id (path, required) — Deposit id
  • verification_id (path, required) — Level 2 verification id
  • format (query)
  • redirect (query)

Responses

  • 200 Report facts
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 409 Conflicting state (specific code, e.g. `certificate_not_ready`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support

Webhooks

GET/api/v1/webhook-endpointsList webhook endpoints

**Scope:** `webhooks:read` — Read webhook endpoints and the delivery log. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified.

API-key scopes: webhooks:read

Responses

  • 200 Endpoints (one page; at most 10 per organisation)
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
POST/api/v1/webhook-endpointsCreate a webhook endpoint

HTTPS URLs resolving to public addresses only. The signing secret is returned once (`shown_once: true`). **Scope:** `webhooks:manage` — Manage webhook endpoints (create, rotate, test, delete) and replay deliveries. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 120 writes/min per key, within the 600/min per-key budget.

API-key scopes: webhooks:manage

Request body: WebhookEndpointCreate

Responses

  • 201 The endpoint and its secret
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 402 The organisation's plan does not include this feature (`payment_required`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 409 Conflicting state (specific code, e.g. `certificate_not_ready`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
  • 503 Feature not configured on this deployment
DELETE/api/v1/webhook-endpoints/{endpoint_id}Delete a webhook endpoint

Deletes the endpoint and its delivery log. **Scope:** `webhooks:manage` — Manage webhook endpoints (create, rotate, test, delete) and replay deliveries. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 120 writes/min per key, within the 600/min per-key budget.

API-key scopes: webhooks:manage

Parameters

  • endpoint_id (path, required) — Webhook endpoint id

Responses

  • 200 Deleted
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
GET/api/v1/webhook-endpoints/{endpoint_id}Get a webhook endpoint

**Scope:** `webhooks:read` — Read webhook endpoints and the delivery log. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified.

API-key scopes: webhooks:read

Parameters

  • endpoint_id (path, required) — Webhook endpoint id

Responses

  • 200 The endpoint
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
PATCH/api/v1/webhook-endpoints/{endpoint_id}Change a webhook endpoint

URL, description, event filter, or `active` to disable / re-enable it. **Scope:** `webhooks:manage` — Manage webhook endpoints (create, rotate, test, delete) and replay deliveries. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 120 writes/min per key, within the 600/min per-key budget.

API-key scopes: webhooks:manage

Parameters

  • endpoint_id (path, required) — Webhook endpoint id

Request body: WebhookEndpointUpdate

Responses

  • 200 The endpoint
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
POST/api/v1/webhook-endpoints/{endpoint_id}/rotate-secretRotate a webhook signing secret

Returns the new secret once. The previous secret keeps signing for 24 hours (two `v1=` values in the signature header). **Scope:** `webhooks:manage` — Manage webhook endpoints (create, rotate, test, delete) and replay deliveries. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 120 writes/min per key, within the 600/min per-key budget.

API-key scopes: webhooks:manage

Parameters

  • endpoint_id (path, required) — Webhook endpoint id

Request body: object

Responses

  • 200 The endpoint and its new secret
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
  • 503 Feature not configured on this deployment
POST/api/v1/webhook-endpoints/{endpoint_id}/testSend a test event

Queues a signed `webhook.ping` delivery to the endpoint, whatever its event filter. **Scope:** `webhooks:manage` — Manage webhook endpoints (create, rotate, test, delete) and replay deliveries. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 120 writes/min per key, within the 600/min per-key budget.

API-key scopes: webhooks:manage

Parameters

  • endpoint_id (path, required) — Webhook endpoint id

Request body: object

Responses

  • 202 The queued delivery
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 409 Conflicting state (specific code, e.g. `certificate_not_ready`)
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
GET/api/v1/webhooks/deliveriesList webhook deliveries

The delivery log, newest first; kept 30 days. **Scope:** `webhooks:read` — Read webhook endpoints and the delivery log. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified. **Pagination:** cursor — pass `next_cursor` as `cursor` until it is null.

API-key scopes: webhooks:read

Parameters

  • endpoint_id (query)
  • status (query)
  • event_type (query)
  • limit (query)
  • cursor (query)

Responses

  • 200 A page of deliveries
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
GET/api/v1/webhooks/deliveries/{delivery_id}Get a webhook delivery

Payload, request/response excerpts (≤ 1 KiB) and status history. **Scope:** `webhooks:read` — Read webhook endpoints and the delivery log. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified.

API-key scopes: webhooks:read

Parameters

  • delivery_id (path, required) — Webhook delivery id

Responses

  • 200 The delivery
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
POST/api/v1/webhooks/deliveries/{delivery_id}/replayReplay a webhook delivery

Queues the delivery again with the same event id and payload and a fresh signature. 409 when it is already queued or the endpoint is disabled. **Scope:** `webhooks:manage` — Manage webhook endpoints (create, rotate, test, delete) and replay deliveries. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 120 writes/min per key, within the 600/min per-key budget.

API-key scopes: webhooks:manage

Parameters

  • delivery_id (path, required) — Webhook delivery id

Request body: object

Responses

  • 202 The re-queued delivery
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 404 Not found, or not visible to the key's organisation / agreement restriction (`not_found`)
  • 409 Conflicting state (specific code, e.g. `certificate_not_ready`)
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support
GET/api/v1/webhooks/eventsList webhook event types

**Scope:** `webhooks:read` — Read webhook endpoints and the delivery log. **Auth:** API key (`Authorization: Bearer ce_…`) or a signed-in session. **Rate limit:** 600 requests/min per key (all API-key requests) + 300/min per IP before the key is verified.

API-key scopes: webhooks:read

Responses

  • 200 The event catalogue
  • 401 Missing, invalid, expired or revoked API key (`unauthorized`)
  • 403 The key lacks the scope (`api_key_scope`) or the role grant (`forbidden`)
  • 422 Validation failed (`validation_failed`): unknown fields, wrong types or out-of-range values
  • 429 Rate limited (`rate_limited`); see `Retry-After`
  • 500 Unexpected error (`internal_error`); quote `x-request-id` to support

Signed in? Manage keys, webhooks and the MCP endpoint under Settings → Developers.