# Management API reference

> Your server, CI or Terraform provisions tenants, apps, custom domains, sender addresses, social logins, resource servers, organizations and theming across an account with a secret credential, an account API key.

Source: https://docs.authalla.com/docs/management-api-reference

OpenAPI spec: https://api.authalla.com/management/v1/openapi.json (YAML: https://api.authalla.com/management/v1/openapi.yaml)
Base URL: https://api.authalla.com

## Overview

The Authalla **Management API** (control plane) lets an operator provision
and configure identity infrastructure across an entire account: tenants
and their allowed origins, apps, custom domains, sender addresses,
social logins, resource servers, organizations, and themes.

**Called with:** a **secret credential**, an account API key. Call it from
your server, CI or Terraform.

**Reaches:** every tenant in the account, or the tenants the key allows,
so the key never goes in a browser or a mobile app.

**Use another API when:**

- your server creates, reads or changes the users of a tenant: use the
  **Application API**, called with a different secret credential, a
  tenant app's client credentials token
  ([spec](https://api.authalla.com/api/v1/openapi.json)).
- a signed-in user views or changes their own details, sessions or
  connected apps: use the **User API**, called with that user's own
  access token
  ([spec](https://api.authalla.com/me/v1/openapi.json)).

Which API, and which credential, for which job:
https://docs.authalla.com/docs/choosing-an-api

### Account API keys

The Management API authenticates with an **account API key** — a static,
account-wide bearer token created from the Authalla dashboard:

```
Authorization: Bearer authalla_ak_<keyid>_<secret>
```

Unlike the tenant-bound Application API credential, an account key is scoped
to an account and can act across all (or an allowlisted subset of) its
tenants. Keys carry an explicit scope set (`resource:read` / `resource:write`)
and are shown exactly once at creation. The scopes are listed on the
`accountApiKey` security scheme, and each operation names the one it needs
in `x-required-scope`.

Errors share one shape with a stable `error` code, documented at
https://docs.authalla.com/docs/errors.

## Scopes

- `account:read`: Read the account the key belongs to
- `tenants:read`: Read tenants and their allowed origins
- `tenants:write`: Create, update, and delete tenants, and add and remove their allowed origins
- `apps:read`: Read apps
- `apps:write`: Create, update, and delete apps
- `domains:read`: Read custom domains
- `domains:write`: Create, verify, and delete custom domains
- `sender-addresses:read`: Read sender addresses, and the address a tenant sends from
- `sender-addresses:write`: Add, verify, and remove sender addresses, and choose the address a tenant sends from
- `social-logins:read`: Read social login providers
- `social-logins:write`: Create, update, and delete social login providers
- `resource-servers:read`: Read resource servers and their scopes
- `resource-servers:write`: Create, update, and delete resource servers and their scopes
- `organizations:read`: Read organizations and their members
- `organizations:write`: Create, update, and delete organizations and their email domains
- `organization-members:write`: Add and remove an organization's members
- `portal-links:write`: Create and revoke an organization's setup link
- `theme:read`: Read the login page theme
- `theme:write`: Update the login page theme

Errors use one JSON shape with a stable `error` code; see https://docs.authalla.com/docs/errors.

## Operations

### Get account details

`GET /management/v1/account` · Required scope: `account:read`

Returns details of the account the API key belongs to.

Responses:

- `200` Account details (Account)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)

### List tenants

`GET /management/v1/tenants` · Required scope: `tenants:read`

Lists the tenants in the account. Account-collection operation —
available only to account-global keys.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | integer | no |  |
| `offset` | query | integer | no |  |

Responses:

- `200` The account's tenants (TenantList)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)

### Create a tenant

`POST /management/v1/tenants` · Required scope: `tenants:write`

Creates a tenant in the account. Account-collection operation —
available only to account-global keys.

Request body (`application/json`, required): TenantInput

Responses:

- `201` The created tenant (Tenant)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)

### Get a tenant

`GET /management/v1/tenants/{tenant_id}` · Required scope: `tenants:read`

Returns the tenant.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Responses:

- `200` The tenant (Tenant)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Update a tenant

`PUT /management/v1/tenants/{tenant_id}` · Required scope: `tenants:write`

Renames the tenant and changes its settings. `name` is required; settings left out of the body keep their current value.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Request body (`application/json`, required): TenantInput

Responses:

- `200` The updated tenant (Tenant)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Delete a tenant

`DELETE /management/v1/tenants/{tenant_id}` · Required scope: `tenants:write`

Deletes a tenant. Account-collection operation — available only to
account-global keys.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Responses:

- `204` Deleted
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### List a tenant's allowed origins

`GET /management/v1/tenants/{tenant_id}/allowed-origins` · Required scope: `tenants:read`

Returns the browser origins allowed to call the tenant (CORS): its
OAuth2 endpoints, the Application API and the User API. While the
list is empty, a tenant that never had allowed origins, or whose
last one was removed through this API, allows the origins of its
apps' redirect URIs instead. A tenant whose last origin was removed
in the dashboard allows no browser origin.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Responses:

- `200` The tenant's allowed origins (AllowedOrigins)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Add an allowed origin

`POST /management/v1/tenants/{tenant_id}/allowed-origins` · Required scope: `tenants:write`

Allows a browser origin to call the tenant, for example a preview
deployment of a single-page app. The origin is stored as scheme,
host and port: a path is dropped, and a default port (`:443`,
`:80`) is left out; the first change stores the tenant's existing
origins the same way. Adding an origin the tenant already allows
changes nothing and returns 200, so a deployment can add its origin
on every run; a new origin returns 201.

Once a tenant has an allowed origin, only its allowed origins can
call it from a browser, not the origins of its apps' redirect URIs.
A tenant can have up to 50 allowed origins; adding one more returns
409.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Request body (`application/json`, required): AllowedOriginInput

Responses:

- `200` The tenant already allowed the origin; its allowed origins, unchanged (AllowedOrigins)
- `201` The origin was added; the tenant's allowed origins (AllowedOrigins)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)
- `409` The request conflicts with the current state of the resource. (Error)

### Remove an allowed origin

`DELETE /management/v1/tenants/{tenant_id}/allowed-origins` · Required scope: `tenants:write`

Stops a browser origin from calling the tenant, for example when a
preview deployment is torn down. The origin is matched after the
same normalization as when it was added. An origin the tenant
doesn't allow returns 404. Removing the last one returns the tenant
to allowing the origins of its apps' redirect URIs, as before it had
any.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `origin` | query | string | yes | The origin to remove, such as `https://pr-42.preview.acme.com`. It is a query parameter so its `://` needs no escaping. |

Responses:

- `200` The tenant's remaining allowed origins (AllowedOrigins)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### List apps

`GET /management/v1/tenants/{tenant_id}/apps` · Required scope: `apps:read`

Lists the apps in the given tenant.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Responses:

- `200` The tenant's apps (AppList)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Create an app

`POST /management/v1/tenants/{tenant_id}/apps` · Required scope: `apps:write`

Creates an app in the given tenant. For confidential apps
the generated app secret is returned exactly once in the response.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Request body (`application/json`, required): AppInput

Responses:

- `201` The created app (with one-time secret for confidential apps) (AppWithSecret)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Get an app

`GET /management/v1/tenants/{tenant_id}/apps/{app_id}` · Required scope: `apps:read`

Returns the app. The app secret is never returned after creation.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `app_id` | path | string | yes | Public ID of the app the request acts on. It is also the app's OAuth2 `client_id`. |

Responses:

- `200` The app (App)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Update an app (full replace)

`PUT /management/v1/tenants/{tenant_id}/apps/{app_id}` · Required scope: `apps:write`

Replaces the app's mutable fields. Redirect URIs and allowed logout
URIs are replaced with exactly the sets provided (omitting them clears
them).

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `app_id` | path | string | yes | Public ID of the app the request acts on. It is also the app's OAuth2 `client_id`. |

Request body (`application/json`, required): AppInput

Responses:

- `200` The updated app (App)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Delete an app

`DELETE /management/v1/tenants/{tenant_id}/apps/{app_id}` · Required scope: `apps:write`

Deletes the app. It can no longer sign users in or request tokens.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `app_id` | path | string | yes | Public ID of the app the request acts on. It is also the app's OAuth2 `client_id`. |

Responses:

- `204` Deleted
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### List an app's secrets

`GET /management/v1/tenants/{tenant_id}/apps/{app_id}/secrets` · Required scope: `apps:read`

Returns metadata for every secret the app holds, newest first. Secret
values are never returned: a secret is shown exactly once, in the
response to the call that created it. Public apps return an empty
list.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `app_id` | path | string | yes | Public ID of the app the request acts on. It is also the app's OAuth2 `client_id`. |

Responses:

- `200` The app's secrets (AppSecretList)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Add an app secret

`POST /management/v1/tenants/{tenant_id}/apps/{app_id}/secrets` · Required scope: `apps:write`

Issues a new secret and returns its value exactly once. An app may hold
at most three secrets, counting expired ones; over that the call returns
409 and an expired secret has to be deleted first.

Set `expire_existing_at` to retire the outgoing secret in the same call:
both secrets authenticate the app until that time, which is what makes
a rotation possible without an outage.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `app_id` | path | string | yes | Public ID of the app the request acts on. It is also the app's OAuth2 `client_id`. |

Request body (`application/json`): AppSecretInput

Responses:

- `201` The new secret, including its value (AppSecretWithValue)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)
- `409` The request conflicts with the current state of the resource. (Error)

### Set when an app secret expires

`PATCH /management/v1/tenants/{tenant_id}/apps/{app_id}/secrets/{secret_id}` · Required scope: `apps:write`

Sets the secret's expiry. A time in the past or now revokes it
immediately; there is no separate revoke operation.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `app_id` | path | string | yes | Public ID of the app the request acts on. It is also the app's OAuth2 `client_id`. |
| `secret_id` | path | string | yes | Public ID of the app secret the request acts on. |

Request body (`application/json`, required): AppSecretExpiryInput

Responses:

- `200` The updated secret (AppSecret)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Delete an expired app secret

`DELETE /management/v1/tenants/{tenant_id}/apps/{app_id}/secrets/{secret_id}` · Required scope: `apps:write`

Frees the secret's slot. Only an already-expired secret can be deleted;
expiring it first is what makes the deletion safe.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `app_id` | path | string | yes | Public ID of the app the request acts on. It is also the app's OAuth2 `client_id`. |
| `secret_id` | path | string | yes | Public ID of the app secret the request acts on. |

Responses:

- `204` Deleted
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Get tenant theme

`GET /management/v1/tenants/{tenant_id}/theme` · Required scope: `theme:read`

Returns the login-page theme for the given tenant.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Responses:

- `200` The tenant's theme (Theme)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Update tenant theme

`PUT /management/v1/tenants/{tenant_id}/theme` · Required scope: `theme:write`

Updates the login-page theme for the given tenant. Only the fields
present in the request body are changed; omitted fields keep their
current value.

A `logo_url`, `dark.logo_url` or `icon_url` that is an absolute
http(s) URL is fetched once and stored by Authalla; the response carries the stored
copy's `/oauth2/logo/...` path. The image must be PNG, JPEG, GIF or
WebP, at most 2 MiB, served without a redirect.

Colours are 3- or 6-digit hex (`#4f46e5`, `#fff`). Text and secondary
buttons are derived from them, so they stay readable on the card.
Dark colours are only used when `color_mode` is `light_dark`. Sending
them does not change the mode. `corner_style` applies to both.

`default_language` (`en`, `fi` or `sv`) is the sign-in language when
the app passes no supported `ui_locales`, in place of the browser's.
An empty string goes back to following the browser.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Request body (`application/json`, required): ThemeUpdate

Responses:

- `200` The updated theme (Theme)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### List custom domains

`GET /management/v1/tenants/{tenant_id}/domains` · Required scope: `domains:read`

Lists the custom login domains in the given tenant.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Responses:

- `200` The tenant's custom domains (CustomDomainList)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Create a custom domain

`POST /management/v1/tenants/{tenant_id}/domains` · Required scope: `domains:write`

Adds a custom login domain to the given tenant. The response includes the
DNS records the operator must configure to verify and activate it.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Request body (`application/json`, required): CustomDomainInput

Responses:

- `201` The created custom domain (with DNS records to configure) (CustomDomain)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)
- `409` The request conflicts with the current state of the resource. (Error)

