Skip to content

Create a tenant

View as Markdown
POST
/v1/system/tenants
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.

Idempotency-Key
string
>= 1 characters

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

Media typeapplication/json
object
name
required
string
>= 1 characters <= 100 characters
slug

Lowercase letters, digits and single hyphens; generated when absent.

string
<= 63 characters /^[a-z0-9](?:-?[a-z0-9])*$/
productionAllowed

Whether it may put projects in production. Absent: blocked, until the operator allows it; true takes system:tenants:production.

boolean
test

A test tenant. The full system capability only.

boolean
imagePrefix

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.

string
firstAdmin

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
email
required

An email address.

string
<= 254 characters
name
required
string
>= 1 characters <= 100 characters
Examplegenerated
{
"name": "example",
"slug": "example",
"productionAllowed": true,
"test": true,
"imagePrefix": "example",
"firstAdmin": {
"email": "example",
"name": "example"
}
}

The tenant, and its first admin.

Media typeapplication/json
object
tenant
required

A tenant, as the operator sees it: never its content.

object
id
required
string format: uuid
name
required
string
slug
required
string
state
required

closing: readable and exportable with its own keys for its window, nothing runs. suspended: the same, reversibly. closing wins over suspended.

string
Allowed values: active suspended closing
createdAt
required
string format: date-time
test
required

A test tenant: left out of usage, invoices and counts.

boolean
productionAllowed
required
boolean
imagePrefix

The repository its deployments’ images must sit at or under, <host>/<path>.

string
closingAt
string format: date-time
suspendedAt
string format: date-time
suspendedReason
string
firstAdmin
object
personId
required
string
adminKey

The tenant’s first admin key, without firstAdmin: shown once.

object
tokenId
required
string
token
required
string
Example
{
"tenant": {
"state": "active"
}
}

Malformed body: a name, slug, image prefix or first admin that is refused, or an unknown field.

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

Missing / malformed / expired / revoked bearer token.

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

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

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

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

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

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

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

Server error (unmapped domain code or framework crash).

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

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

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

See also