user-engine/openapi/portal-v1.yaml
tegwick 8229c6dd33
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Expand portal onboarding and administration
2026-08-08 23:09:23 +02:00

306 lines
10 KiB
YAML

openapi: 3.1.0
info:
title: user-engine portal API
version: 0.2.0
servers:
- url: /api/v1
security:
- verifiedOidc: []
paths:
/me:
get:
operationId: currentUser
responses:
"200":
description: Current user and linked identities
"403":
$ref: "#/components/responses/Denied"
/me/profile:
patch:
operationId: updateCurrentUserProfile
description: Updates display name and versioned consent; verified email cannot be changed here.
parameters: [{$ref: "#/components/parameters/IdempotencyKey"}]
requestBody:
required: true
content:
application/json:
schema: {$ref: "#/components/schemas/UpdateSelfProfile"}
responses:
"200": {description: Durable self-service profile state}
"403": {$ref: "#/components/responses/Denied"}
/registrations:
post:
operationId: startRegistration
parameters:
- $ref: "#/components/parameters/IdempotencyKey"
responses:
"201":
description: Registration started
"403":
$ref: "#/components/responses/Denied"
/registrations/{registrationId}/complete:
post:
operationId: completeRegistration
parameters:
- name: registrationId
in: path
required: true
schema: {type: string}
- $ref: "#/components/parameters/IdempotencyKey"
responses:
"200":
description: Registration completed
"400":
description: Missing verified factors or invalid state
/tenants/{tenant}/users:
get:
operationId: listTenantUsers
parameters:
- $ref: "#/components/parameters/Tenant"
- {name: offset, in: query, schema: {type: integer, minimum: 0}}
- {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 100}}
responses:
"200": {description: Tenant-scoped memberships}
"403": {$ref: "#/components/responses/Denied"}
post:
operationId: createTenantUser
parameters:
- $ref: "#/components/parameters/Tenant"
- $ref: "#/components/parameters/IdempotencyKey"
requestBody:
required: true
content:
application/json:
schema: {$ref: "#/components/schemas/CreateUser"}
responses:
"201": {description: Tenant user and membership created}
"403": {$ref: "#/components/responses/Denied"}
/tenants/{tenant}/users/{userId}:
patch:
operationId: updateTenantUserLifecycle
parameters:
- $ref: "#/components/parameters/Tenant"
- name: userId
in: path
required: true
schema: {type: string}
- $ref: "#/components/parameters/IdempotencyKey"
responses:
"200": {description: Tenant account updated}
"403": {$ref: "#/components/responses/Denied"}
delete:
operationId: removeTenantUser
parameters:
- $ref: "#/components/parameters/Tenant"
- $ref: "#/components/parameters/UserId"
- $ref: "#/components/parameters/IdempotencyKey"
responses:
"200": {description: Login deprovisioned and tenant account disabled}
"403": {$ref: "#/components/responses/Denied"}
/tenants/{tenant}/invitations:
get:
operationId: listInvitations
parameters: [{$ref: "#/components/parameters/Tenant"}]
responses:
"200": {description: Tenant invitations}
"403": {$ref: "#/components/responses/Denied"}
post:
operationId: createInvitation
parameters:
- $ref: "#/components/parameters/Tenant"
- $ref: "#/components/parameters/IdempotencyKey"
requestBody:
required: true
content:
application/json:
schema: {$ref: "#/components/schemas/CreateInvitation"}
responses:
"201": {description: Invitation created with an expiry and version}
"409": {$ref: "#/components/responses/Conflict"}
/tenants/{tenant}/invitations/{invitationId}/resend:
post:
operationId: resendInvitation
parameters:
- $ref: "#/components/parameters/Tenant"
- $ref: "#/components/parameters/InvitationId"
- $ref: "#/components/parameters/IfMatch"
responses:
"200": {description: Invitation expiry renewed and version advanced}
"409": {$ref: "#/components/responses/Conflict"}
/tenants/{tenant}/invitations/{invitationId}/expire:
post:
operationId: expireInvitation
parameters:
- $ref: "#/components/parameters/Tenant"
- $ref: "#/components/parameters/InvitationId"
- $ref: "#/components/parameters/IfMatch"
responses:
"200": {description: Invitation revoked}
"409": {$ref: "#/components/responses/Conflict"}
/invitations/{invitationId}/claim:
post:
operationId: claimInvitation
parameters:
- $ref: "#/components/parameters/InvitationId"
- $ref: "#/components/parameters/IdempotencyKey"
responses:
"200": {description: Invitation claimed and identity linked}
"400": {description: Invitation expired, revoked, or already claimed}
/onboarding/{journeyId}/steps/{stepKey}/complete:
post:
operationId: completeOnboardingStep
description: Completes an active user-engine-owned step. Provider-owned steps must use their external handoff and callback.
parameters:
- name: journeyId
in: path
required: true
schema: {type: string}
- name: stepKey
in: path
required: true
schema: {type: string}
- $ref: "#/components/parameters/IdempotencyKey"
responses:
"200": {description: Updated resumable onboarding journey}
"403": {$ref: "#/components/responses/Denied"}
"404": {description: Journey is absent or belongs to another user}
/platform/tenants:
post:
operationId: createPlatformTenant
description: Creates a tenant through the configured tenant authority and optionally prepares its first administrator.
parameters: [{$ref: "#/components/parameters/IdempotencyKey"}]
requestBody:
required: true
content:
application/json:
schema: {$ref: "#/components/schemas/CreateTenant"}
responses:
"201": {description: Tenant created or resumed and first administrator prepared}
"403": {$ref: "#/components/responses/Denied"}
/platform/tenants/{tenant}/users/{userId}/recover:
post:
operationId: recoverTenantUser
description: Reconciles or recreates the provider identity and activates the tenant account without exposing provider credentials.
parameters:
- $ref: "#/components/parameters/Tenant"
- $ref: "#/components/parameters/UserId"
- $ref: "#/components/parameters/IdempotencyKey"
responses:
"200": {description: Redacted recovery outcome}
"403": {$ref: "#/components/responses/Denied"}
/platform/outbox/deliver:
post:
operationId: deliverOutbox
requestBody:
content:
application/json:
schema:
type: object
properties:
worker_id: {type: string}
max_attempts: {type: integer, minimum: 1, maximum: 20}
additionalProperties: false
responses:
"200": {description: Bounded delivery results}
"403": {$ref: "#/components/responses/Denied"}
/platform/outbox/{eventId}/replay:
post:
operationId: replayOutboxEvent
parameters:
- name: eventId
in: path
required: true
schema: {type: string}
responses:
"200": {description: Event returned to pending delivery}
"403": {$ref: "#/components/responses/Denied"}
components:
securitySchemes:
verifiedOidc:
type: openIdConnect
openIdConnectUrl: https://kc.coulomb.social/.well-known/openid-configuration
parameters:
Tenant:
name: tenant
in: path
required: true
schema: {type: string}
IdempotencyKey:
name: Idempotency-Key
in: header
required: true
schema: {type: string, minLength: 16, maxLength: 200}
UserId:
name: userId
in: path
required: true
schema: {type: string}
InvitationId:
name: invitationId
in: path
required: true
schema: {type: string}
IfMatch:
name: If-Match
in: header
required: true
schema: {type: string, pattern: '^"?[0-9]+"?$'}
schemas:
CreateUser:
type: object
required: [primary_email, role]
properties:
primary_email: {type: string, format: email}
display_name: {type: string, maxLength: 200}
role: {type: string, enum: [user, tenant-admin]}
additionalProperties: false
CreateInvitation:
type: object
required: [primary_email]
properties:
primary_email: {type: string, format: email}
display_name: {type: string, maxLength: 200}
role: {type: string, enum: [user, tenant-admin]}
application_id: {type: string}
scope_id: {type: string}
additionalProperties: false
CreateTenant:
type: object
required: [tenant, display_name]
properties:
tenant: {type: string, pattern: '^tenant:'}
display_name: {type: string, minLength: 1, maxLength: 200}
first_admin:
type: object
required: [primary_email]
properties:
primary_email: {type: string, format: email}
display_name: {type: string, maxLength: 200}
additionalProperties: false
additionalProperties: false
UpdateSelfProfile:
type: object
required: [display_name, consent_accepted, consent_version]
properties:
display_name: {type: string, minLength: 1, maxLength: 200}
consent_accepted: {type: boolean}
consent_version: {type: string, minLength: 1, maxLength: 100}
additionalProperties: false
Error:
type: object
required: [error, message, correlation_id]
properties:
error: {type: string}
message: {type: string}
correlation_id: {type: string}
responses:
Denied:
description: Caller is unauthenticated or unauthorized
content:
application/json:
schema: {$ref: "#/components/schemas/Error"}
Conflict:
description: Optimistic concurrency or uniqueness conflict
content:
application/json:
schema: {$ref: "#/components/schemas/Error"}