### Get a custom domain

`GET /management/v1/tenants/{tenant_id}/domains/{domain_id}` · Required scope: `domains:read`

Returns the custom domain, with its status and the DNS records it needs.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `domain_id` | path | string | yes | Public ID of the custom domain the request acts on. |

Responses:

- `200` The custom domain (CustomDomain)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Delete a custom domain

`DELETE /management/v1/tenants/{tenant_id}/domains/{domain_id}` · Required scope: `domains:write`

Removes the custom domain from the tenant.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `domain_id` | path | string | yes | Public ID of the custom domain the request acts on. |

Responses:

- `204` Deleted
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Verify a custom domain

`POST /management/v1/tenants/{tenant_id}/domains/{domain_id}/verify` · Required scope: `domains:write`

Triggers re-verification of the custom domain's DNS configuration and
returns its freshly resolved status and DNS records.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `domain_id` | path | string | yes | Public ID of the custom domain the request acts on. |

Responses:

- `200` The custom domain with refreshed status (CustomDomain)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### List sender addresses

`GET /management/v1/sender-addresses` · Required scope: `sender-addresses:read`

Lists the account's sender addresses, oldest first, with the tenants
that send from each. Account-wide operation — available only to
account-global keys.

Responses:

- `200` The account's sender addresses (SenderAddressList)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)

### Add a sender address

`POST /management/v1/sender-addresses` · Required scope: `sender-addresses:write`

Adds an address on a domain the account owns. The response has the DNS
records to publish at the domain before calling verify. An address on a
domain the account has verified already is verified at once. Fails with
409 if the account has the address already or another account has
verified its domain; another account's unverified claim on the domain
is released. Account-wide operation — available only to account-global
keys.

Request body (`application/json`, required): SenderAddressInput

Responses:

- `201` The sender address, with the DNS records to publish (SenderAddress)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `409` The request conflicts with the current state of the resource. (Error)

### Get a sender address

`GET /management/v1/sender-addresses/{address_id}` · Required scope: `sender-addresses:read`

Returns the sender address with its DNS records as the last check found
them. Account-wide operation — available only to account-global keys.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `address_id` | path | string | yes | ID of the sender address the request acts on. |

Responses:

- `200` The sender address (SenderAddress)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Remove a sender address

`DELETE /management/v1/sender-addresses/{address_id}` · Required scope: `sender-addresses:write`

Deletes the address from the account and from the email provider. The
tenants that sent from it send from Authalla's sign-in address again. Removing
the last address on a domain removes the domain too, so adding an
address on it again means publishing new DNS records. Account-wide
operation — available only to account-global keys.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `address_id` | path | string | yes | ID of the sender address the request acts on. |

