Appearance
Operations API
26 operations. Paths are relative to the API host (http://localhost:5080 in local development). Every operation accepts an OIDC bearer token (Authorization: Bearer …) or a workspace API key (X-Api-Key: keel_live_…); errors are RFC 9457 Problem Details with a code (error codes). Permissions: matrix.
| Method | Path | Summary |
|---|---|---|
| GET | /api/v1/ops/exports/{fileName} | Download a tenant export written by offboarding (operators; the export folder must be shared with the worker). |
| GET | /api/v1/ops/health | Component health (the public /status document) for the console. |
| GET | /api/v1/ops/leads | Demo requests from the website, newest first, received in [from, to); cursor paging. Never older than the 24-month retention. |
| GET | /api/v1/ops/leads:export | CSV of the demo requests matching from/to (at most 10,000, newest first). Recorded in the operator log with the count only. |
| GET | /api/v1/ops/log | The operator log, newest first: offboarding tombstones, exports, support sessions. |
| GET | /api/v1/ops/me | The signed-in platform operator and their platform role, owner or operator (403 for everyone else). |
| GET | /api/v1/ops/overview | Customer tenants by plan and status, trials and contracts ending, usage at 80% of a limit or more, estimated monthly recurring revenue from list prices and new tenants per week. Internal tenants are left out. |
| GET | /api/v1/ops/platform-members | The platform owner and operators (operators; read-only for everyone but the owner). |
| POST | /api/v1/ops/platform-members | Owner: invite an operator by email. The IdP emails them a link to set a password and an authenticator app. |
| DELETE | /api/v1/ops/platform-members/{memberId} | Owner: remove an operator. Their platform access and open support sessions end at once. |
| POST | /api/v1/ops/platform-members/{memberId}:transfer-ownership | Owner: hand platform ownership to an operator (type their email to confirm). You stay an operator. |
| PUT | /api/v1/ops/platform-members/{memberId}/required-amr | Owner: require a sign-in method (token amr, e.g. hwk or otp) for one member, or clear it with null. |
| POST | /api/v1/ops/status/incidents | Operators: open an incident or maintenance on the status page; confirmed subscribers are emailed. |
| POST | /api/v1/ops/status/incidents/{incidentId}/updates | Operators: post an update to an open incident (Resolved closes it); confirmed subscribers are emailed. |
| POST | /api/v1/ops/support-sessions/{sessionId}:end | End a support session. |
| GET | /api/v1/ops/tenants | Every tenant with plan, region, licensed modules, usage counts, last activity and support consent. |
| POST | /api/v1/ops/tenants | Provision a customer tenant: its first workspace, a trial or a contract, and an Owner invitation emailed to the first Owner. The operator does not become a member. 200 with the existing tenant when the slug was already provisioned for the same first Owner. |
| GET | /api/v1/ops/tenants/{tenantId} | One tenant: identity, usage, billing (plan, contract, trial, internal), the first-Owner invitation and any scheduled deletion. |
| POST | /api/v1/ops/tenants/{tenantId}:cancel-offboarding | Cancel a tenant's scheduled deletion within its grace period (audited in the customer's log). |
| POST | /api/v1/ops/tenants/{tenantId}:extend-trial | Extend the tenant's Keel trial by 1–90 days (while it runs or within 30 days of its end; at most 90 days from its start). An ended trial becomes writable again. |
| POST | /api/v1/ops/tenants/{tenantId}:offboard | Offboard a tenant: scheduled 7 days out (read-only, Owners emailed, cancellable), or now with a reason; then an optional export and a verified hard delete of all its data (a platform job). |
| PUT | /api/v1/ops/tenants/{tenantId}/contract | Put the tenant on a contract: plan, add-ons, limit overrides (null = unlimited), end date and annual value (USD). Usage over the new limits comes back as warnings. |
| PUT | /api/v1/ops/tenants/{tenantId}/internal | Mark the tenant internal (excluded from customer metrics; no trial expiry, no billing notices) or a customer again. |
| POST | /api/v1/ops/tenants/{tenantId}/owner-invitation:resend | Email the first-Owner invitation again with a new link (the previous link stops working), valid for 7 days. |
| POST | /api/v1/ops/tenants/{tenantId}/owner-invitation:revoke | Withdraw the pending first-Owner invitation; its link stops working. |
| POST | /api/v1/ops/tenants/{tenantId}/support-sessions | Start a read-only support session in a tenant that granted consent (403 Support.NoConsent otherwise). |
DownloadTenantExport
http
GET /api/v1/ops/exports/{fileName}Download a tenant export written by offboarding (operators; the export folder must be shared with the worker).
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
fileName | path | string | yes |
| Status | Description | Body |
|---|---|---|
| 200 | OK | — |
| 403 | Forbidden | ProblemDetails |
GetOperatorHealth
http
GET /api/v1/ops/healthComponent health (the public /status document) for the console.
| Status | Description | Body |
|---|---|---|
| 200 | OK | PlatformStatusDto |
Response 200: PlatformStatusDto
| Field | Type | Notes |
|---|---|---|
components | StatusComponentDto[] | |
generatedAt | date-time | |
status | string | The worst component status (unknown components do not count). |
ListLeads
http
GET /api/v1/ops/leadsDemo requests from the website, newest first, received in [from, to); cursor paging. Never older than the 24-month retention.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
from | query | date-time | no | |
to | query | date-time | no | |
cursor | query | string | no | |
limit | query | int32 | no |
| Status | Description | Body |
|---|---|---|
| 200 | OK | LeadPageDto |
Response 200: LeadPageDto
| Field | Type | Notes |
|---|---|---|
items | LeadDto[] | |
nextCursor | string? |
ExportLeads
http
GET /api/v1/ops/leads:exportCSV of the demo requests matching from/to (at most 10,000, newest first). Recorded in the operator log with the count only.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
from | query | date-time | no | |
to | query | date-time | no |
| Status | Description | Body |
|---|---|---|
| 200 | OK | string |
| 400 | Bad Request | ProblemDetails |
| 403 | Forbidden | ProblemDetails |
ListOperatorLog
http
GET /api/v1/ops/logThe operator log, newest first: offboarding tombstones, exports, support sessions.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
limit | query | int32 | no |
| Status | Description | Body |
|---|---|---|
| 200 | OK | OperatorLogDto[] |
GetOperatorIdentity
http
GET /api/v1/ops/meThe signed-in platform operator and their platform role, owner or operator (403 for everyone else).
| Status | Description | Body |
|---|---|---|
| 200 | OK | OperatorIdentityResponse |
| 403 | Forbidden | ProblemDetails |
Response 200: OperatorIdentityResponse
| Field | Type | Notes |
|---|---|---|
name | string | |
role | string | M12.4: owner or operator. |
subject | string |
GetOperatorOverview
http
GET /api/v1/ops/overviewCustomer tenants by plan and status, trials and contracts ending, usage at 80% of a limit or more, estimated monthly recurring revenue from list prices and new tenants per week. Internal tenants are left out.
| Status | Description | Body |
|---|---|---|
| 200 | OK | OperatorOverviewDto |
Response 200: OperatorOverviewDto
| Field | Type | Notes |
|---|---|---|
byPlan | PlanCountDto[] | |
byStatus | OverviewStatusCountsDto | |
contractsEnding | ContractEndingDto[] | |
customerTenants | int32 | |
generatedAt | date-time | |
internalTenants | int32 | |
limitAlerts | LimitAlertDto[] | |
newTenantsPerWeek | WeeklyCountDto[] | |
revenue | RevenueEstimateDto | |
trialsEnding | TrialEndingDto[] | |
trialsEndingIn14Days | int32 | |
trialsEndingIn7Days | int32 |
ListPlatformMembers
http
GET /api/v1/ops/platform-membersThe platform owner and operators (operators; read-only for everyone but the owner).
| Status | Description | Body |
|---|---|---|
| 200 | OK | PlatformMemberDto[] |
InvitePlatformOperator
http
POST /api/v1/ops/platform-membersOwner: invite an operator by email. The IdP emails them a link to set a password and an authenticator app.
Request body (application/json): InvitePlatformOperatorRequest
| Field | Type | Notes |
|---|---|---|
displayName | string? | |
email | string | |
requiredAmr | string? |
| Status | Description | Body |
|---|---|---|
| 200 | OK | PlatformOperatorInvitedDto |
Response 200: PlatformOperatorInvitedDto
| Field | Type | Notes |
|---|---|---|
emailed | boolean | |
member | PlatformMemberDto |
RemovePlatformOperator
http
DELETE /api/v1/ops/platform-members/{memberId}Owner: remove an operator. Their platform access and open support sessions end at once.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
memberId | path | uuid | yes |
| Status | Description | Body |
|---|---|---|
| 204 | No Content | — |
TransferPlatformOwnership
http
POST /api/v1/ops/platform-members/{memberId}:transfer-ownershipOwner: hand platform ownership to an operator (type their email to confirm). You stay an operator.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
memberId | path | uuid | yes |
Request body (application/json): TransferPlatformOwnershipRequest
| Field | Type | Notes |
|---|---|---|
confirmEmail | string |
| Status | Description | Body |
|---|---|---|
| 204 | No Content | — |
SetPlatformMemberRequiredAmr
http
PUT /api/v1/ops/platform-members/{memberId}/required-amrOwner: require a sign-in method (token amr, e.g. hwk or otp) for one member, or clear it with null.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
memberId | path | uuid | yes |
Request body (application/json): SetRequiredAmrRequest
| Field | Type | Notes |
|---|---|---|
requiredAmr | string? |
| Status | Description | Body |
|---|---|---|
| 200 | OK | PlatformMemberDto |
Response 200: PlatformMemberDto
| Field | Type | Notes |
|---|---|---|
addedAt | date-time | |
addedBy | string | |
displayName | string? | |
email | string? | |
id | uuid | |
requiredAmr | string? | |
role | string | |
subject | string |
CreateStatusIncident
http
POST /api/v1/ops/status/incidentsOperators: open an incident or maintenance on the status page; confirmed subscribers are emailed.
Request body (application/json): CreateStatusIncidentRequest
| Field | Type | Notes |
|---|---|---|
components | string[] | |
impact | StatusPageIncidentImpact | One of Maintenance, Minor, Major |
message | string | |
status | StatusPageIncidentStatus | One of Investigating, Identified, Monitoring, Resolved |
title | string |
| Status | Description | Body |
|---|---|---|
| 201 | Created | StatusIncidentDto |
Response 201: StatusIncidentDto
| Field | Type | Notes |
|---|---|---|
components | string[] | |
id | uuid | |
impact | StatusPageIncidentImpact | One of Maintenance, Minor, Major |
resolvedAt | date-time? | |
startedAt | date-time | |
status | StatusPageIncidentStatus | One of Investigating, Identified, Monitoring, Resolved |
title | string | |
updates | StatusIncidentUpdateDto[] |
PostStatusIncidentUpdate
http
POST /api/v1/ops/status/incidents/{incidentId}/updatesOperators: post an update to an open incident (Resolved closes it); confirmed subscribers are emailed.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
incidentId | path | uuid | yes |
Request body (application/json): PostStatusIncidentUpdateRequest
| Field | Type | Notes |
|---|---|---|
message | string | |
status | StatusPageIncidentStatus | One of Investigating, Identified, Monitoring, Resolved |
| Status | Description | Body |
|---|---|---|
| 200 | OK | StatusIncidentDto |
Response 200: StatusIncidentDto
| Field | Type | Notes |
|---|---|---|
components | string[] | |
id | uuid | |
impact | StatusPageIncidentImpact | One of Maintenance, Minor, Major |
resolvedAt | date-time? | |
startedAt | date-time | |
status | StatusPageIncidentStatus | One of Investigating, Identified, Monitoring, Resolved |
title | string | |
updates | StatusIncidentUpdateDto[] |
EndSupportSession
http
POST /api/v1/ops/support-sessions/{sessionId}:endEnd a support session.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
sessionId | path | uuid | yes |
| Status | Description | Body |
|---|---|---|
| 204 | No Content | — |
ListOperatorTenants
http
GET /api/v1/ops/tenantsEvery tenant with plan, region, licensed modules, usage counts, last activity and support consent.
| Status | Description | Body |
|---|---|---|
| 200 | OK | OperatorTenantDto[] |
ProvisionTenant
http
POST /api/v1/ops/tenantsProvision a customer tenant: its first workspace, a trial or a contract, and an Owner invitation emailed to the first Owner. The operator does not become a member. 200 with the existing tenant when the slug was already provisioned for the same first Owner.
Request body (application/json): ProvisionTenantRequest
| Field | Type | Notes |
|---|---|---|
contract | ContractTermsInput? | |
firstOwnerEmail | string | |
firstWorkspaceName | string? | The first workspace's name; the tenant's name when left out. |
internal | boolean? | |
name | string | |
region | string | US or EU. |
slug | string |
| Status | Description | Body |
|---|---|---|
| 200 | OK | ProvisionedTenantDto |
| 201 | Created | ProvisionedTenantDto |
Response 200: ProvisionedTenantDto
| Field | Type | Notes |
|---|---|---|
billing | OperatorTenantBillingDto? | |
created | boolean | |
invitationId | uuid | |
tenantId | uuid | |
workspaceId | uuid |
GetOperatorTenant
http
GET /api/v1/ops/tenants/{tenantId}One tenant: identity, usage, billing (plan, contract, trial, internal), the first-Owner invitation and any scheduled deletion.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
tenantId | path | uuid | yes |
| Status | Description | Body |
|---|---|---|
| 200 | OK | OperatorTenantDetailDto |
Response 200: OperatorTenantDetailDto
| Field | Type | Notes |
|---|---|---|
billing | OperatorTenantBillingDto? | |
contractOptions | ContractOptionsDto | |
deletion | TenantDeletionDto? | |
ownerInvitation | FoundingInvitation? | |
tenant | OperatorTenantDto |
CancelTenantOffboarding
http
POST /api/v1/ops/tenants/{tenantId}:cancel-offboardingCancel a tenant's scheduled deletion within its grace period (audited in the customer's log).
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
tenantId | path | uuid | yes |
| Status | Description | Body |
|---|---|---|
| 200 | OK | TenantDeletionDto |
Response 200: TenantDeletionDto
| Field | Type | Notes |
|---|---|---|
export | boolean | |
id | uuid | |
purgeJobId | uuid? | |
requestedAt | date-time | |
requestedByName | string | |
requestedByType | string | |
scheduledFor | date-time |
ExtendTenantTrial
http
POST /api/v1/ops/tenants/{tenantId}:extend-trialExtend the tenant's Keel trial by 1–90 days (while it runs or within 30 days of its end; at most 90 days from its start). An ended trial becomes writable again.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
tenantId | path | uuid | yes |
Request body (application/json): ExtendTrialRequest
| Field | Type | Notes |
|---|---|---|
days | int32 |
| Status | Description | Body |
|---|---|---|
| 200 | OK | OperatorTenantBillingDto |
Response 200: OperatorTenantBillingDto
| Field | Type | Notes |
|---|---|---|
addOns | string[] | |
annualValueUsd | number? | |
endsOn | date? | |
limitOverrides | object | |
limits | object | |
plan | string | |
readOnly | boolean | |
source | string | |
status | string | |
trialEndsAt | date-time? | |
trialStartedAt | date-time? | |
warnings | string[] |
OffboardTenant
http
POST /api/v1/ops/tenants/{tenantId}:offboardOffboard a tenant: scheduled 7 days out (read-only, Owners emailed, cancellable), or now with a reason; then an optional export and a verified hard delete of all its data (a platform job).
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
tenantId | path | uuid | yes |
Request body (application/json): OffboardTenantRequest
| Field | Type | Notes |
|---|---|---|
confirmSlug | string | |
export | boolean | |
immediate | boolean? | Skip the 7-day grace period (legal or abuse cases); needs Reason. |
reason | string? |
| Status | Description | Body |
|---|---|---|
| 202 | Accepted | OffboardingQueuedDto |
Response 202: OffboardingQueuedDto
| Field | Type | Notes |
|---|---|---|
jobId | uuid | |
scheduledFor | date-time? | |
slug | string | |
tenantId | uuid |
SetTenantContract
http
PUT /api/v1/ops/tenants/{tenantId}/contractPut the tenant on a contract: plan, add-ons, limit overrides (null = unlimited), end date and annual value (USD). Usage over the new limits comes back as warnings.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
tenantId | path | uuid | yes |
Request body (application/json): ContractTermsInput
| Field | Type | Notes |
|---|---|---|
addOns | string[]? | |
annualValueUsd | number? | |
endsOn | date? | |
limitOverrides | object? | |
plan | string |
| Status | Description | Body |
|---|---|---|
| 200 | OK | OperatorTenantBillingDto |
Response 200: OperatorTenantBillingDto
| Field | Type | Notes |
|---|---|---|
addOns | string[] | |
annualValueUsd | number? | |
endsOn | date? | |
limitOverrides | object | |
limits | object | |
plan | string | |
readOnly | boolean | |
source | string | |
status | string | |
trialEndsAt | date-time? | |
trialStartedAt | date-time? | |
warnings | string[] |
SetTenantInternal
http
PUT /api/v1/ops/tenants/{tenantId}/internalMark the tenant internal (excluded from customer metrics; no trial expiry, no billing notices) or a customer again.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
tenantId | path | uuid | yes |
Request body (application/json): SetTenantInternalRequest
| Field | Type | Notes |
|---|---|---|
internal | boolean |
| Status | Description | Body |
|---|---|---|
| 200 | OK | OperatorTenantDto |
Response 200: OperatorTenantDto
| Field | Type | Notes |
|---|---|---|
createdAt | date-time | |
deletionScheduledFor | date-time? | |
features | string[] | |
id | uuid | |
internal | boolean | |
lastActivityAt | date-time? | |
members | int32 | |
models | int32 | |
modules | string[] | |
name | string | |
plan | string | |
region | string | |
slug | string | |
sources | int32 | |
status | string | |
supportAccessExpiresAt | date-time? | |
workspaces | int32 |
ResendOwnerInvitation
http
POST /api/v1/ops/tenants/{tenantId}/owner-invitation:resendEmail the first-Owner invitation again with a new link (the previous link stops working), valid for 7 days.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
tenantId | path | uuid | yes |
| Status | Description | Body |
|---|---|---|
| 200 | OK | FoundingInvitation |
Response 200: FoundingInvitation
| Field | Type | Notes |
|---|---|---|
acceptedAt | date-time? | |
email | string | |
expiresAt | date-time | |
id | uuid | |
invitedBy | string? | |
revokedAt | date-time? | |
status | string |
RevokeOwnerInvitation
http
POST /api/v1/ops/tenants/{tenantId}/owner-invitation:revokeWithdraw the pending first-Owner invitation; its link stops working.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
tenantId | path | uuid | yes |
| Status | Description | Body |
|---|---|---|
| 200 | OK | FoundingInvitation |
Response 200: FoundingInvitation
| Field | Type | Notes |
|---|---|---|
acceptedAt | date-time? | |
email | string | |
expiresAt | date-time | |
id | uuid | |
invitedBy | string? | |
revokedAt | date-time? | |
status | string |
StartSupportSession
http
POST /api/v1/ops/tenants/{tenantId}/support-sessionsStart a read-only support session in a tenant that granted consent (403 Support.NoConsent otherwise).
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
tenantId | path | uuid | yes |
Request body (application/json): StartSupportSessionRequest
| Field | Type | Notes |
|---|---|---|
reason | string |
| Status | Description | Body |
|---|---|---|
| 200 | OK | StartedSupportSessionDto |
Response 200: StartedSupportSessionDto
| Field | Type | Notes |
|---|---|---|
expiresAt | date-time | |
sessionId | uuid | |
tenantId | uuid | |
tenantName | string |