Create a tenant
const url = 'https://example.com/v1/system/tenants';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"name":"example","slug":"example","productionAllowed":true,"test":true,"imagePrefix":"example","firstAdmin":{"email":"example","name":"example"}}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/v1/system/tenants \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "name": "example", "slug": "example", "productionAllowed": true, "test": true, "imagePrefix": "example", "firstAdmin": { "email": "example", "name": "example" } }'Creates a tenant on a runtime serving many tenants, with its setup (its default project, its authorization store). Safe to retry with the same slug: if the setup didn’t finish, the tenant waits, frozen, and the same request (the same name, slug, settings and first admin, under any Idempotency-Key) finishes it and answers as the first would have; another request naming that slug is tenant-slug-taken. A repeat of a create that finished is tenant-slug-taken too, unless it carries the first request’s Idempotency-Key: then it gets the first answer again, or 409 idempotency-key-replay-withheld when that answer held the admin key, which is never sent twice (send an Idempotency-Key with every create, so a lost answer can be told from another tenant’s slug). With firstAdmin, that person becomes its admin and member by email, with every capability but secrets:reveal, and the runtime’s own sign-in finds them; the answer carries no key. Without it, the answer carries the tenant’s first admin key, shown once, and that takes the full system capability (kindgi:system), as does a test tenant. Takes system:tenants:create (sign-up’s) or system:tenants. A tenant created here starts with production blocked; productionAllowed: true takes system:tenants:production, and imagePrefix takes system:tenants:image-prefix. Asking for more than the key’s scopes is refused naming the scope, never dropped. An Idempotency-Key retry answers the same tenant, with the admin key withheld.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Header Parameters
Section titled “Header Parameters”Caller-supplied idempotency key, scoped to the caller: the same key from someone else in the tenant is their own request (from 0.1.5). Retries with the same key return the original response byte-identical (per docs/API-ROUTE-CONVENTIONS.md §3.1). A retry sent while the first request still runs is answered 409 idempotency-key-in-flight with Retry-After, on a runtime whose store holds keys (from 0.1.5); retry after it, and you get the first request’s answer. An answer that carries a secret (a new API key or public run token, a session token, a generated signing secret) isn’t kept: a retry gets 409 idempotency-key-replay-withheld, with the first request’s status and when it succeeded (from 0.1.5).
Request Bodyrequired
Section titled “Request Bodyrequired”object
Lowercase letters, digits and single hyphens; generated when absent.
Whether it may put projects in production. Absent: blocked, until the operator allows it; true takes system:tenants:production.
A test tenant. The full system capability only.
The repository its deployments’ images must sit in, <host>/<path> with no trailing slash; takes system:tenants:image-prefix. On a runtime serving many tenants, a tenant without one can’t register a deployment.
The person made the tenant’s admin and member, by email: the runtime’s own sign-in finds them. The runtime gives them every capability but secrets:reveal, to put on the keys they make. Absent: the answer carries an admin key instead, which takes the full system capability.
object
An email address.
Examplegenerated
{ "name": "example", "slug": "example", "productionAllowed": true, "test": true, "imagePrefix": "example", "firstAdmin": { "email": "example", "name": "example" }}Responses
Section titled “Responses”The tenant, and its first admin.
object
A tenant, as the operator sees it: never its content.
object
closing: readable and exportable with its own keys for its window, nothing runs. suspended: the same, reversibly. closing wins over suspended.
A test tenant: left out of usage, invoices and counts.
The repository its deployments’ images must sit at or under, <host>/<path>.
object
The tenant’s first admin key, without firstAdmin: shown once.
object
Example
{ "tenant": { "state": "active" }}Malformed body: a name, slug, image prefix or first admin that is refused, or an unknown field.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Missing / malformed / expired / revoked bearer token.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}A tenant’s key or session (system-principal-required); a system key without system:tenants:create or system:tenants, or asking for what its scopes don’t give: production (system:tenants:production), an image prefix (system:tenants:image-prefix), an admin key or a test tenant (kindgi:system) (system-scope-missing, details.scope naming it).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Another tenant has the slug, or a create whose setup is pending holds it with another request (tenant-slug-taken); or its image prefix overlaps another tenant’s (image-prefix-taken); or an Idempotency-Key reused with another body; or that key’s replay, when the first answer held the admin key (idempotency-key-replay-withheld).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}The request body is over the API’s limit (request-too-large; details.maxBytes says how much).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Server error (unmapped domain code or framework crash).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}The operator’s access record can’t be written, so nothing was done (access-record-unavailable).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}See also
GET /v1/system/tenants: List tenantsGET /v1/system/tenants/{tenantId}: Read a tenantPATCH /v1/system/tenants/{tenantId}: Rename a tenantPOST /v1/system/tenants/{tenantId}/close: Close a tenant- People, API keys and service accounts
- Deploy on Google Cloud Run
- Serve many tenants
- Error code
access-record-unavailable - Error code
idempotency-key-replay-withheld - Error code
image-prefix-taken - Error code
request-too-large - Error code
system-principal-required - Error code
system-scope-missing - Error code
tenant-slug-taken - Every error code