Responses:

- `204` Removed
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Check a sender address's DNS records

`POST /management/v1/sender-addresses/{address_id}/verify` · Required scope: `sender-addresses:write`

Looks up each DNS record of the address's domain and returns what it
found. Once all are published and the email provider has authenticated
the domain, the domain is verified, with every address on it, and stays
verified. Account-wide operation — available only to account-global
keys.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `address_id` | path | string | yes | ID of the sender address the request acts on. |

Responses:

- `200` The sender address, with whether this check found each record (SenderAddress)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Get the address a tenant sends from

`GET /management/v1/tenants/{tenant_id}/sender-address` · Required scope: `sender-addresses:read`

Returns the address the tenant's sign-in emails come from.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Responses:

- `200` The address the tenant sends from (TenantSender)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Send a tenant's emails from a sender address

`PUT /management/v1/tenants/{tenant_id}/sender-address` · Required scope: `sender-addresses:write`

Makes the tenant's sign-in emails come from one of the account's sender
addresses. Fails with 409 when the address isn't verified yet.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Request body (`application/json`, required): TenantSenderInput

Responses:

- `200` The address the tenant sends from now (TenantSender)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)
- `409` The request conflicts with the current state of the resource. (Error)

### Send a tenant's emails from Authalla's sign-in address

`DELETE /management/v1/tenants/{tenant_id}/sender-address` · Required scope: `sender-addresses:write`

The tenant's sign-in emails come from Authalla's sign-in address again.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Responses:

- `204` The tenant sends from Authalla's sign-in address
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### List social logins

`GET /management/v1/tenants/{tenant_id}/social-logins` · Required scope: `social-logins:read`

Lists the tenant's social logins, enabled and disabled.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Responses:

- `200` The tenant's social login providers (SocialLoginList)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Create a social login

`POST /management/v1/tenants/{tenant_id}/social-logins` · Required scope: `social-logins:write`

Configures a social login for the given tenant. A tenant has at most one
social login per provider type; a second one returns 409. The response
includes the redirect URI to whitelist on the upstream provider. A
social login created without a client secret starts disabled.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Request body (`application/json`, required): SocialLoginInput

Responses:

- `201` The created social login provider (SocialLogin)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)
- `409` The request conflicts with the current state of the resource. (Error)

### Get a social login

`GET /management/v1/tenants/{tenant_id}/social-logins/{social_login_id}` · Required scope: `social-logins:read`

Returns the social login provider. The client secret is never returned.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `social_login_id` | path | string | yes | Public ID of the social login provider the request acts on. |

Responses:

- `200` The social login provider (SocialLogin)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Update a social login

`PUT /management/v1/tenants/{tenant_id}/social-logins/{social_login_id}` · Required scope: `social-logins:write`

Updates the social login. Fields omitted from the body are left
unchanged. Setting `status` to `active` without a client secret returns
400.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `social_login_id` | path | string | yes | Public ID of the social login provider the request acts on. |

Request body (`application/json`, required): SocialLoginUpdate

Responses:

- `200` The updated social login provider (SocialLogin)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)
- `409` The request conflicts with the current state of the resource. (Error)

### Delete a social login

`DELETE /management/v1/tenants/{tenant_id}/social-logins/{social_login_id}` · Required scope: `social-logins:write`

Deletes the social login from this tenant. Other tenants are not affected.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `social_login_id` | path | string | yes | Public ID of the social login provider the request acts on. |

Responses:

- `204` Deleted
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Get the social login redirect URI

`GET /management/v1/tenants/{tenant_id}/social-logins/redirect-uri` · Required scope: `social-logins:read`

Returns the OAuth callback URL to whitelist on upstream providers for the
given tenant. Call this before creating a provider.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Responses:

- `200` The redirect URI for the tenant (SocialLoginRedirectUri)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### List resource servers

`GET /management/v1/tenants/{tenant_id}/resource-servers` · Required scope: `resource-servers:read`

Lists the tenant's resource servers with their scopes.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Responses:

- `200` The tenant's resource servers (ResourceServerList)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Create a resource server

`POST /management/v1/tenants/{tenant_id}/resource-servers` · Required scope: `resource-servers:write`

Registers an API or MCP server in the tenant, with its first scopes.
The identifier becomes the `aud` of access tokens requested for it with
the RFC 8707 `resource` parameter; a `resource` that isn't registered
is refused with `invalid_target`. The scope prefix is fixed after
creation, and each scope is issued as `<scope_prefix>:<name>`. Prefixes
Authalla uses for its own scopes are reserved. A clashing identifier or
prefix in the tenant returns 409. The response carries the protected
resource metadata (RFC 9728) the server should serve.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Request body (`application/json`, required): ResourceServerInput

Responses:

- `201` The created resource server (ResourceServer)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)
- `409` The request conflicts with the current state of the resource. (Error)

### Get a resource server

`GET /management/v1/tenants/{tenant_id}/resource-servers/{resource_server_id}` · Required scope: `resource-servers:read`

Returns the resource server with its scopes and protected resource metadata.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `resource_server_id` | path | string | yes | Public ID of the resource server the request acts on. |

Responses:

- `200` The resource server (ResourceServer)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Update a resource server

`PUT /management/v1/tenants/{tenant_id}/resource-servers/{resource_server_id}` · Required scope: `resource-servers:write`

Changes the identifier or name. Fields omitted from the body are left
unchanged; the scope prefix can't be changed. Changing the identifier
makes refreshes of tokens issued for the old one fail with
`invalid_target`.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `resource_server_id` | path | string | yes | Public ID of the resource server the request acts on. |

Request body (`application/json`, required): ResourceServerUpdate

Responses:

- `200` The updated resource server (ResourceServer)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)
- `409` The request conflicts with the current state of the resource. (Error)

### Delete a resource server

`DELETE /management/v1/tenants/{tenant_id}/resource-servers/{resource_server_id}` · Required scope: `resource-servers:write`

Deletes the resource server. Its scopes are deleted and taken from
every user who held them, and refreshes of tokens issued for it fail.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `resource_server_id` | path | string | yes | Public ID of the resource server the request acts on. |

Responses:

- `204` Deleted
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Add a scope to a resource server

`POST /management/v1/tenants/{tenant_id}/resource-servers/{resource_server_id}/scopes` · Required scope: `resource-servers:write`

Adds a scope, issued as `<scope_prefix>:<name>`. With
`grant_to_all_users` on, every current user of the tenant gets the
scope at once, and every user created later gets it too. A scope with
the same name returns 409.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `resource_server_id` | path | string | yes | Public ID of the resource server the request acts on. |

Request body (`application/json`, required): ResourceServerScopeInput

Responses:

- `201` The resource server with all its scopes (ResourceServer)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)
- `409` The request conflicts with the current state of the resource. (Error)

### Update a resource server scope

`PUT /management/v1/tenants/{tenant_id}/resource-servers/{resource_server_id}/scopes/{scope_name}` · Required scope: `resource-servers:write`

Changes a scope's description or its Grant to all users switch. Fields
omitted from the body are left unchanged; the name can't be changed.
Switching `grant_to_all_users` on grants the scope to every user of the
tenant, now and later. Switching it off takes the scope from every user
of the tenant, including users it was granted to one by one; the
scope's `user_count` says how many that is.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `resource_server_id` | path | string | yes | Public ID of the resource server the request acts on. |
| `scope_name` | path | string | yes | Name of the resource server scope, without the prefix (`read`, not `crm:read`). |

