POST /v1/admin/org/create

Provision a new org and mint its first API key (master-key auth; payment-worker only).

What this is

Creates a new organizations row and mints the first API key for that org. The plaintext key is returned exactly once in the response body; replays of the same Idempotency-Key return prefix-only with already_provisioned: true. Called by payment-worker on Stripe checkout.session.completed.

Authentication

X-API-Key: $MASTER_ADMIN_KEY_ORG_CREATE — the scope-specific Cloudflare Secret bound to this endpoint. NOT the per-org ck_live_* key. payment-worker holds this secret; admin-worker does not. The 8-character prefix concept does not apply (master keys are opaque).

Idempotency

Idempotency-Key header required (use Stripe event.id when called from a webhook handler). Missing or empty header → 400 with error: "VALIDATION_FAILED". Maximum length 256 chars. Replays return the cached envelope sanitized of plaintext keys; the original caller had ONE chance to capture the api_key field on first response. Composite primary key (endpoint, idempotency_key) prevents cross-endpoint replay (Codex P1-16).

⚠️ Plaintext key returned ONCE

The api_key field in the 201 response body is the only place the plaintext appears. It is NEVER persisted to D1, audit logs, or the idempotency cache. Replays return prefix + already_provisioned: true only — the original caller cannot recover the key after the first response. Lost keys must be re-minted via force-rotate.

Rate limiting

Request

curl -X POST https://api.cohesionauth.com/v1/admin/org/create \
  -H "X-API-Key: $MASTER_ADMIN_KEY_ORG_CREATE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: example-org-create-key" \
  -d '{
    "org_id": "org_acme_2026_example",
    "name": "Acme Corp",
    "domain": "general",
    "tier": "starter",
    "allowed_origins": "https://acme.example.com"
  }'

Response

{
  "org_id": "org_acme_2026_example",
  "api_key": "TEST_FIXTURE_NOT_A_REAL_KEY_ORG_CREATE",
  "prefix": "TESTFIXT",
  "created_at": "2026-05-02T20:00:00.000Z",
  "warning": "This API key is shown ONCE. Store it securely. It cannot be retrieved later.",
  "request_id": "req_01H...",
  "timestamp": "2026-05-02T20:00:00.000Z"
}

Errors

StatusMeaning
400Invalid JSON body
401Master-key missing or wrong scope (audit-logged as AUTH_FAIL_MASTER_KEY with detail=scope=ORG_CREATE)
409org_id already exists
422Validation failure (invalid domain enum, tier enum, missing allowed_origins, name out of range, malformed org_id)
500Server misconfigured (pepper missing) or D1 transaction failure

Next step

See the Postman collection “Admin (master-key)” folder for ready-to-run requests with the {{master_admin_key_org_create}} env variable wired up.