Request body (`application/json`, required): ResourceServerScopeUpdate

Responses:

- `200` The resource server with all its scopes (ResourceServer)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Delete a resource server scope

`DELETE /management/v1/tenants/{tenant_id}/resource-servers/{resource_server_id}/scopes/{scope_name}` · Required scope: `resource-servers:write`

Deletes the scope and takes it from every user who held it.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `resource_server_id` | path | string | yes | Public ID of the resource server the request acts on. |
| `scope_name` | path | string | yes | Name of the resource server scope, without the prefix (`read`, not `crm:read`). |

Responses:

- `200` The resource server with its remaining scopes (ResourceServer)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### List organizations

`GET /management/v1/tenants/{tenant_id}/organizations` · Required scope: `organizations:read`

Lists the tenant's organizations, your business customers, with their
email domains, their enterprise connection's status and how many
members each has.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Responses:

- `200` The tenant's organizations (OrganizationList)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Create an organization

`POST /management/v1/tenants/{tenant_id}/organizations` · Required scope: `organizations:write`

Adds one of your business customers. Its email domains route those
users' sign-ins to its enterprise connection once that is active, and
from then on they can sign in only through it. A domain can belong to
only one organization in a tenant, and an external ID to only one;
either clash returns 409. Domains are trusted as given: set only
domains you know the customer owns.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |

Request body (`application/json`, required): OrganizationInput

Responses:

- `201` The created organization (Organization)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)
- `409` The request conflicts with the current state of the resource. (Error)

### Get an organization

`GET /management/v1/tenants/{tenant_id}/organizations/{organization_id}` · Required scope: `organizations:read`

Returns the organization with its domains, its enterprise connection's
status, whether SCIM provisioning is on and when its identity provider
last called, how many of the users on its domains haven't signed in
through it yet, and when its setup link expires, if it has one.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `organization_id` | path | string | yes | Public ID of the organization the request acts on (`org_…`). |

Responses:

- `200` The organization (Organization)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Update an organization

`PUT /management/v1/tenants/{tenant_id}/organizations/{organization_id}` · Required scope: `organizations:write`

Changes the name, external ID or email domains. Fields omitted from
the body are left unchanged. `domains` replaces the whole list, and an
empty `external_id` clears it.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `organization_id` | path | string | yes | Public ID of the organization the request acts on (`org_…`). |

Request body (`application/json`, required): OrganizationUpdate

Responses:

- `200` The updated organization (Organization)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)
- `409` The request conflicts with the current state of the resource. (Error)

### Delete an organization

`DELETE /management/v1/tenants/{tenant_id}/organizations/{organization_id}` · Required scope: `organizations:write`

Deletes the organization with its enterprise connection, email
domains, memberships and setup links. Its users stay in the tenant,
and those who could sign in only through its connection can no longer
sign in.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `organization_id` | path | string | yes | Public ID of the organization the request acts on (`org_…`). |

Responses:

- `204` Deleted
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### List an organization's members

`GET /management/v1/tenants/{tenant_id}/organizations/{organization_id}/members` · Required scope: `organizations:read`

Lists the organization's members, the most recent single sign-on
first. A user becomes a member by signing in through its connection,
or when added. With `not_on_sso=true` it lists instead the active
users on its domains who haven't signed in through its connection
yet, with how each still signs in.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `organization_id` | path | string | yes | Public ID of the organization the request acts on (`org_…`). |
| `search` | query | string | no | Only users whose email or name contains this. |
| `not_on_sso` | query | boolean | no | List the users on its domains who aren't on its single sign-on yet, instead of its members. |
| `limit` | query | integer | no |  |
| `offset` | query | integer | no |  |

Responses:

- `200` A page of members (OrganizationMemberList)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Add a member

`POST /management/v1/tenants/{tenant_id}/organizations/{organization_id}/members` · Required scope: `organization-members:write`

Makes a user a member of the organization by email, even one on
another domain such as a contractor. When the tenant has no user with
that email, one is created, so you can add someone before their first
sign-in. With `send_email`, they are emailed that they can now sign
in with the organization's single sign-on, unless they were a member
already; that is refused with 400 while the organization's connection
isn't enabled.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `organization_id` | path | string | yes | Public ID of the organization the request acts on (`org_…`). |

Request body (`application/json`, required): OrganizationMemberInput

Responses:

- `201` The member (AddedOrganizationMember)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Remove a member

`DELETE /management/v1/tenants/{tenant_id}/organizations/{organization_id}/members/{user_id}` · Required scope: `organization-members:write`

Removes the user from the organization. The user stays in the tenant,
and signing in through the organization's connection makes them a
member again, so to offboard someone, remove their access in the
identity provider first. With `sign_out=true` the user is also signed
out everywhere: their sessions end and their tokens are revoked.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `organization_id` | path | string | yes | Public ID of the organization the request acts on (`org_…`). |
| `user_id` | path | string | yes | Public ID of the user the request acts on. |
| `sign_out` | query | boolean | no | Also sign the user out everywhere. |

Responses:

- `204` Removed
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Create a setup link

`POST /management/v1/tenants/{tenant_id}/organizations/{organization_id}/portal-links` · Required scope: `portal-links:write`

Creates a link that lets the organization's IT admin set up, test and
turn off its enterprise connection in the setup portal, on your
tenant's sign-in domain, without an Authalla account. Send it to them,
or redirect a signed-in admin of your customer to it. It lasts seven
days and can be opened more than once. The URL is returned only here,
and creating a link revokes the organization's previous one. Changes
made in the portal show in Team activity as the setup link's.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `organization_id` | path | string | yes | Public ID of the organization the request acts on (`org_…`). |

Responses:

- `201` The setup link (PortalLink)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Revoke the setup link

`DELETE /management/v1/tenants/{tenant_id}/organizations/{organization_id}/portal-links` · Required scope: `portal-links:write`

Revokes the organization's setup link, which also ends any portal
session opened with it.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `organization_id` | path | string | yes | Public ID of the organization the request acts on (`org_…`). |

Responses:

- `204` Revoked
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Get an organization's connection

`GET /management/v1/tenants/{tenant_id}/organizations/{organization_id}/connection` · Required scope: `organizations:read`

Returns the organization's enterprise connection: its status, its last
test sign-in, the identity provider details being edited, and the
values to give the identity provider. A client secret or certificate
is never returned, only whether one is set. Returns 404 when the
organization has no connection yet.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `organization_id` | path | string | yes | Public ID of the organization the request acts on (`org_…`). |

Responses:

- `200` The connection (OrganizationConnection)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Set up an organization's connection

`POST /management/v1/tenants/{tenant_id}/organizations/{organization_id}/connection` · Required scope: `organizations:write`

Creates the organization's enterprise connection as a draft, which
signs no one in until it passes a test sign-in or you turn it on. An
OIDC connection needs its discovery URL and client credentials; its
endpoints are read from the discovery document. A SAML connection can
take its identity provider details now or later, as a metadata URL or
as the sign-on URL, entity ID and certificate. An organization has at
most one connection: a second returns 409.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `organization_id` | path | string | yes | Public ID of the organization the request acts on (`org_…`). |

Request body (`application/json`, required): OrganizationConnectionInput

Responses:

- `201` The connection, as a draft (OrganizationConnection)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)
- `409` The request conflicts with the current state of the resource. (Error)

### Update an organization's connection

`PUT /management/v1/tenants/{tenant_id}/organizations/{organization_id}/connection` · Required scope: `organizations:write`

Changes the identity provider details, or turns the connection on or
off. Fields omitted from the body are left unchanged. On a draft the
details apply at once. On a connection that has been on they wait as
pending changes, and sign-ins keep using the previous details until a
test sign-in of the new ones passes. `status: active` turns a draft on
without a test sign-in, so check the setup first.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `organization_id` | path | string | yes | Public ID of the organization the request acts on (`org_…`). |

Request body (`application/json`, required): OrganizationConnectionUpdate

Responses:

- `200` The connection (OrganizationConnection)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Delete an organization's connection

`DELETE /management/v1/tenants/{tenant_id}/organizations/{organization_id}/connection` · Required scope: `organizations:write`

Deletes the organization's enterprise connection, whatever its
status. Users on the organization's domains who could sign in only
through it can't sign in until it has a connection again.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `organization_id` | path | string | yes | Public ID of the organization the request acts on (`org_…`). |

Responses:

- `204` Deleted
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Start a test sign-in

`POST /management/v1/tenants/{tenant_id}/organizations/{organization_id}/connection/test` · Required scope: `organizations:write`

Returns a link that runs a test sign-in through the connection, with
its pending changes if it has any. Open it in a browser and sign in
at the identity provider. The test signs no one in and creates no
user; the connection's `last_test` then shows what the identity
provider sent. A passing test turns a draft on, and makes pending
changes live. The link works once, for 15 minutes.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `organization_id` | path | string | yes | Public ID of the organization the request acts on (`org_…`). |

Responses:

- `201` The test sign-in link (ConnectionTestLink)
- `400` The request was malformed or contained invalid values. (Error)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Discard pending changes

`DELETE /management/v1/tenants/{tenant_id}/organizations/{organization_id}/connection/pending-changes` · Required scope: `organizations:write`

Drops the identity provider details waiting for a test sign-in. The live details stay.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `organization_id` | path | string | yes | Public ID of the organization the request acts on (`org_…`). |

Responses:

- `204` Discarded
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Get an organization's provisioning

`GET /management/v1/tenants/{tenant_id}/organizations/{organization_id}/provisioning` · Required scope: `organizations:read`

Returns the organization's SCIM provisioning: the SCIM base URL its
identity provider calls, when its SCIM token was made and when the
identity provider last called with it. The token itself is never
returned. Returns 404 when provisioning is off.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `organization_id` | path | string | yes | Public ID of the organization the request acts on (`org_…`). |

Responses:

- `200` The provisioning (OrganizationProvisioning)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Turn on an organization's provisioning

`POST /management/v1/tenants/{tenant_id}/organizations/{organization_id}/provisioning` · Required scope: `organizations:write`

Turns on SCIM provisioning, so the organization's identity provider
(Microsoft Entra ID, Okta or any SCIM 2.0 client) can create, update
and deactivate its members. Returns the SCIM token, only here: paste
it with `scim_base_url` into the identity provider. The token reaches
only the organization's members whose email is on its domains, and
its groups, and doesn't expire. Returns 409 when provisioning is
already on; make a new token instead.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `organization_id` | path | string | yes | Public ID of the organization the request acts on (`org_…`). |

Responses:

- `201` The provisioning, with its SCIM token (OrganizationProvisioningWithToken)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)
- `409` The request conflicts with the current state of the resource. (Error)

### Turn off an organization's provisioning

`DELETE /management/v1/tenants/{tenant_id}/organizations/{organization_id}/provisioning` · Required scope: `organizations:write`

Turns off SCIM provisioning and deletes its token, so the identity
provider's calls fail from now on. Provisioned members and the
organization's groups stay, and turning provisioning on again picks
up where it was.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `organization_id` | path | string | yes | Public ID of the organization the request acts on (`org_…`). |

Responses:

- `204` Turned off
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

### Make a new SCIM token

`POST /management/v1/tenants/{tenant_id}/organizations/{organization_id}/provisioning/token` · Required scope: `organizations:write`

Makes a new SCIM token for the organization's provisioning, returned
only here. The previous token stops working at once, so paste the new
one into the identity provider straight away; it retries calls that
fail in between. Returns 404 when provisioning is off.

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `tenant_id` | path | string | yes | Public ID of the tenant the request acts on. |
| `organization_id` | path | string | yes | Public ID of the organization the request acts on (`org_…`). |

Responses:

- `200` The provisioning, with its new SCIM token (OrganizationProvisioningWithToken)
- `401` Authentication failed (missing, malformed, revoked, or expired key). (Error)
- `403` The key's scope or tenant restriction does not permit this request. (Error)
- `404` The resource does not exist or is not visible to this key. (Error)

## Schemas

### Account

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Stable public identifier of the account. |
| `name` | string | yes |  |
| `status` | string | yes |  |
| `created_at` | string (date-time) | no |  |

### AllowedOrigins

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `allowed_origins` | string[] | yes | Browser origins allowed to call the tenant, each a scheme, host and optional port. |

### AllowedOriginInput

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `origin` | string | yes | An `http` or `https` origin, such as `https://pr-42.preview.acme.com` or `http://localhost:3000`. |

### Tenant

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Stable public identifier of the tenant. |
| `name` | string | yes |  |
| `product_name` | string | yes | The name the tenant's users see in sign-in emails, such as "Acme". Empty means they see the tenant's name. |
| `status` | string | yes |  |
| `allow_client_id_metadata_documents` | boolean | no | Allow apps that identify themselves with a Client ID Metadata Document (AI agents / MCP clients) to sign users in. Defaults to false. |
| `allow_registration` | boolean | no |  |
| `created_at` | string (date-time) | no |  |

### TenantInput

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | yes |  |
| `product_name` | string | no | The name the tenant's users see in sign-in emails, such as "Acme". Trimmed; no line breaks or control characters. An empty string clears it, so users see the tenant's name. Omit to keep the current one. |
| `allow_client_id_metadata_documents` | boolean | no | Allow apps that identify themselves with a Client ID Metadata Document (AI agents / MCP clients) to sign users in. Defaults to false. |
| `allow_registration` | boolean | no | Whether end users may sign up on this tenant. A new tenant allows it unless this is false. Omit on update to keep the current setting. |

### TenantList

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `tenants` | Tenant[] | yes |  |
| `total` | integer | yes |  |

### App

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Stable public identifier of the app. It is also the app's OAuth2 `client_id`. |
| `name` | string | yes |  |
| `tenant_id` | string | yes | Public ID of the tenant this app belongs to. |
| `application_type` | string | yes | Application type (spa, native, web, backend). |
| `client_type` | string | yes | The OAuth2 client type (public or confidential). |
| `status` | string | yes |  |
| `initiate_login_uri` | string | no |  |
| `require_consent` | boolean | no | Show the consent screen before issuing an authorization code. |
| `redirect_uris` | string[] | no |  |
| `allowed_logout_uris` | string[] | no |  |
| `scopes` | string[] | no |  |
| `created_at` | string (date-time) | no |  |
| `updated_at` | string (date-time) | no |  |

### AppWithSecret

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes |  |
| `name` | string | yes |  |
| `tenant_id` | string | yes |  |
| `application_type` | string | yes |  |
| `client_type` | string | yes |  |
| `status` | string | yes |  |
| `initiate_login_uri` | string | no |  |
| `require_consent` | boolean | no | Show the consent screen before issuing an authorization code. |
| `redirect_uris` | string[] | no |  |
| `allowed_logout_uris` | string[] | no |  |
| `scopes` | string[] | no |  |
| `client_secret` | string | no | The generated app secret, returned exactly once at creation for confidential apps. Absent for public apps. |
| `created_at` | string (date-time) | no |  |

### AppInput

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | yes |  |
| `application_type` | string | yes | Application type (spa, native, web, backend). |
| `initiate_login_uri` | string | no |  |
| `require_consent` | boolean | no | Show the consent screen before issuing an authorization code. |
| `redirect_uris` | string[] | no | Where the app may receive authorization codes. A redirect_uri must match one exactly, except that an http URI on localhost, 127.0.0.1 or [::1] matches on any port. A `*` in the host or path is refused with `bad_request`: add each preview deployment's URI on its own. |
| `allowed_logout_uris` | string[] | no | Where the app may send users after logout. Matched exactly, so a `*` in the host or path is refused with `bad_request`. |
| `scopes` | string[] | no | OAuth2 scopes. Defaults to openid, profile, email when omitted. |

### AppSecret

An app secret's metadata. The secret value itself is never part of this
schema; it is returned only by the call that creates it.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Public ID of the secret. |
| `label` | string | yes | Operator-chosen name, defaulting to secret-YYYY-MM-DD. |
| `prefix` | string | yes | The secret's first characters, enough to tell two secrets apart. |
| `created_at` | string (date-time) | yes |  |
| `last_used_at` | string (date-time) | no | When this secret last authenticated the app, if ever. |
| `expires_at` | string (date-time) | no | When the secret stops authenticating the app. Null means never. |

### AppSecretList

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `secrets` | AppSecret[] | yes |  |
| `max_secrets` | integer | yes | How many secrets an app may hold, counting expired ones. |

### AppSecretInput

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `label` | string | no | Names the secret in listings. Defaults to secret-YYYY-MM-DD. |
| `expire_existing_at` | string (date-time) | no | Retires every other secret of this app at this time, so a rotation with a deadline is a single call. |

### AppSecretExpiryInput

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `expires_at` | string (date-time) | yes | When the secret stops working. Now or in the past revokes it. |

### AppSecretWithValue

A newly issued secret. This is the only place its value appears.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes |  |
| `label` | string | yes |  |
| `prefix` | string | yes |  |
| `created_at` | string (date-time) | yes |  |
| `expires_at` | string (date-time) | no |  |
| `client_secret` | string | yes | The secret value. Shown once and never retrievable again. |

### AppList

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `apps` | App[] | yes |  |
| `total` | integer | yes |  |

### ThemeColorMode

`single` renders one set of colours on every device. `light_dark`
follows the visitor's system colour scheme and uses `dark` on
dark-scheme devices.

One of: `single`, `light_dark`

### ThemeCornerStyle

How round the buttons, fields and code boxes are: `square` (0),
`rounded` (8px) or `pill` (fully round). The card follows at a larger
radius. One setting for both light and dark.

One of: `square`, `rounded`, `pill`

### ThemeFont

The sign-in pages' font, one of a list bundled with them. `system`
uses the device's own font. One setting for both light and dark.

One of: `dm_sans`, `inter`, `figtree`, `manrope`, `nunito`, `space_grotesk`, `source_serif_4`, `system`

### ThemeDefaultLanguage

The language the sign-in pages and emails use when the app's
authorization request names no supported language in `ui_locales`.
It replaces the browser's `Accept-Language`. An empty string follows
the browser.

One of: ``, `en`, `fi`, `sv`

### ThemeColors

The dark-scheme colours and logo, used in `light_dark` mode.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `logo_url` | string | no | Logo shown on dark-scheme devices in `light_dark` mode. Falls back to the main logo when empty. An absolute http(s) URL is imported. |
| `brand_color` | string | no | Buttons, links, focus rings and the code step's active box. |
| `page_background_color` | string | no | Behind the card. |
| `card_background_color` | string | no | The card that holds the sign-in form. Text is near-black or near-white, whichever reads on it. |

### Theme

A tenant's login-page theme.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `color_mode` | ThemeColorMode | no |  |
| `corner_style` | ThemeCornerStyle | no |  |
| `font` | ThemeFont | no |  |
| `default_language` | ThemeDefaultLanguage | no |  |
| `logo_url` | string | no |  |
| `icon_url` | string | no | The browser tab's icon. Empty means none. |
| `brand_color` | string | no | Buttons, links, focus rings and the code step's active box. |
| `page_background_color` | string | no | Behind the card. |
| `card_background_color` | string | no | The card that holds the sign-in form. Text is near-black or near-white, whichever reads on it. |
| `dark` | ThemeColors | no |  |

### ThemeUpdate

Partial update of a tenant theme. Only the provided fields are changed.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `color_mode` | ThemeColorMode | no |  |
| `corner_style` | ThemeCornerStyle | no |  |
| `font` | ThemeFont | no |  |
| `default_language` | ThemeDefaultLanguage | no |  |
| `logo_url` | string | no | A path returned by a logo upload, or an absolute http(s) URL to import. |
| `icon_url` | string | no | The browser tab's icon, in the same forms as `logo_url`. An empty string removes it. |
| `brand_color` | string | no | Buttons, links, focus rings and the code step's active box. |
| `page_background_color` | string | no | Behind the card. |
| `card_background_color` | string | no | The card that holds the sign-in form. Text is near-black or near-white, whichever reads on it. |
| `dark` | ThemeColors | no |  |

### DNSRecord

A DNS record the operator must configure.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `type` | string | no |  |
| `name` | string | no |  |
| `value` | string | no |  |
| `status` | string | no |  |

### CustomDomain

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Stable public identifier of the custom domain. |
| `custom_domain` | string | yes |  |
| `tenant_id` | string | yes |  |
| `tenant_name` | string | no |  |
| `status` | string | yes | Resolved status (pending, active, error). |
| `is_primary` | boolean | no |  |
| `verification_records` | DNSRecord[] | no |  |
| `created_at` | string (date-time) | no |  |
| `updated_at` | string (date-time) | no |  |

### CustomDomainInput

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `custom_domain` | string | yes | The custom domain (e.g. auth.example.com). |

### CustomDomainList

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `custom_domains` | CustomDomain[] | yes |  |
| `total` | integer | yes |  |

### SenderAddress

An address of the account's that its tenants can send sign-in emails from, once verified.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Stable identifier of the sender address. |
| `email` | string | yes |  |
| `domain` | string | yes |  |
| `verification_status` | string (pending \\| verified) | yes | `verified` once the domain's DNS records are all published and verify has been called; until then `pending`. Every address on a domain shares its status and records. A tenant can send from an address only once it is verified. |
| `dns_records` | SenderAddressDNSRecord[] | yes | The records to publish at the domain's DNS, with whether the last check found each. The first, a TXT record at `_authalla`, proves this account's claim on the domain; the rest authenticate the mail Authalla sends from it. |
| `tenants` | TenantRef[] | yes | The tenants that send from the address. |
| `created_at` | string (date-time) | yes |  |

### SenderAddressDNSRecord

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `type` | string | yes | TXT or CNAME. |
| `name` | string | yes | The host, relative to the domain; `@` is the domain itself. |
| `value` | string | yes |  |
| `found` | boolean | yes | Whether the last check found the record in DNS. |

### TenantRef

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes |  |
| `name` | string | yes |  |

### SenderAddressInput

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `email` | string | yes | The address, such as hi@acme.com, on a domain the account owns. |

### SenderAddressList

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `sender_addresses` | SenderAddress[] | yes |  |

### TenantSender

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `email` | string | yes | The address the tenant's sign-in emails come from. |
| `sender_address_id` | string | no | The sender address the tenant sends from. Absent when it sends from Authalla's sign-in address. |

### TenantSenderInput

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `sender_address_id` | string | yes |  |

### SocialLogin

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Stable public identifier of the social login. |
| `provider_type` | string | yes | Provider type (google, github, microsoft, facebook, linkedin, x, discord). |
| `client_id` | string | yes |  |
| `status` | string | yes |  |
| `redirect_uri` | string | no | The OAuth callback URL to whitelist on the upstream provider. |
| `created_at` | string (date-time) | no |  |
| `updated_at` | string (date-time) | no |  |

### SocialLoginInput

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `provider_type` | string | yes | Provider type (google, github, microsoft, facebook, linkedin, x, discord). |
| `client_id` | string | yes |  |
| `client_secret` | string | no | OAuth2 client secret from the provider. The provider is not functional until a secret is set. |

### SocialLoginUpdate

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `client_id` | string | no |  |
| `client_secret` | string | no |  |
| `status` | string | no |  |

### SocialLoginList

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `social_logins` | SocialLogin[] | yes |  |
| `total` | integer | yes |  |

### SocialLoginRedirectUri

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `redirect_uri` | string | yes |  |

### ResourceServer

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Stable public identifier of the resource server. |
| `identifier` | string | yes | The `aud` of tokens issued for this resource server, and the `resource` value apps send. |
| `name` | string | yes | Shown to users on the consent screen. |
| `scope_prefix` | string | yes | Fixed after creation. Scopes are issued as `<scope_prefix>:<name>`. |
| `scopes` | ResourceServerScope[] | yes |  |
| `protected_resource_metadata` | ProtectedResourceMetadata | yes |  |
| `protected_resource_metadata_url` | string | yes | Where the resource server serves `protected_resource_metadata` (RFC 9728), such as `https://mcp.example.com/.well-known/oauth-protected-resource/mcp`. |
| `created_at` | string (date-time) | no |  |
| `updated_at` | string (date-time) | no |  |

### ProtectedResourceMetadata

The RFC 9728 protected resource metadata the resource server serves,
naming the tenant as its authorization server. `scopes_supported`
lists `openid`, which every authorization request needs, `email`, so
the user's access token carries their email address, and the
resource server's scopes.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `resource` | string | yes |  |
| `resource_name` | string | yes |  |
| `authorization_servers` | string[] | yes |  |
| `scopes_supported` | string[] | yes |  |
| `bearer_methods_supported` | string[] | yes |  |

### ResourceServerScope

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | yes | The scope's name within the resource server. |
| `scope` | string | yes | The full scope as it appears in requests, tokens and user scopes, such as `crm:read`. |
| `description` | string | yes | Shown to users on the consent screen. |
| `grant_to_all_users` | boolean | yes | Every current and future user of the tenant holds the scope. |
| `user_count` | integer (int64) | yes | How many users of the tenant hold the scope. |

### ResourceServerList

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `resource_servers` | ResourceServer[] | yes |  |
| `total` | integer | yes |  |

### ResourceServerInput

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `identifier` | string | yes | An https URL without a fragment (http only for localhost), such as `https://mcp.example.com/mcp`. Unique in the tenant. |
| `name` | string | yes | Shown to users on the consent screen. |
| `scope_prefix` | string | yes | Lowercase letters, digits and hyphens, starting with a letter. Unique in the tenant and fixed after creation. Prefixes Authalla uses for its own scopes are reserved. |
| `scopes` | ResourceServerScopeInput[] | no |  |

### ResourceServerUpdate

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `identifier` | string | no |  |
| `name` | string | no |  |

### ResourceServerScopeInput

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | yes | Lowercase letters, digits, '.', '_' and '-', starting with a letter. Fixed after creation. |
| `description` | string | yes | Shown to users on the consent screen. |
| `grant_to_all_users` | boolean | no | Grant the scope to every current and future user of the tenant. |

### ResourceServerScopeUpdate

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `description` | string | no |  |
| `grant_to_all_users` | boolean | no | On grants the scope to every user of the tenant, now and later; off takes it from every user. |

### Organization

One of the tenant's business customers, whose staff sign in through their own identity provider.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Stable public identifier of the organization. Tokens of a sign-in through its connection carry it as `org_id`, and `/oauth2/authorize` takes it as `organization`. |
| `name` | string | yes |  |
| `external_id` | string | no | Your own ID for this customer. Unique in the tenant. |
| `domains` | string[] | yes | Email domains whose users sign in through the organization's connection once it is active. |
| `connection` | OrganizationConnectionSummary | no |  |
| `provisioning` | OrganizationProvisioningSummary | no |  |
| `member_count` | integer (int64) | yes | How many members it has. |
| `not_on_sso_count` | integer (int64) | no | How many active users on its domains haven't signed in through its connection yet. Only when getting a single organization. |
| `portal_link_expires_at` | string (date-time) | no | When its working setup link expires. Absent when it has none. Only when getting a single organization. |
| `created_at` | string (date-time) | yes |  |
| `updated_at` | string (date-time) | yes |  |

### OrganizationConnectionSummary

The organization's enterprise connection. Absent until one is set up.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes |  |
| `type` | string (oidc \\| saml) | yes |  |
| `name` | string | yes |  |
| `provider_type` | string | yes | The identity provider, `azure_ad` (Microsoft Entra ID), `okta_saml`, `generic_saml`, `microsoft_entra`, `okta`, `google_workspace` or `generic_oidc`. |
| `status` | string (draft \\| active \\| inactive) | yes | A draft hasn't passed its test sign-in yet and signs no one in. Only an active connection routes its organization's domains and is required for them. |

### OrganizationProvisioningSummary

The organization's SCIM provisioning. Absent while it is off.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `token_created_at` | string (date-time) | yes | When its current SCIM token was made. |
| `last_request_at` | string (date-time) | yes | When the identity provider last called with the token, to the minute. Null until it first calls. |
| `last_failed_request_at` | string (date-time) | yes | When a write the identity provider sent with the token was last refused, to the minute. Null until one is. Each refused write is in the dashboard's Activity, with what the identity provider was told. |
| `created_at` | string (date-time) | yes | When provisioning was turned on. |

### OrganizationProvisioning

An organization's SCIM provisioning, through which its identity provider creates, updates and deactivates its members.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `scim_base_url` | string | yes | The SCIM base URL to give the identity provider (Entra ID's Tenant URL, Okta's SCIM connector base URL), on the tenant's sign-in domain. |
| `token_created_at` | string (date-time) | yes | When its current SCIM token was made. |
| `last_request_at` | string (date-time) | yes | When the identity provider last called with the token, to the minute. Null until it first calls. |
| `last_failed_request_at` | string (date-time) | yes | When a write the identity provider sent with the token was last refused, to the minute. Null until one is. Each refused write is in the dashboard's Activity, with what the identity provider was told. |
| `created_at` | string (date-time) | yes | When provisioning was turned on. |

### OrganizationProvisioningWithToken

An organization's SCIM provisioning with its SCIM token, which is returned only once.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `scim_base_url` | string | yes | The SCIM base URL to give the identity provider (Entra ID's Tenant URL, Okta's SCIM connector base URL), on the tenant's sign-in domain. |
| `scim_token` | string | yes | The bearer token the identity provider sends (Entra ID's Secret Token, Okta's API token for HTTP Header authentication). Starts with `scim_` and doesn't expire. Returned only here; store it in the identity provider at once. |
| `token_created_at` | string (date-time) | yes | When this SCIM token was made. |
| `last_request_at` | string (date-time) | yes | When the identity provider last called with this token, to the minute. Null for a new token. |
| `last_failed_request_at` | string (date-time) | yes | When a write the identity provider sent with the token was last refused, to the minute. Null until one is. Each refused write is in the dashboard's Activity, with what the identity provider was told. |
| `created_at` | string (date-time) | yes | When provisioning was turned on. |

### OrganizationList

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `organizations` | Organization[] | yes |  |
| `total` | integer | yes |  |

### OrganizationInput

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | yes |  |
| `external_id` | string | no | Your own ID for this customer, so you can find the organization without storing Authalla's. Unique in the tenant. |
| `domains` | string[] | no | Email domains, like `globex.com`. Each can belong to only one organization in the tenant. |

### OrganizationUpdate

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | no |  |
| `external_id` | string | no | An empty string clears it. |
| `domains` | string[] | no | Replaces the organization's domains. |

### OrganizationMember

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `user_id` | string | yes |  |
| `email` | string | yes |  |
| `name` | string | yes |  |
| `status` | string (active \\| inactive \\| suspended) | yes |  |
| `last_sso_sign_in_at` | string (date-time) | no | Their last sign-in through the organization's connection. Absent until they sign in that way. |
| `provisioned_at` | string (date-time) | no | When the organization's identity provider made them a member through SCIM provisioning. Absent for a member who signed in through its connection or was added. |
| `last_login` | string (date-time) | no | Their last sign-in by any method. |
| `last_sign_in_method` | SignInMethod | no |  |

### SignInMethod

How a user not on single sign-on yet last signed in. Only with `not_on_sso=true`, and absent when not recorded.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `kind` | string (passkey \\| magic_link \\| pin_code \\| connection) | yes |  |
| `connection` | string | no | For a connection, a social provider's type (`google`) or an enterprise connection's name. Absent when it has since been deleted. |

### OrganizationMemberList

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `members` | OrganizationMember[] | yes |  |
| `total` | integer (int64) | yes |  |

### OrganizationMemberInput

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `email` | string | yes |  |
| `name` | string | no | Name for a user created because the tenant has none with this email. Defaults to the email; their identity provider can set it when they sign in. |
| `send_email` | boolean | no | Email them that they can now sign in with the organization's single sign-on. Defaults to false. |

### AddedOrganizationMember

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `user_id` | string | yes |  |
| `email` | string | yes |  |
| `name` | string | yes |  |
| `status` | string (active \\| inactive \\| suspended) | yes |  |
| `last_sso_sign_in_at` | string (date-time) | no |  |
| `last_login` | string (date-time) | no |  |
| `user_created` | boolean | yes | Whether the tenant had no user with this email, so one was created. |
| `emailed` | boolean | yes | Whether the email went out. False when not asked for, when they were a member already, or when it couldn't be sent. |

### PortalLink

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `url` | string | yes | Returned only once. Send it to the organization's IT admin. |
| `expires_at` | string (date-time) | yes |  |

### OrganizationConnection

An organization's enterprise connection, with the identity provider details being edited. Exactly one of `oidc` and `saml` is set, by `type`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes |  |
| `type` | string (oidc \\| saml) | yes |  |
| `name` | string | yes |  |
| `provider_type` | string | yes | The identity provider, such as `azure_ad` (Microsoft Entra ID over SAML), `okta_saml`, `generic_saml`, `microsoft_entra`, `okta` or `generic_oidc`. |
| `status` | string (draft \\| active \\| inactive) | yes | A draft hasn't passed a test sign-in and signs no one in. Only an active connection routes its organization's domains, and is then required for them. |
| `pending_changes` | boolean | yes | Whether identity provider details are waiting for a test sign-in. The details shown are those; sign-ins use the previous ones until the test passes. |
| `last_test` | ConnectionTestResult | no |  |
| `oidc` | OrganizationOIDCSettings | no |  |
| `saml` | OrganizationSAMLSettings | no |  |

### OrganizationOIDCSettings

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `discovery_url` | string | yes |  |
| `client_id` | string | yes |  |
| `client_secret_set` | boolean | yes | Whether a client secret is stored. It is never returned. |
| `redirect_uri` | string | yes | The redirect URI to register at the identity provider. |

### OrganizationSAMLSettings

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `idp_sso_url` | string | yes | The identity provider's sign-on URL. Empty until set. |
| `idp_entity_id` | string | yes | The identity provider's entity ID (issuer). Empty until set. |
| `idp_certificate_set` | boolean | yes | Whether the identity provider's signing certificate is set. |
| `acs_url` | string | yes | The reply (ACS) URL to give the identity provider. |
| `sp_entity_id` | string | yes | The identifier (entity ID) to give the identity provider. |
| `metadata_url` | string | yes | Service provider metadata, for an identity provider that imports it (Entra ID does). |

### ConnectionTestResult

The connection's last test sign-in. Absent before its first.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `tested_at` | string (date-time) | yes |  |
| `passed` | boolean | yes |  |
| `error` | string | no | Why it failed, such as an email outside the organization's domains. |
| `mapped` | object | yes | The values the connection read from the identity provider's answer, such as email and name. |
| `claims` | object | no | Everything the identity provider sent. |

### OrganizationConnectionInput

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `type` | string (oidc \\| saml) | yes |  |
| `provider_type` | string (microsoft_entra \\| okta \\| generic_oidc \\| azure_ad \\| okta_saml \\| generic_saml) | yes | The identity provider. `microsoft_entra`, `okta` and `generic_oidc` are OIDC; `azure_ad` (Microsoft Entra ID), `okta_saml` and `generic_saml` are SAML. |
| `name` | string | yes |  |
| `discovery_url` | string | no | OIDC only, required. The identity provider's `/.well-known/openid-configuration` URL. |
| `client_id` | string | no | OIDC only, required. |
| `client_secret` | string (password) | no | OIDC only, required. Stored encrypted and never returned. |
| `metadata_url` | string | no | SAML only. Where the identity provider publishes its metadata (Entra ID's App Federation Metadata Url, Okta's Metadata URL); the sign-on URL, entity ID and certificate are read from it. |
| `idp_sso_url` | string | no | SAML only, instead of `metadata_url`. |
| `idp_entity_id` | string | no | SAML only, instead of `metadata_url`. |
| `idp_certificate` | string | no | SAML only, instead of `metadata_url`. The identity provider's signing certificate in PEM. |

### OrganizationConnectionUpdate

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `status` | string (active \\| inactive) | no | `active` turns the connection on, a draft without a test sign-in; `inactive` turns it off. |
| `discovery_url` | string | no | OIDC only. A new URL reads the endpoints again. |
| `client_id` | string | no | OIDC only. |
| `client_secret` | string (password) | no | OIDC only. |
| `metadata_url` | string | no | SAML only. Reads the sign-on URL, entity ID and certificate again. |
| `idp_sso_url` | string | no | SAML only. |
| `idp_entity_id` | string | no | SAML only. |
| `idp_certificate` | string | no | SAML only. The identity provider's signing certificate in PEM, such as a rotated one. |

### ConnectionTestLink

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `url` | string | yes | Open in a browser to run the test sign-in. Works once, for 15 minutes. |

### Error

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | string (bad_request \\| validation_error \\| invalid_theme \\| unauthorized \\| forbidden \\| not_found \\| conflict \\| internal_error) | yes | Machine-readable error code. See https://docs.authalla.com/docs/errors. |
| `message` | string | yes | Human-readable error message. |
| `documentation_url` | string (uri) | yes | Link to this error code on the Errors page. |
