{
  "components": {
    "parameters": {
      "AppId": {
        "description": "Public ID of the app the request acts on. It is also the app's OAuth2 `client_id`.",
        "in": "path",
        "name": "app_id",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "DomainId": {
        "description": "Public ID of the custom domain the request acts on.",
        "in": "path",
        "name": "domain_id",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "OrganizationId": {
        "description": "Public ID of the organization the request acts on (`org_…`).",
        "in": "path",
        "name": "organization_id",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "ResourceServerId": {
        "description": "Public ID of the resource server the request acts on.",
        "in": "path",
        "name": "resource_server_id",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "ScopeName": {
        "description": "Name of the resource server scope, without the prefix (`read`, not `crm:read`).",
        "in": "path",
        "name": "scope_name",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "SecretId": {
        "description": "Public ID of the app secret the request acts on.",
        "in": "path",
        "name": "secret_id",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "SenderAddressId": {
        "description": "ID of the sender address the request acts on.",
        "in": "path",
        "name": "address_id",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "SocialLoginId": {
        "description": "Public ID of the social login provider the request acts on.",
        "in": "path",
        "name": "social_login_id",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "TenantId": {
        "description": "Public ID of the tenant the request acts on.",
        "in": "path",
        "name": "tenant_id",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "UserId": {
        "description": "Public ID of the user the request acts on.",
        "in": "path",
        "name": "user_id",
        "required": true,
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "BadRequestError": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "The request was malformed or contained invalid values."
      },
      "ConflictError": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "The request conflicts with the current state of the resource."
      },
      "ForbiddenError": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "The key's scope or tenant restriction does not permit this request."
      },
      "NotFoundError": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "The resource does not exist or is not visible to this key."
      },
      "UnauthorizedError": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "Authentication failed (missing, malformed, revoked, or expired key)."
      }
    },
    "schemas": {
      "Account": {
        "additionalProperties": false,
        "properties": {
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "id": {
            "description": "Stable public identifier of the account.",
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "status"
        ],
        "type": "object"
      },
      "AddedOrganizationMember": {
        "additionalProperties": false,
        "properties": {
          "email": {
            "type": "string"
          },
          "emailed": {
            "description": "Whether the email went out. False when not asked for, when they were a member already, or when it couldn't be sent.",
            "type": "boolean"
          },
          "last_login": {
            "format": "date-time",
            "type": "string"
          },
          "last_sso_sign_in_at": {
            "format": "date-time",
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "enum": [
              "active",
              "inactive",
              "suspended"
            ],
            "type": "string"
          },
          "user_created": {
            "description": "Whether the tenant had no user with this email, so one was created.",
            "type": "boolean"
          },
          "user_id": {
            "type": "string"
          }
        },
        "required": [
          "user_id",
          "email",
          "name",
          "status",
          "user_created",
          "emailed"
        ],
        "type": "object"
      },
      "AllowedOriginInput": {
        "additionalProperties": false,
        "properties": {
          "origin": {
            "description": "An `http` or `https` origin, such as `https://pr-42.preview.acme.com` or `http://localhost:3000`.",
            "example": "https://pr-42.preview.acme.com",
            "maxLength": 2048,
            "minLength": 1,
            "type": "string"
          }
        },
        "required": [
          "origin"
        ],
        "type": "object"
      },
      "AllowedOrigins": {
        "additionalProperties": false,
        "properties": {
          "allowed_origins": {
            "description": "Browser origins allowed to call the tenant, each a scheme, host and optional port.",
            "example": [
              "https://app.acme.com"
            ],
            "items": {
              "maxLength": 2048,
              "type": "string"
            },
            "maxItems": 50,
            "type": "array"
          }
        },
        "required": [
          "allowed_origins"
        ],
        "type": "object"
      },
      "App": {
        "additionalProperties": false,
        "properties": {
          "allowed_logout_uris": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "application_type": {
            "description": "Application type (spa, native, web, backend).",
            "type": "string"
          },
          "client_type": {
            "description": "The OAuth2 client type (public or confidential).",
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "id": {
            "description": "Stable public identifier of the app. It is also the app's OAuth2 `client_id`.",
            "type": "string"
          },
          "initiate_login_uri": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "redirect_uris": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "require_consent": {
            "description": "Show the consent screen before issuing an authorization code.",
            "type": "boolean"
          },
          "scopes": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "status": {
            "type": "string"
          },
          "tenant_id": {
            "description": "Public ID of the tenant this app belongs to.",
            "type": "string"
          },
          "updated_at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "tenant_id",
          "application_type",
          "client_type",
          "status"
        ],
        "type": "object"
      },
      "AppInput": {
        "properties": {
          "allowed_logout_uris": {
            "description": "Where the app may send users after logout. Matched exactly, so a\n`*` in the host or path is refused with `bad_request`.\n",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "application_type": {
            "description": "Application type (spa, native, web, backend).",
            "type": "string"
          },
          "initiate_login_uri": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "redirect_uris": {
            "description": "Where the app may receive authorization codes. A redirect_uri must\nmatch one exactly, except that an http URI on localhost,\n127.0.0.1 or [::1] matches on any port. A `*` in the host or path\nis refused with `bad_request`: add each preview deployment's URI\non its own.\n",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "require_consent": {
            "description": "Show the consent screen before issuing an authorization code.",
            "type": "boolean"
          },
          "scopes": {
            "description": "OAuth2 scopes. Defaults to openid, profile, email when omitted.\nSCIM scopes (`scim:*`) need an app with the client_credentials\ngrant (a backend app); on any other app they are refused with\n`bad_request`.\n",
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "name",
          "application_type"
        ],
        "type": "object"
      },
      "AppList": {
        "additionalProperties": false,
        "properties": {
          "apps": {
            "items": {
              "$ref": "#/components/schemas/App"
            },
            "type": "array"
          },
          "total": {
            "type": "integer"
          }
        },
        "required": [
          "apps",
          "total"
        ],
        "type": "object"
      },
      "AppSecret": {
        "description": "An app secret's metadata. The secret value itself is never part of this\nschema; it is returned only by the call that creates it.\n",
        "properties": {
          "created_at": {
            "example": "2026-09-20T09:15:00Z",
            "format": "date-time",
            "type": "string"
          },
          "expires_at": {
            "description": "When the secret stops authenticating the app. Null means never.",
            "example": "2026-10-20T12:00:00Z",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "id": {
            "description": "Public ID of the secret.",
            "example": "V1StGXR8Z5jdHi6Bm",
            "type": "string"
          },
          "label": {
            "description": "Operator-chosen name, defaulting to secret-YYYY-MM-DD.",
            "example": "secret-2026-09-20",
            "type": "string"
          },
          "last_used_at": {
            "description": "When this secret last authenticated the app, if ever.",
            "example": "2026-09-21T11:02:44Z",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "prefix": {
            "description": "The secret's first characters, enough to tell two secrets apart.",
            "example": "ath_cs_9fQ2xKp",
            "type": "string"
          }
        },
        "required": [
          "id",
          "label",
          "prefix",
          "created_at"
        ],
        "type": "object"
      },
      "AppSecretExpiryInput": {
        "properties": {
          "expires_at": {
            "description": "When the secret stops working. Now or in the past revokes it.",
            "example": "2026-10-20T12:00:00Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "expires_at"
        ],
        "type": "object"
      },
      "AppSecretInput": {
        "properties": {
          "expire_existing_at": {
            "description": "Retires every other secret of this app at this time, so a rotation\nwith a deadline is a single call.\n",
            "example": "2026-10-20T12:00:00Z",
            "format": "date-time",
            "type": "string"
          },
          "label": {
            "description": "Names the secret in listings. Defaults to secret-YYYY-MM-DD.",
            "example": "rotation-2026-09",
            "maxLength": 100,
            "type": "string"
          }
        },
        "type": "object"
      },
      "AppSecretList": {
        "properties": {
          "max_secrets": {
            "description": "How many secrets an app may hold, counting expired ones.",
            "example": 3,
            "type": "integer"
          },
          "secrets": {
            "items": {
              "$ref": "#/components/schemas/AppSecret"
            },
            "type": "array"
          }
        },
        "required": [
          "secrets",
          "max_secrets"
        ],
        "type": "object"
      },
      "AppSecretWithValue": {
        "description": "A newly issued secret. This is the only place its value appears.",
        "properties": {
          "client_secret": {
            "description": "The secret value. Shown once and never retrievable again.",
            "example": "ath_cs_9fQ2xKpR3mV7tN1bY6wZ4hL8sD0gJ5cA2eU",
            "type": "string"
          },
          "created_at": {
            "example": "2026-09-20T09:15:00Z",
            "format": "date-time",
            "type": "string"
          },
          "expires_at": {
            "example": "2027-09-20T12:00:00Z",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "id": {
            "example": "V1StGXR8Z5jdHi6Bm",
            "type": "string"
          },
          "label": {
            "example": "rotation-2026-09",
            "type": "string"
          },
          "prefix": {
            "example": "ath_cs_9fQ2xKp",
            "type": "string"
          }
        },
        "required": [
          "id",
          "label",
          "prefix",
          "created_at",
          "client_secret"
        ],
        "type": "object"
      },
      "AppWithSecret": {
        "additionalProperties": false,
        "properties": {
          "allowed_logout_uris": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "application_type": {
            "type": "string"
          },
          "client_secret": {
            "description": "The generated app secret, returned exactly once at creation for\nconfidential apps. Absent for public apps.\n",
            "type": "string"
          },
          "client_type": {
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "initiate_login_uri": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "redirect_uris": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "require_consent": {
            "description": "Show the consent screen before issuing an authorization code.",
            "type": "boolean"
          },
          "scopes": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "status": {
            "type": "string"
          },
          "tenant_id": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "tenant_id",
          "application_type",
          "client_type",
          "status"
        ],
        "type": "object"
      },
      "ConnectionTestLink": {
        "additionalProperties": false,
        "properties": {
          "url": {
            "description": "Open in a browser to run the test sign-in. Works once, for 15 minutes.",
            "type": "string"
          }
        },
        "required": [
          "url"
        ],
        "type": "object"
      },
      "ConnectionTestResult": {
        "additionalProperties": false,
        "description": "The connection's last test sign-in. Absent before its first.",
        "properties": {
          "claims": {
            "additionalProperties": true,
            "description": "Everything the identity provider sent.",
            "type": "object"
          },
          "error": {
            "description": "Why it failed, such as an email outside the organization's domains.",
            "type": "string"
          },
          "mapped": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "The values the connection read from the identity provider's answer, such as email and name.",
            "type": "object"
          },
          "passed": {
            "type": "boolean"
          },
          "tested_at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "tested_at",
          "passed",
          "mapped"
        ],
        "type": "object"
      },
      "CustomDomain": {
        "additionalProperties": false,
        "properties": {
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "custom_domain": {
            "type": "string"
          },
          "id": {
            "description": "Stable public identifier of the custom domain.",
            "type": "string"
          },
          "is_primary": {
            "type": "boolean"
          },
          "status": {
            "description": "Resolved status (pending, active, error).",
            "type": "string"
          },
          "tenant_id": {
            "type": "string"
          },
          "tenant_name": {
            "type": "string"
          },
          "updated_at": {
            "format": "date-time",
            "type": "string"
          },
          "verification_records": {
            "items": {
              "$ref": "#/components/schemas/DNSRecord"
            },
            "type": "array"
          }
        },
        "required": [
          "id",
          "custom_domain",
          "tenant_id",
          "status"
        ],
        "type": "object"
      },
      "CustomDomainInput": {
        "properties": {
          "custom_domain": {
            "description": "The custom domain (e.g. auth.example.com).",
            "type": "string"
          }
        },
        "required": [
          "custom_domain"
        ],
        "type": "object"
      },
      "CustomDomainList": {
        "additionalProperties": false,
        "properties": {
          "custom_domains": {
            "items": {
              "$ref": "#/components/schemas/CustomDomain"
            },
            "type": "array"
          },
          "total": {
            "type": "integer"
          }
        },
        "required": [
          "custom_domains",
          "total"
        ],
        "type": "object"
      },
      "DNSRecord": {
        "additionalProperties": false,
        "description": "A DNS record the operator must configure.",
        "properties": {
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "value": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "Error": {
        "additionalProperties": false,
        "properties": {
          "documentation_url": {
            "description": "Link to this error code on the Errors page.",
            "example": "https://docs.authalla.com/docs/errors#not_found",
            "format": "uri",
            "type": "string"
          },
          "error": {
            "description": "Machine-readable error code. See https://docs.authalla.com/docs/errors.",
            "enum": [
              "bad_request",
              "validation_error",
              "invalid_theme",
              "unauthorized",
              "forbidden",
              "not_found",
              "conflict",
              "internal_error"
            ],
            "example": "not_found",
            "type": "string",
            "x-error-codes": {
              "bad_request": {
                "description": "The request couldn't be processed: the body isn't valid JSON, or a value breaks a rule the schema can't express.",
                "fix": "Read `message`, which names the problem, and correct the request.",
                "status": 400
              },
              "conflict": {
                "description": "The request clashes with existing state, for example a user with that email already exists or the tenant already has a custom domain.",
                "fix": "Fetch the existing resource and update it instead, or remove it first.",
                "status": 409
              },
              "forbidden": {
                "description": "The credential is valid but doesn't permit this request: it lacks the required scope, it isn't allowed to act on this tenant, the operation needs an account-global key, or the token isn't a client_credentials token.",
                "fix": "Read `message`. For a missing scope it names the scope; grant it to the app or key. Each operation's required scope is listed in the API reference.",
                "status": 403
              },
              "internal_error": {
                "description": "Something failed on Authalla's side.",
                "fix": "Retry with exponential backoff. If it persists, contact support with the time of the request.",
                "status": 500
              },
              "invalid_theme": {
                "description": "A theme value is invalid, for example a colour that isn't a hex code.",
                "fix": "Read `message`, correct the value, and send the whole theme again.",
                "status": 400
              },
              "not_found": {
                "description": "The resource doesn't exist, or it isn't visible to this credential.",
                "fix": "Check the ID and that it belongs to the tenant or account the credential is bound to.",
                "status": 404
              },
              "unauthorized": {
                "description": "The credential is missing, malformed, expired, or revoked.",
                "fix": "Send `Authorization: Bearer \u003ccredential\u003e`. For the Application API, request a new client_credentials token. For the Management API, check that the account API key hasn't been revoked.",
                "status": 401
              },
              "validation_error": {
                "description": "The request doesn't match the OpenAPI spec: a required field is missing, a value has the wrong type, or it's out of range.",
                "fix": "Compare the request with the operation's schema in the API reference. `message` names the failing parameter or field.",
                "status": 400
              }
            }
          },
          "message": {
            "description": "Human-readable error message.",
            "example": "tenant not found",
            "type": "string"
          }
        },
        "required": [
          "error",
          "message",
          "documentation_url"
        ],
        "type": "object"
      },
      "Organization": {
        "additionalProperties": false,
        "description": "One of the tenant's business customers, whose staff sign in through their own identity provider.",
        "properties": {
          "connection": {
            "$ref": "#/components/schemas/OrganizationConnectionSummary"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "domains": {
            "description": "Email domains whose users sign in through the organization's connection once it is active.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "external_id": {
            "description": "Your own ID for this customer. Unique in the tenant.",
            "type": "string"
          },
          "id": {
            "description": "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`.",
            "type": "string"
          },
          "member_count": {
            "description": "How many members it has.",
            "format": "int64",
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "not_on_sso_count": {
            "description": "How many active users on its domains haven't signed in through its connection yet. Only when getting a single organization.",
            "format": "int64",
            "type": "integer"
          },
          "portal_link_expires_at": {
            "description": "When its working setup link expires. Absent when it has none. Only when getting a single organization.",
            "format": "date-time",
            "type": "string"
          },
          "updated_at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "domains",
          "member_count",
          "created_at",
          "updated_at"
        ],
        "type": "object"
      },
      "OrganizationConnection": {
        "additionalProperties": false,
        "description": "An organization's enterprise connection, with the identity provider details being edited. Exactly one of `oidc` and `saml` is set, by `type`.",
        "properties": {
          "id": {
            "type": "string"
          },
          "last_test": {
            "$ref": "#/components/schemas/ConnectionTestResult"
          },
          "name": {
            "type": "string"
          },
          "oidc": {
            "$ref": "#/components/schemas/OrganizationOIDCSettings"
          },
          "pending_changes": {
            "description": "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.",
            "type": "boolean"
          },
          "provider_type": {
            "description": "The identity provider, such as `azure_ad` (Microsoft Entra ID over SAML), `okta_saml`, `generic_saml`, `microsoft_entra`, `okta` or `generic_oidc`.",
            "type": "string"
          },
          "saml": {
            "$ref": "#/components/schemas/OrganizationSAMLSettings"
          },
          "status": {
            "description": "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.",
            "enum": [
              "draft",
              "active",
              "inactive"
            ],
            "type": "string"
          },
          "type": {
            "enum": [
              "oidc",
              "saml"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "name",
          "provider_type",
          "status",
          "pending_changes"
        ],
        "type": "object"
      },
      "OrganizationConnectionInput": {
        "additionalProperties": false,
        "properties": {
          "client_id": {
            "description": "OIDC only, required.",
            "type": "string"
          },
          "client_secret": {
            "description": "OIDC only, required. Stored encrypted and never returned.",
            "format": "password",
            "type": "string",
            "writeOnly": true
          },
          "discovery_url": {
            "description": "OIDC only, required. The identity provider's `/.well-known/openid-configuration` URL.",
            "type": "string"
          },
          "idp_certificate": {
            "description": "SAML only, instead of `metadata_url`. The identity provider's signing certificate in PEM.",
            "type": "string"
          },
          "idp_entity_id": {
            "description": "SAML only, instead of `metadata_url`.",
            "type": "string"
          },
          "idp_sso_url": {
            "description": "SAML only, instead of `metadata_url`.",
            "type": "string"
          },
          "metadata_url": {
            "description": "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.",
            "type": "string"
          },
          "name": {
            "maxLength": 255,
            "minLength": 1,
            "type": "string"
          },
          "provider_type": {
            "description": "The identity provider. `microsoft_entra`, `okta` and `generic_oidc` are OIDC; `azure_ad` (Microsoft Entra ID), `okta_saml` and `generic_saml` are SAML.",
            "enum": [
              "microsoft_entra",
              "okta",
              "generic_oidc",
              "azure_ad",
              "okta_saml",
              "generic_saml"
            ],
            "type": "string"
          },
          "type": {
            "enum": [
              "oidc",
              "saml"
            ],
            "type": "string"
          }
        },
        "required": [
          "type",
          "provider_type",
          "name"
        ],
        "type": "object"
      },
      "OrganizationConnectionSummary": {
        "additionalProperties": false,
        "description": "The organization's enterprise connection. Absent until one is set up.",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "provider_type": {
            "description": "The identity provider, `azure_ad` (Microsoft Entra ID), `okta_saml`, `generic_saml`, `microsoft_entra`, `okta`, `google_workspace` or `generic_oidc`.",
            "type": "string"
          },
          "status": {
            "description": "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.",
            "enum": [
              "draft",
              "active",
              "inactive"
            ],
            "type": "string"
          },
          "type": {
            "enum": [
              "oidc",
              "saml"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "name",
          "provider_type",
          "status"
        ],
        "type": "object"
      },
      "OrganizationConnectionUpdate": {
        "additionalProperties": false,
        "properties": {
          "client_id": {
            "description": "OIDC only.",
            "type": "string"
          },
          "client_secret": {
            "description": "OIDC only.",
            "format": "password",
            "type": "string",
            "writeOnly": true
          },
          "discovery_url": {
            "description": "OIDC only. A new URL reads the endpoints again.",
            "type": "string"
          },
          "idp_certificate": {
            "description": "SAML only. The identity provider's signing certificate in PEM, such as a rotated one.",
            "type": "string"
          },
          "idp_entity_id": {
            "description": "SAML only.",
            "type": "string"
          },
          "idp_sso_url": {
            "description": "SAML only.",
            "type": "string"
          },
          "metadata_url": {
            "description": "SAML only. Reads the sign-on URL, entity ID and certificate again.",
            "type": "string"
          },
          "status": {
            "description": "`active` turns the connection on, a draft without a test sign-in; `inactive` turns it off.",
            "enum": [
              "active",
              "inactive"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "OrganizationInput": {
        "additionalProperties": false,
        "properties": {
          "domains": {
            "description": "Email domains, like `globex.com`. Each can belong to only one organization in the tenant.",
            "items": {
              "type": "string"
            },
            "maxItems": 50,
            "type": "array"
          },
          "external_id": {
            "description": "Your own ID for this customer, so you can find the organization without storing Authalla's. Unique in the tenant.",
            "maxLength": 255,
            "type": "string"
          },
          "name": {
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "type": "object"
      },
      "OrganizationList": {
        "additionalProperties": false,
        "properties": {
          "organizations": {
            "items": {
              "$ref": "#/components/schemas/Organization"
            },
            "type": "array"
          },
          "total": {
            "type": "integer"
          }
        },
        "required": [
          "organizations",
          "total"
        ],
        "type": "object"
      },
      "OrganizationMember": {
        "additionalProperties": false,
        "properties": {
          "email": {
            "type": "string"
          },
          "last_login": {
            "description": "Their last sign-in by any method.",
            "format": "date-time",
            "type": "string"
          },
          "last_sign_in_method": {
            "$ref": "#/components/schemas/SignInMethod"
          },
          "last_sso_sign_in_at": {
            "description": "Their last sign-in through the organization's connection. Absent until they sign in that way.",
            "format": "date-time",
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "enum": [
              "active",
              "inactive",
              "suspended"
            ],
            "type": "string"
          },
          "user_id": {
            "type": "string"
          }
        },
        "required": [
          "user_id",
          "email",
          "name",
          "status"
        ],
        "type": "object"
      },
      "OrganizationMemberInput": {
        "additionalProperties": false,
        "properties": {
          "email": {
            "maxLength": 320,
            "minLength": 3,
            "type": "string"
          },
          "name": {
            "description": "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.",
            "maxLength": 255,
            "type": "string"
          },
          "send_email": {
            "description": "Email them that they can now sign in with the organization's single sign-on. Defaults to false.",
            "type": "boolean"
          }
        },
        "required": [
          "email"
        ],
        "type": "object"
      },
      "OrganizationMemberList": {
        "additionalProperties": false,
        "properties": {
          "members": {
            "items": {
              "$ref": "#/components/schemas/OrganizationMember"
            },
            "type": "array"
          },
          "total": {
            "format": "int64",
            "type": "integer"
          }
        },
        "required": [
          "members",
          "total"
        ],
        "type": "object"
      },
      "OrganizationOIDCSettings": {
        "additionalProperties": false,
        "properties": {
          "client_id": {
            "type": "string"
          },
          "client_secret_set": {
            "description": "Whether a client secret is stored. It is never returned.",
            "type": "boolean"
          },
          "discovery_url": {
            "type": "string"
          },
          "redirect_uri": {
            "description": "The redirect URI to register at the identity provider.",
            "type": "string"
          }
        },
        "required": [
          "discovery_url",
          "client_id",
          "client_secret_set",
          "redirect_uri"
        ],
        "type": "object"
      },
      "OrganizationSAMLSettings": {
        "additionalProperties": false,
        "properties": {
          "acs_url": {
            "description": "The reply (ACS) URL to give the identity provider.",
            "type": "string"
          },
          "idp_certificate_set": {
            "description": "Whether the identity provider's signing certificate is set.",
            "type": "boolean"
          },
          "idp_entity_id": {
            "description": "The identity provider's entity ID (issuer). Empty until set.",
            "type": "string"
          },
          "idp_sso_url": {
            "description": "The identity provider's sign-on URL. Empty until set.",
            "type": "string"
          },
          "metadata_url": {
            "description": "Service provider metadata, for an identity provider that imports it (Entra ID does).",
            "type": "string"
          },
          "sp_entity_id": {
            "description": "The identifier (entity ID) to give the identity provider.",
            "type": "string"
          }
        },
        "required": [
          "idp_sso_url",
          "idp_entity_id",
          "idp_certificate_set",
          "acs_url",
          "sp_entity_id",
          "metadata_url"
        ],
        "type": "object"
      },
      "OrganizationUpdate": {
        "additionalProperties": false,
        "properties": {
          "domains": {
            "description": "Replaces the organization's domains.",
            "items": {
              "type": "string"
            },
            "maxItems": 50,
            "type": "array"
          },
          "external_id": {
            "description": "An empty string clears it.",
            "maxLength": 255,
            "type": "string"
          },
          "name": {
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          }
        },
        "type": "object"
      },
      "PortalLink": {
        "additionalProperties": false,
        "properties": {
          "expires_at": {
            "format": "date-time",
            "type": "string"
          },
          "url": {
            "description": "Returned only once. Send it to the organization's IT admin.",
            "type": "string"
          }
        },
        "required": [
          "url",
          "expires_at"
        ],
        "type": "object"
      },
      "ProtectedResourceMetadata": {
        "additionalProperties": false,
        "description": "The RFC 9728 protected resource metadata the resource server serves,\nnaming the tenant as its authorization server. `scopes_supported`\nlists `openid`, which every authorization request needs, `email`, so\nthe user's access token carries their email address, and the\nresource server's scopes.\n",
        "properties": {
          "authorization_servers": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "bearer_methods_supported": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "resource": {
            "type": "string"
          },
          "resource_name": {
            "type": "string"
          },
          "scopes_supported": {
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "resource",
          "resource_name",
          "authorization_servers",
          "scopes_supported",
          "bearer_methods_supported"
        ],
        "type": "object"
      },
      "ResourceServer": {
        "additionalProperties": false,
        "properties": {
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "id": {
            "description": "Stable public identifier of the resource server.",
            "type": "string"
          },
          "identifier": {
            "description": "The `aud` of tokens issued for this resource server, and the `resource` value apps send.",
            "type": "string"
          },
          "name": {
            "description": "Shown to users on the consent screen.",
            "type": "string"
          },
          "protected_resource_metadata": {
            "$ref": "#/components/schemas/ProtectedResourceMetadata"
          },
          "protected_resource_metadata_url": {
            "description": "Where the resource server serves `protected_resource_metadata` (RFC 9728), such as `https://mcp.example.com/.well-known/oauth-protected-resource/mcp`.",
            "type": "string"
          },
          "scope_prefix": {
            "description": "Fixed after creation. Scopes are issued as `\u003cscope_prefix\u003e:\u003cname\u003e`.",
            "type": "string"
          },
          "scopes": {
            "items": {
              "$ref": "#/components/schemas/ResourceServerScope"
            },
            "type": "array"
          },
          "updated_at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "id",
          "identifier",
          "name",
          "scope_prefix",
          "scopes",
          "protected_resource_metadata",
          "protected_resource_metadata_url"
        ],
        "type": "object"
      },
      "ResourceServerInput": {
        "properties": {
          "identifier": {
            "description": "An https URL without a fragment (http only for localhost), such as `https://mcp.example.com/mcp`. Unique in the tenant.",
            "maxLength": 2048,
            "minLength": 1,
            "type": "string"
          },
          "name": {
            "description": "Shown to users on the consent screen.",
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          },
          "scope_prefix": {
            "description": "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.",
            "maxLength": 32,
            "minLength": 1,
            "type": "string"
          },
          "scopes": {
            "items": {
              "$ref": "#/components/schemas/ResourceServerScopeInput"
            },
            "type": "array"
          }
        },
        "required": [
          "identifier",
          "name",
          "scope_prefix"
        ],
        "type": "object"
      },
      "ResourceServerList": {
        "additionalProperties": false,
        "properties": {
          "resource_servers": {
            "items": {
              "$ref": "#/components/schemas/ResourceServer"
            },
            "type": "array"
          },
          "total": {
            "type": "integer"
          }
        },
        "required": [
          "resource_servers",
          "total"
        ],
        "type": "object"
      },
      "ResourceServerScope": {
        "additionalProperties": false,
        "properties": {
          "description": {
            "description": "Shown to users on the consent screen.",
            "type": "string"
          },
          "grant_to_all_users": {
            "description": "Every current and future user of the tenant holds the scope.",
            "type": "boolean"
          },
          "name": {
            "description": "The scope's name within the resource server.",
            "type": "string"
          },
          "scope": {
            "description": "The full scope as it appears in requests, tokens and user scopes, such as `crm:read`.",
            "type": "string"
          },
          "user_count": {
            "description": "How many users of the tenant hold the scope.",
            "format": "int64",
            "type": "integer"
          }
        },
        "required": [
          "name",
          "scope",
          "description",
          "grant_to_all_users",
          "user_count"
        ],
        "type": "object"
      },
      "ResourceServerScopeInput": {
        "properties": {
          "description": {
            "description": "Shown to users on the consent screen.",
            "maxLength": 200,
            "minLength": 1,
            "type": "string"
          },
          "grant_to_all_users": {
            "default": false,
            "description": "Grant the scope to every current and future user of the tenant.",
            "type": "boolean"
          },
          "name": {
            "description": "Lowercase letters, digits, '.', '_' and '-', starting with a letter. Fixed after creation.",
            "maxLength": 64,
            "minLength": 1,
            "type": "string"
          }
        },
        "required": [
          "name",
          "description"
        ],
        "type": "object"
      },
      "ResourceServerScopeUpdate": {
        "properties": {
          "description": {
            "maxLength": 200,
            "minLength": 1,
            "type": "string"
          },
          "grant_to_all_users": {
            "description": "On grants the scope to every user of the tenant, now and later; off takes it from every user.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "ResourceServerUpdate": {
        "properties": {
          "identifier": {
            "maxLength": 2048,
            "minLength": 1,
            "type": "string"
          },
          "name": {
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          }
        },
        "type": "object"
      },
      "SenderAddress": {
        "additionalProperties": false,
        "description": "An address of the account's that its tenants can send sign-in emails from, once verified.",
        "properties": {
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "dns_records": {
            "description": "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.",
            "items": {
              "$ref": "#/components/schemas/SenderAddressDNSRecord"
            },
            "type": "array"
          },
          "domain": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "id": {
            "description": "Stable identifier of the sender address.",
            "type": "string"
          },
          "tenants": {
            "description": "The tenants that send from the address.",
            "items": {
              "$ref": "#/components/schemas/TenantRef"
            },
            "type": "array"
          },
          "verification_status": {
            "description": "`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.",
            "enum": [
              "pending",
              "verified"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "email",
          "domain",
          "verification_status",
          "dns_records",
          "tenants",
          "created_at"
        ],
        "type": "object"
      },
      "SenderAddressDNSRecord": {
        "additionalProperties": false,
        "properties": {
          "found": {
            "description": "Whether the last check found the record in DNS.",
            "type": "boolean"
          },
          "name": {
            "description": "The host, relative to the domain; `@` is the domain itself.",
            "type": "string"
          },
          "type": {
            "description": "TXT or CNAME.",
            "type": "string"
          },
          "value": {
            "type": "string"
          }
        },
        "required": [
          "type",
          "name",
          "value",
          "found"
        ],
        "type": "object"
      },
      "SenderAddressInput": {
        "properties": {
          "email": {
            "description": "The address, such as hi@acme.com, on a domain the account owns.",
            "type": "string"
          }
        },
        "required": [
          "email"
        ],
        "type": "object"
      },
      "SenderAddressList": {
        "additionalProperties": false,
        "properties": {
          "sender_addresses": {
            "items": {
              "$ref": "#/components/schemas/SenderAddress"
            },
            "type": "array"
          }
        },
        "required": [
          "sender_addresses"
        ],
        "type": "object"
      },
      "SignInMethod": {
        "additionalProperties": false,
        "description": "How a user not on single sign-on yet last signed in. Only with `not_on_sso=true`, and absent when not recorded.",
        "properties": {
          "connection": {
            "description": "For a connection, a social provider's type (`google`) or an enterprise connection's name. Absent when it has since been deleted.",
            "type": "string"
          },
          "kind": {
            "enum": [
              "passkey",
              "magic_link",
              "pin_code",
              "connection"
            ],
            "type": "string"
          }
        },
        "required": [
          "kind"
        ],
        "type": "object"
      },
      "SocialLogin": {
        "additionalProperties": false,
        "properties": {
          "client_id": {
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "id": {
            "description": "Stable public identifier of the social login.",
            "type": "string"
          },
          "provider_type": {
            "description": "Provider type (google, github, microsoft, facebook, linkedin, x, discord).",
            "type": "string"
          },
          "redirect_uri": {
            "description": "The OAuth callback URL to whitelist on the upstream provider.",
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "updated_at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "id",
          "provider_type",
          "client_id",
          "status"
        ],
        "type": "object"
      },
      "SocialLoginInput": {
        "properties": {
          "client_id": {
            "type": "string"
          },
          "client_secret": {
            "description": "OAuth2 client secret from the provider. The provider is not functional\nuntil a secret is set.\n",
            "type": "string"
          },
          "provider_type": {
            "description": "Provider type (google, github, microsoft, facebook, linkedin, x, discord).",
            "type": "string"
          }
        },
        "required": [
          "provider_type",
          "client_id"
        ],
        "type": "object"
      },
      "SocialLoginList": {
        "additionalProperties": false,
        "properties": {
          "social_logins": {
            "items": {
              "$ref": "#/components/schemas/SocialLogin"
            },
            "type": "array"
          },
          "total": {
            "type": "integer"
          }
        },
        "required": [
          "social_logins",
          "total"
        ],
        "type": "object"
      },
      "SocialLoginRedirectUri": {
        "additionalProperties": false,
        "properties": {
          "redirect_uri": {
            "type": "string"
          }
        },
        "required": [
          "redirect_uri"
        ],
        "type": "object"
      },
      "SocialLoginUpdate": {
        "properties": {
          "client_id": {
            "type": "string"
          },
          "client_secret": {
            "type": "string"
          },
          "status": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "Tenant": {
        "additionalProperties": false,
        "properties": {
          "allow_client_id_metadata_documents": {
            "description": "Allow apps that identify themselves with a Client ID Metadata Document (AI agents / MCP clients) to sign users in. Defaults to false.",
            "type": "boolean"
          },
          "allow_registration": {
            "type": "boolean"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "id": {
            "description": "Stable public identifier of the tenant.",
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "product_name": {
            "description": "The name the tenant's users see in sign-in emails, such as \"Acme\". Empty means they see the tenant's name.",
            "example": "Acme",
            "type": "string"
          },
          "status": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "product_name",
          "status"
        ],
        "type": "object"
      },
      "TenantInput": {
        "properties": {
          "allow_client_id_metadata_documents": {
            "description": "Allow apps that identify themselves with a Client ID Metadata Document (AI agents / MCP clients) to sign users in. Defaults to false.",
            "type": "boolean"
          },
          "allow_registration": {
            "description": "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.",
            "type": "boolean"
          },
          "name": {
            "type": "string"
          },
          "product_name": {
            "description": "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.",
            "example": "Acme",
            "maxLength": 255,
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "type": "object"
      },
      "TenantList": {
        "additionalProperties": false,
        "properties": {
          "tenants": {
            "items": {
              "$ref": "#/components/schemas/Tenant"
            },
            "type": "array"
          },
          "total": {
            "type": "integer"
          }
        },
        "required": [
          "tenants",
          "total"
        ],
        "type": "object"
      },
      "TenantRef": {
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name"
        ],
        "type": "object"
      },
      "TenantSender": {
        "additionalProperties": false,
        "properties": {
          "email": {
            "description": "The address the tenant's sign-in emails come from.",
            "type": "string"
          },
          "sender_address_id": {
            "description": "The sender address the tenant sends from. Absent when it sends from Authalla's sign-in address.",
            "type": "string"
          }
        },
        "required": [
          "email"
        ],
        "type": "object"
      },
      "TenantSenderInput": {
        "properties": {
          "sender_address_id": {
            "type": "string"
          }
        },
        "required": [
          "sender_address_id"
        ],
        "type": "object"
      },
      "Theme": {
        "additionalProperties": false,
        "description": "A tenant's login-page theme.",
        "properties": {
          "brand_color": {
            "description": "Buttons, links, focus rings and the code step's active box.",
            "pattern": "^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$",
            "type": "string"
          },
          "card_background_color": {
            "description": "The card that holds the sign-in form. Text is near-black or\nnear-white, whichever reads on it.\n",
            "pattern": "^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$",
            "type": "string"
          },
          "color_mode": {
            "$ref": "#/components/schemas/ThemeColorMode"
          },
          "corner_style": {
            "$ref": "#/components/schemas/ThemeCornerStyle"
          },
          "dark": {
            "$ref": "#/components/schemas/ThemeColors"
          },
          "default_language": {
            "$ref": "#/components/schemas/ThemeDefaultLanguage"
          },
          "font": {
            "$ref": "#/components/schemas/ThemeFont"
          },
          "icon_url": {
            "description": "The browser tab's icon. Empty means none.",
            "type": "string"
          },
          "logo_url": {
            "type": "string"
          },
          "page_background_color": {
            "description": "Behind the card.",
            "pattern": "^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ThemeColorMode": {
        "description": "`single` renders one set of colours on every device. `light_dark`\nfollows the visitor's system colour scheme and uses `dark` on\ndark-scheme devices.\n",
        "enum": [
          "single",
          "light_dark"
        ],
        "type": "string"
      },
      "ThemeColors": {
        "description": "The dark-scheme colours and logo, used in `light_dark` mode.",
        "properties": {
          "brand_color": {
            "description": "Buttons, links, focus rings and the code step's active box.",
            "pattern": "^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$",
            "type": "string"
          },
          "card_background_color": {
            "description": "The card that holds the sign-in form. Text is near-black or\nnear-white, whichever reads on it.\n",
            "pattern": "^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$",
            "type": "string"
          },
          "logo_url": {
            "description": "Logo shown on dark-scheme devices in `light_dark` mode. Falls back\nto the main logo when empty. An absolute http(s) URL is imported.\n",
            "type": "string"
          },
          "page_background_color": {
            "description": "Behind the card.",
            "pattern": "^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ThemeCornerStyle": {
        "description": "How round the buttons, fields and code boxes are: `square` (0),\n`rounded` (8px) or `pill` (fully round). The card follows at a larger\nradius. One setting for both light and dark.\n",
        "enum": [
          "square",
          "rounded",
          "pill"
        ],
        "type": "string"
      },
      "ThemeDefaultLanguage": {
        "description": "The language the sign-in pages and emails use when the app's\nauthorization request names no supported language in `ui_locales`.\nIt replaces the browser's `Accept-Language`. An empty string follows\nthe browser.\n",
        "enum": [
          "",
          "en",
          "fi",
          "sv"
        ],
        "type": "string",
        "x-enum-varnames": [
          "ThemeDefaultLanguageFollowBrowser",
          "ThemeDefaultLanguageEn",
          "ThemeDefaultLanguageFi",
          "ThemeDefaultLanguageSv"
        ]
      },
      "ThemeFont": {
        "description": "The sign-in pages' font, one of a list bundled with them. `system`\nuses the device's own font. One setting for both light and dark.\n",
        "enum": [
          "dm_sans",
          "inter",
          "figtree",
          "manrope",
          "nunito",
          "space_grotesk",
          "source_serif_4",
          "system"
        ],
        "type": "string"
      },
      "ThemeUpdate": {
        "description": "Partial update of a tenant theme. Only the provided fields are changed.\n",
        "properties": {
          "brand_color": {
            "description": "Buttons, links, focus rings and the code step's active box.",
            "pattern": "^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$",
            "type": "string"
          },
          "card_background_color": {
            "description": "The card that holds the sign-in form. Text is near-black or\nnear-white, whichever reads on it.\n",
            "pattern": "^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$",
            "type": "string"
          },
          "color_mode": {
            "$ref": "#/components/schemas/ThemeColorMode"
          },
          "corner_style": {
            "$ref": "#/components/schemas/ThemeCornerStyle"
          },
          "dark": {
            "$ref": "#/components/schemas/ThemeColors"
          },
          "default_language": {
            "$ref": "#/components/schemas/ThemeDefaultLanguage"
          },
          "font": {
            "$ref": "#/components/schemas/ThemeFont"
          },
          "icon_url": {
            "description": "The browser tab's icon, in the same forms as `logo_url`. An empty\nstring removes it.\n",
            "type": "string"
          },
          "logo_url": {
            "description": "A path returned by a logo upload, or an absolute http(s) URL to\nimport.\n",
            "type": "string"
          },
          "page_background_color": {
            "description": "Behind the card.",
            "pattern": "^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$",
            "type": "string"
          }
        },
        "type": "object"
      }
    },
    "securitySchemes": {
      "accountApiKey": {
        "description": "Account API key in the form `authalla_ak_\u003ckeyid\u003e_\u003csecret\u003e`. Each operation\nnames the scope it needs in `x-required-scope`.\n",
        "scheme": "bearer",
        "type": "http",
        "x-scopes": {
          "account:read": "Read the account the key belongs to",
          "apps:read": "Read apps",
          "apps:write": "Create, update, and delete apps",
          "domains:read": "Read custom domains",
          "domains:write": "Create, verify, and delete custom domains",
          "organization-members:write": "Add and remove an organization's members",
          "organizations:read": "Read organizations and their members",
          "organizations:write": "Create, update, and delete organizations and their email domains",
          "portal-links:write": "Create and revoke an organization's setup link",
          "resource-servers:read": "Read resource servers and their scopes",
          "resource-servers:write": "Create, update, and delete resource servers and their scopes",
          "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",
          "tenants:read": "Read tenants and their allowed origins",
          "tenants:write": "Create, update, and delete tenants, and add and remove their allowed origins",
          "theme:read": "Read the login page theme",
          "theme:write": "Update the login page theme"
        }
      }
    }
  },
  "info": {
    "contact": {
      "name": "Authalla Support",
      "url": "https://authalla.com"
    },
    "description": "The Authalla **Management API** (control plane) lets an operator provision\nand configure identity infrastructure across an entire account: tenants\nand their allowed origins, apps, custom domains, sender addresses,\nsocial logins, resource servers, organizations, and themes.\n\n**Called with:** a **secret credential**, an account API key. Call it from\nyour server, CI or Terraform.\n\n**Reaches:** every tenant in the account, or the tenants the key allows,\nso the key never goes in a browser or a mobile app.\n\n**Use another API when:**\n\n- your server creates, reads or changes the users of a tenant: use the\n  **Application API**, called with a different secret credential, a\n  tenant app's client credentials token\n  ([spec](https://api.authalla.com/api/v1/openapi.json)).\n- a signed-in user views or changes their own details, sessions or\n  connected apps: use the **User API**, called with that user's own\n  access token\n  ([spec](https://api.authalla.com/me/v1/openapi.json)).\n\nWhich API, and which credential, for which job:\nhttps://docs.authalla.com/docs/choosing-an-api\n\n## Account API keys\n\nThe Management API authenticates with an **account API key** — a static,\naccount-wide bearer token created from the Authalla dashboard:\n\n```\nAuthorization: Bearer authalla_ak_\u003ckeyid\u003e_\u003csecret\u003e\n```\n\nUnlike the tenant-bound Application API credential, an account key is scoped\nto an account and can act across all (or an allowlisted subset of) its\ntenants. Keys carry an explicit scope set (`resource:read` / `resource:write`)\nand are shown exactly once at creation. The scopes are listed on the\n`accountApiKey` security scheme, and each operation names the one it needs\nin `x-required-scope`.\n\nErrors share one shape with a stable `error` code, documented at\nhttps://docs.authalla.com/docs/errors.\n",
    "license": {
      "name": "Proprietary"
    },
    "title": "Authalla Management API",
    "version": "1.0.0"
  },
  "openapi": "3.0.3",
  "paths": {
    "/management/v1/account": {
      "get": {
        "description": "Returns details of the account the API key belongs to.",
        "operationId": "getAccount",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-03-02T09:15:00Z",
                  "id": "a8r2t5y9u3i6o1p4s7d0f",
                  "name": "Acme Inc.",
                  "status": "active"
                },
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            },
            "description": "Account details"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Get account details",
        "tags": [
          "Account"
        ],
        "x-required-scope": "account:read"
      }
    },
    "/management/v1/sender-addresses": {
      "get": {
        "description": "Lists the account's sender addresses, oldest first, with the tenants\nthat send from each. Account-wide operation — available only to\naccount-global keys.\n",
        "operationId": "listSenderAddresses",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "sender_addresses": [
                    {
                      "created_at": "2026-03-02T09:15:00Z",
                      "dns_records": [
                        {
                          "found": true,
                          "name": "_authalla",
                          "type": "TXT",
                          "value": "authalla-verification=3f9c2a7e5d1b4086a2c8e0f4b6d9a1c3"
                        },
                        {
                          "found": true,
                          "name": "@",
                          "type": "TXT",
                          "value": "brevo-code:6c1f0a9e2b7d4c38"
                        },
                        {
                          "found": true,
                          "name": "brevo1._domainkey",
                          "type": "CNAME",
                          "value": "b1.acme-com.dkim.brevo.com"
                        },
                        {
                          "found": true,
                          "name": "brevo2._domainkey",
                          "type": "CNAME",
                          "value": "b2.acme-com.dkim.brevo.com"
                        },
                        {
                          "found": true,
                          "name": "_dmarc",
                          "type": "TXT",
                          "value": "v=DMARC1; p=none; rua=mailto:rua@dmarc.brevo.com"
                        }
                      ],
                      "domain": "acme.com",
                      "email": "hi@acme.com",
                      "id": "42",
                      "tenants": [
                        {
                          "id": "k3v9q2m7x1b8n4c6d0f5h",
                          "name": "Acme Production"
                        }
                      ],
                      "verification_status": "verified"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/SenderAddressList"
                }
              }
            },
            "description": "The account's sender addresses"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "List sender addresses",
        "tags": [
          "Sender Addresses"
        ],
        "x-required-scope": "sender-addresses:read"
      },
      "post": {
        "description": "Adds an address on a domain the account owns. The response has the DNS\nrecords to publish at the domain before calling verify. An address on a\ndomain the account has verified already is verified at once. Fails with\n409 if the account has the address already or another account has\nverified its domain; another account's unverified claim on the domain\nis released. Account-wide operation — available only to account-global\nkeys.\n",
        "operationId": "addSenderAddress",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "email": "hi@acme.com"
              },
              "schema": {
                "$ref": "#/components/schemas/SenderAddressInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-03-02T09:15:00Z",
                  "dns_records": [
                    {
                      "found": false,
                      "name": "_authalla",
                      "type": "TXT",
                      "value": "authalla-verification=3f9c2a7e5d1b4086a2c8e0f4b6d9a1c3"
                    },
                    {
                      "found": false,
                      "name": "@",
                      "type": "TXT",
                      "value": "brevo-code:6c1f0a9e2b7d4c38"
                    },
                    {
                      "found": false,
                      "name": "brevo1._domainkey",
                      "type": "CNAME",
                      "value": "b1.acme-com.dkim.brevo.com"
                    },
                    {
                      "found": false,
                      "name": "brevo2._domainkey",
                      "type": "CNAME",
                      "value": "b2.acme-com.dkim.brevo.com"
                    },
                    {
                      "found": false,
                      "name": "_dmarc",
                      "type": "TXT",
                      "value": "v=DMARC1; p=none; rua=mailto:rua@dmarc.brevo.com"
                    }
                  ],
                  "domain": "acme.com",
                  "email": "hi@acme.com",
                  "id": "42",
                  "tenants": [],
                  "verification_status": "pending"
                },
                "schema": {
                  "$ref": "#/components/schemas/SenderAddress"
                }
              }
            },
            "description": "The sender address, with the DNS records to publish"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "409": {
            "$ref": "#/components/responses/ConflictError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Add a sender address",
        "tags": [
          "Sender Addresses"
        ],
        "x-required-scope": "sender-addresses:write"
      }
    },
    "/management/v1/sender-addresses/{address_id}": {
      "delete": {
        "description": "Deletes the address from the account and from the email provider. The\ntenants that sent from it send from Authalla's sign-in address again. Removing\nthe last address on a domain removes the domain too, so adding an\naddress on it again means publishing new DNS records. Account-wide\noperation — available only to account-global keys.\n",
        "operationId": "removeSenderAddress",
        "responses": {
          "204": {
            "description": "Removed"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Remove a sender address",
        "tags": [
          "Sender Addresses"
        ],
        "x-required-scope": "sender-addresses:write"
      },
      "get": {
        "description": "Returns the sender address with its DNS records as the last check found\nthem. Account-wide operation — available only to account-global keys.\n",
        "operationId": "getSenderAddress",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-03-02T09:15:00Z",
                  "dns_records": [
                    {
                      "found": true,
                      "name": "_authalla",
                      "type": "TXT",
                      "value": "authalla-verification=3f9c2a7e5d1b4086a2c8e0f4b6d9a1c3"
                    },
                    {
                      "found": false,
                      "name": "@",
                      "type": "TXT",
                      "value": "brevo-code:6c1f0a9e2b7d4c38"
                    }
                  ],
                  "domain": "acme.com",
                  "email": "hi@acme.com",
                  "id": "42",
                  "tenants": [],
                  "verification_status": "pending"
                },
                "schema": {
                  "$ref": "#/components/schemas/SenderAddress"
                }
              }
            },
            "description": "The sender address"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Get a sender address",
        "tags": [
          "Sender Addresses"
        ],
        "x-required-scope": "sender-addresses:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SenderAddressId"
        }
      ]
    },
    "/management/v1/sender-addresses/{address_id}/verify": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SenderAddressId"
        }
      ],
      "post": {
        "description": "Looks up each DNS record of the address's domain and returns what it\nfound. Once all are published and the email provider has authenticated\nthe domain, the domain is verified, with every address on it, and stays\nverified. Account-wide operation — available only to account-global\nkeys.\n",
        "operationId": "verifySenderAddress",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-03-02T09:15:00Z",
                  "dns_records": [
                    {
                      "found": true,
                      "name": "_authalla",
                      "type": "TXT",
                      "value": "authalla-verification=3f9c2a7e5d1b4086a2c8e0f4b6d9a1c3"
                    },
                    {
                      "found": true,
                      "name": "@",
                      "type": "TXT",
                      "value": "brevo-code:6c1f0a9e2b7d4c38"
                    }
                  ],
                  "domain": "acme.com",
                  "email": "hi@acme.com",
                  "id": "42",
                  "tenants": [],
                  "verification_status": "verified"
                },
                "schema": {
                  "$ref": "#/components/schemas/SenderAddress"
                }
              }
            },
            "description": "The sender address, with whether this check found each record"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Check a sender address's DNS records",
        "tags": [
          "Sender Addresses"
        ],
        "x-required-scope": "sender-addresses:write"
      }
    },
    "/management/v1/tenants": {
      "get": {
        "description": "Lists the tenants in the account. Account-collection operation —\navailable only to account-global keys.\n",
        "operationId": "listTenants",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "minimum": 0,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "tenants": [
                    {
                      "allow_client_id_metadata_documents": false,
                      "allow_registration": true,
                      "created_at": "2026-03-02T09:15:00Z",
                      "id": "k3v9q2m7x1b8n4c6d0f5h",
                      "name": "Acme Production",
                      "product_name": "Acme",
                      "status": "active"
                    }
                  ],
                  "total": 1
                },
                "schema": {
                  "$ref": "#/components/schemas/TenantList"
                }
              }
            },
            "description": "The account's tenants"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "List tenants",
        "tags": [
          "Tenants"
        ],
        "x-required-scope": "tenants:read"
      },
      "post": {
        "description": "Creates a tenant in the account. Account-collection operation —\navailable only to account-global keys.\n",
        "operationId": "createTenant",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "allow_registration": true,
                "name": "Acme Production",
                "product_name": "Acme"
              },
              "schema": {
                "$ref": "#/components/schemas/TenantInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "allow_client_id_metadata_documents": false,
                  "allow_registration": true,
                  "created_at": "2026-03-02T09:15:00Z",
                  "id": "k3v9q2m7x1b8n4c6d0f5h",
                  "name": "Acme Production",
                  "product_name": "Acme",
                  "status": "active"
                },
                "schema": {
                  "$ref": "#/components/schemas/Tenant"
                }
              }
            },
            "description": "The created tenant"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Create a tenant",
        "tags": [
          "Tenants"
        ],
        "x-required-scope": "tenants:write"
      }
    },
    "/management/v1/tenants/{tenant_id}": {
      "delete": {
        "description": "Deletes a tenant. Account-collection operation — available only to\naccount-global keys.\n",
        "operationId": "deleteTenant",
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Delete a tenant",
        "tags": [
          "Tenants"
        ],
        "x-required-scope": "tenants:write"
      },
      "get": {
        "description": "Returns the tenant.",
        "operationId": "getTenant",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "allow_client_id_metadata_documents": false,
                  "allow_registration": true,
                  "created_at": "2026-03-02T09:15:00Z",
                  "id": "k3v9q2m7x1b8n4c6d0f5h",
                  "name": "Acme Production",
                  "product_name": "Acme",
                  "status": "active"
                },
                "schema": {
                  "$ref": "#/components/schemas/Tenant"
                }
              }
            },
            "description": "The tenant"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Get a tenant",
        "tags": [
          "Tenants"
        ],
        "x-required-scope": "tenants:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        }
      ],
      "put": {
        "description": "Renames the tenant and changes its settings. `name` is required; settings left out of the body keep their current value.",
        "operationId": "updateTenant",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "allow_registration": false,
                "name": "Acme Production",
                "product_name": "Acme"
              },
              "schema": {
                "$ref": "#/components/schemas/TenantInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "allow_client_id_metadata_documents": false,
                  "allow_registration": false,
                  "created_at": "2026-03-02T09:15:00Z",
                  "id": "k3v9q2m7x1b8n4c6d0f5h",
                  "name": "Acme Production",
                  "product_name": "Acme",
                  "status": "active"
                },
                "schema": {
                  "$ref": "#/components/schemas/Tenant"
                }
              }
            },
            "description": "The updated tenant"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Update a tenant",
        "tags": [
          "Tenants"
        ],
        "x-required-scope": "tenants:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/allowed-origins": {
      "delete": {
        "description": "Stops a browser origin from calling the tenant, for example when a\npreview deployment is torn down. The origin is matched after the\nsame normalization as when it was added. An origin the tenant\ndoesn't allow returns 404. Removing the last one returns the tenant\nto allowing the origins of its apps' redirect URIs, as before it had\nany.\n",
        "operationId": "removeAllowedOrigin",
        "parameters": [
          {
            "description": "The origin to remove, such as `https://pr-42.preview.acme.com`. It is a query parameter so its `://` needs no escaping.",
            "example": "https://pr-42.preview.acme.com",
            "in": "query",
            "name": "origin",
            "required": true,
            "schema": {
              "maxLength": 2048,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "allowed_origins": [
                    "https://app.acme.com"
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/AllowedOrigins"
                }
              }
            },
            "description": "The tenant's remaining allowed origins"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Remove an allowed origin",
        "tags": [
          "Tenants"
        ],
        "x-required-scope": "tenants:write"
      },
      "get": {
        "description": "Returns the browser origins allowed to call the tenant (CORS): its\nOAuth2 endpoints, the Application API and the User API. While the\nlist is empty, a tenant that never had allowed origins, or whose\nlast one was removed through this API, allows the origins of its\napps' redirect URIs instead. A tenant whose last origin was removed\nin the dashboard allows no browser origin.\n",
        "operationId": "listAllowedOrigins",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "allowed_origins": [
                    "https://app.acme.com",
                    "https://pr-42.preview.acme.com"
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/AllowedOrigins"
                }
              }
            },
            "description": "The tenant's allowed origins"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "List a tenant's allowed origins",
        "tags": [
          "Tenants"
        ],
        "x-required-scope": "tenants:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        }
      ],
      "post": {
        "description": "Allows a browser origin to call the tenant, for example a preview\ndeployment of a single-page app. The origin is stored as scheme,\nhost and port: a path is dropped, and a default port (`:443`,\n`:80`) is left out; the first change stores the tenant's existing\norigins the same way. Adding an origin the tenant already allows\nchanges nothing and returns 200, so a deployment can add its origin\non every run; a new origin returns 201.\n\nOnce a tenant has an allowed origin, only its allowed origins can\ncall it from a browser, not the origins of its apps' redirect URIs.\nA tenant can have up to 50 allowed origins; adding one more returns\n409.\n",
        "operationId": "addAllowedOrigin",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "origin": "https://pr-42.preview.acme.com"
              },
              "schema": {
                "$ref": "#/components/schemas/AllowedOriginInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "allowed_origins": [
                    "https://app.acme.com",
                    "https://pr-42.preview.acme.com"
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/AllowedOrigins"
                }
              }
            },
            "description": "The tenant already allowed the origin; its allowed origins, unchanged"
          },
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "allowed_origins": [
                    "https://app.acme.com",
                    "https://pr-42.preview.acme.com"
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/AllowedOrigins"
                }
              }
            },
            "description": "The origin was added; the tenant's allowed origins"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "409": {
            "$ref": "#/components/responses/ConflictError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Add an allowed origin",
        "tags": [
          "Tenants"
        ],
        "x-required-scope": "tenants:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/apps": {
      "get": {
        "description": "Lists the apps in the given tenant.",
        "operationId": "listApps",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "apps": [
                    {
                      "allowed_logout_uris": [
                        "https://app.acme.com"
                      ],
                      "application_type": "web",
                      "client_type": "confidential",
                      "created_at": "2026-03-02T09:15:00Z",
                      "id": "qrqpsa69g7hdcae9pcfy0",
                      "initiate_login_uri": "https://app.acme.com/login",
                      "name": "Acme Web",
                      "redirect_uris": [
                        "https://app.acme.com/callback"
                      ],
                      "require_consent": false,
                      "scopes": [
                        "openid",
                        "profile",
                        "email"
                      ],
                      "status": "active",
                      "tenant_id": "k3v9q2m7x1b8n4c6d0f5h",
                      "updated_at": "2026-03-04T14:40:12Z"
                    }
                  ],
                  "total": 1
                },
                "schema": {
                  "$ref": "#/components/schemas/AppList"
                }
              }
            },
            "description": "The tenant's apps"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "List apps",
        "tags": [
          "Apps"
        ],
        "x-required-scope": "apps:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        }
      ],
      "post": {
        "description": "Creates an app in the given tenant. For confidential apps\nthe generated app secret is returned exactly once in the response.\n",
        "operationId": "createApp",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "allowed_logout_uris": [
                  "https://app.acme.com"
                ],
                "application_type": "web",
                "initiate_login_uri": "https://app.acme.com/login",
                "name": "Acme Web",
                "redirect_uris": [
                  "https://app.acme.com/callback"
                ],
                "require_consent": false,
                "scopes": [
                  "openid",
                  "profile",
                  "email"
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/AppInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "allowed_logout_uris": [
                    "https://app.acme.com"
                  ],
                  "application_type": "web",
                  "client_secret": "ath_cs_9fQ2xKpR3mV7tN1bY6wZ4hL8sD0gJ5cA2eU",
                  "client_type": "confidential",
                  "created_at": "2026-03-02T09:15:00Z",
                  "id": "qrqpsa69g7hdcae9pcfy0",
                  "initiate_login_uri": "https://app.acme.com/login",
                  "name": "Acme Web",
                  "redirect_uris": [
                    "https://app.acme.com/callback"
                  ],
                  "require_consent": false,
                  "scopes": [
                    "openid",
                    "profile",
                    "email"
                  ],
                  "status": "active",
                  "tenant_id": "k3v9q2m7x1b8n4c6d0f5h"
                },
                "schema": {
                  "$ref": "#/components/schemas/AppWithSecret"
                }
              }
            },
            "description": "The created app (with one-time secret for confidential apps)"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Create an app",
        "tags": [
          "Apps"
        ],
        "x-required-scope": "apps:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/apps/{app_id}": {
      "delete": {
        "description": "Deletes the app. It can no longer sign users in or request tokens.",
        "operationId": "deleteApp",
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Delete an app",
        "tags": [
          "Apps"
        ],
        "x-required-scope": "apps:write"
      },
      "get": {
        "description": "Returns the app. The app secret is never returned after creation.",
        "operationId": "getApp",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "allowed_logout_uris": [
                    "https://app.acme.com"
                  ],
                  "application_type": "web",
                  "client_type": "confidential",
                  "created_at": "2026-03-02T09:15:00Z",
                  "id": "qrqpsa69g7hdcae9pcfy0",
                  "initiate_login_uri": "https://app.acme.com/login",
                  "name": "Acme Web",
                  "redirect_uris": [
                    "https://app.acme.com/callback"
                  ],
                  "require_consent": false,
                  "scopes": [
                    "openid",
                    "profile",
                    "email"
                  ],
                  "status": "active",
                  "tenant_id": "k3v9q2m7x1b8n4c6d0f5h",
                  "updated_at": "2026-03-04T14:40:12Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/App"
                }
              }
            },
            "description": "The app"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Get an app",
        "tags": [
          "Apps"
        ],
        "x-required-scope": "apps:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        },
        {
          "$ref": "#/components/parameters/AppId"
        }
      ],
      "put": {
        "description": "Replaces the app's mutable fields. Redirect URIs and allowed logout\nURIs are replaced with exactly the sets provided (omitting them clears\nthem).\n",
        "operationId": "updateApp",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "allowed_logout_uris": [
                  "https://app.acme.com"
                ],
                "application_type": "web",
                "initiate_login_uri": "https://app.acme.com/login",
                "name": "Acme Web",
                "redirect_uris": [
                  "https://app.acme.com/callback",
                  "https://staging.acme.com/callback"
                ],
                "require_consent": false,
                "scopes": [
                  "openid",
                  "profile",
                  "email"
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/AppInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "allowed_logout_uris": [
                    "https://app.acme.com"
                  ],
                  "application_type": "web",
                  "client_type": "confidential",
                  "created_at": "2026-03-02T09:15:00Z",
                  "id": "qrqpsa69g7hdcae9pcfy0",
                  "initiate_login_uri": "https://app.acme.com/login",
                  "name": "Acme Web",
                  "redirect_uris": [
                    "https://app.acme.com/callback",
                    "https://staging.acme.com/callback"
                  ],
                  "require_consent": false,
                  "scopes": [
                    "openid",
                    "profile",
                    "email"
                  ],
                  "status": "active",
                  "tenant_id": "k3v9q2m7x1b8n4c6d0f5h",
                  "updated_at": "2026-03-04T14:40:12Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/App"
                }
              }
            },
            "description": "The updated app"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Update an app (full replace)",
        "tags": [
          "Apps"
        ],
        "x-required-scope": "apps:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/apps/{app_id}/secrets": {
      "get": {
        "description": "Returns metadata for every secret the app holds, newest first. Secret\nvalues are never returned: a secret is shown exactly once, in the\nresponse to the call that created it. Public apps return an empty\nlist.\n",
        "operationId": "listAppSecrets",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "max_secrets": 3,
                  "secrets": [
                    {
                      "created_at": "2026-09-20T09:15:00Z",
                      "expires_at": null,
                      "id": "V1StGXR8Z5jdHi6Bm",
                      "label": "secret-2026-09-20",
                      "last_used_at": "2026-09-21T11:02:44Z",
                      "prefix": "ath_cs_9fQ2xKp"
                    },
                    {
                      "created_at": "2026-03-02T09:15:00Z",
                      "expires_at": "2026-10-20T12:00:00Z",
                      "id": "7pQ2mNx4Lk9vTc1Rb",
                      "label": "initial",
                      "last_used_at": "2026-09-20T08:59:12Z",
                      "prefix": "ath_cs_3bW8hYt"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/AppSecretList"
                }
              }
            },
            "description": "The app's secrets"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "List an app's secrets",
        "tags": [
          "Apps"
        ],
        "x-required-scope": "apps:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        },
        {
          "$ref": "#/components/parameters/AppId"
        }
      ],
      "post": {
        "description": "Issues a new secret and returns its value exactly once. An app may hold\nat most three secrets, counting expired ones; over that the call returns\n409 and an expired secret has to be deleted first.\n\nSet `expire_existing_at` to retire the outgoing secret in the same call:\nboth secrets authenticate the app until that time, which is what makes\na rotation possible without an outage.\n",
        "operationId": "addAppSecret",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "expire_existing_at": "2026-10-20T12:00:00Z",
                "label": "rotation-2026-09"
              },
              "schema": {
                "$ref": "#/components/schemas/AppSecretInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "client_secret": "ath_cs_9fQ2xKpR3mV7tN1bY6wZ4hL8sD0gJ5cA2eU",
                  "created_at": "2026-09-20T09:15:00Z",
                  "expires_at": null,
                  "id": "V1StGXR8Z5jdHi6Bm",
                  "label": "rotation-2026-09",
                  "prefix": "ath_cs_9fQ2xKp"
                },
                "schema": {
                  "$ref": "#/components/schemas/AppSecretWithValue"
                }
              }
            },
            "description": "The new secret, including its value"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "409": {
            "$ref": "#/components/responses/ConflictError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Add an app secret",
        "tags": [
          "Apps"
        ],
        "x-required-scope": "apps:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/apps/{app_id}/secrets/{secret_id}": {
      "delete": {
        "description": "Frees the secret's slot. Only an already-expired secret can be deleted;\nexpiring it first is what makes the deletion safe.\n",
        "operationId": "deleteAppSecret",
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Delete an expired app secret",
        "tags": [
          "Apps"
        ],
        "x-required-scope": "apps:write"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        },
        {
          "$ref": "#/components/parameters/AppId"
        },
        {
          "$ref": "#/components/parameters/SecretId"
        }
      ],
      "patch": {
        "description": "Sets the secret's expiry. A time in the past or now revokes it\nimmediately; there is no separate revoke operation.\n",
        "operationId": "expireAppSecret",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "expires_at": "2026-10-20T12:00:00Z"
              },
              "schema": {
                "$ref": "#/components/schemas/AppSecretExpiryInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-03-02T09:15:00Z",
                  "expires_at": "2026-10-20T12:00:00Z",
                  "id": "7pQ2mNx4Lk9vTc1Rb",
                  "label": "initial",
                  "last_used_at": "2026-09-20T08:59:12Z",
                  "prefix": "ath_cs_3bW8hYt"
                },
                "schema": {
                  "$ref": "#/components/schemas/AppSecret"
                }
              }
            },
            "description": "The updated secret"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Set when an app secret expires",
        "tags": [
          "Apps"
        ],
        "x-required-scope": "apps:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/domains": {
      "get": {
        "description": "Lists the custom login domains in the given tenant.",
        "operationId": "listCustomDomains",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "custom_domains": [
                    {
                      "created_at": "2026-03-02T09:15:00Z",
                      "custom_domain": "auth.acme.com",
                      "id": "d4m9n2b7v5c1x8z3l6k0j",
                      "is_primary": true,
                      "status": "active",
                      "tenant_id": "k3v9q2m7x1b8n4c6d0f5h",
                      "tenant_name": "Acme Production",
                      "updated_at": "2026-03-04T14:40:12Z",
                      "verification_records": [
                        {
                          "name": "auth.acme.com",
                          "status": "verified",
                          "type": "CNAME",
                          "value": "custom.authalla.com"
                        }
                      ]
                    }
                  ],
                  "total": 1
                },
                "schema": {
                  "$ref": "#/components/schemas/CustomDomainList"
                }
              }
            },
            "description": "The tenant's custom domains"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "List custom domains",
        "tags": [
          "Custom Domains"
        ],
        "x-required-scope": "domains:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        }
      ],
      "post": {
        "description": "Adds a custom login domain to the given tenant. The response includes the\nDNS records the operator must configure to verify and activate it.\n",
        "operationId": "createCustomDomain",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "custom_domain": "auth.acme.com"
              },
              "schema": {
                "$ref": "#/components/schemas/CustomDomainInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-03-02T09:15:00Z",
                  "custom_domain": "auth.acme.com",
                  "id": "d4m9n2b7v5c1x8z3l6k0j",
                  "is_primary": true,
                  "status": "pending",
                  "tenant_id": "k3v9q2m7x1b8n4c6d0f5h",
                  "tenant_name": "Acme Production",
                  "updated_at": "2026-03-02T09:15:00Z",
                  "verification_records": [
                    {
                      "name": "_authalla.auth.acme.com",
                      "type": "TXT",
                      "value": "authalla-verification=3f9a1c7e5b2d4a8f9e0c6b1d7a3e5f2c"
                    },
                    {
                      "name": "auth.acme.com",
                      "status": "pending",
                      "type": "CNAME",
                      "value": "custom.authalla.com"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/CustomDomain"
                }
              }
            },
            "description": "The created custom domain (with DNS records to configure)"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "409": {
            "$ref": "#/components/responses/ConflictError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Create a custom domain",
        "tags": [
          "Custom Domains"
        ],
        "x-required-scope": "domains:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/domains/{domain_id}": {
      "delete": {
        "description": "Removes the custom domain from the tenant.",
        "operationId": "deleteCustomDomain",
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Delete a custom domain",
        "tags": [
          "Custom Domains"
        ],
        "x-required-scope": "domains:write"
      },
      "get": {
        "description": "Returns the custom domain, with its status and the DNS records it needs.",
        "operationId": "getCustomDomain",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-03-02T09:15:00Z",
                  "custom_domain": "auth.acme.com",
                  "id": "d4m9n2b7v5c1x8z3l6k0j",
                  "is_primary": true,
                  "status": "active",
                  "tenant_id": "k3v9q2m7x1b8n4c6d0f5h",
                  "tenant_name": "Acme Production",
                  "updated_at": "2026-03-04T14:40:12Z",
                  "verification_records": [
                    {
                      "name": "auth.acme.com",
                      "status": "verified",
                      "type": "CNAME",
                      "value": "custom.authalla.com"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/CustomDomain"
                }
              }
            },
            "description": "The custom domain"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Get a custom domain",
        "tags": [
          "Custom Domains"
        ],
        "x-required-scope": "domains:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        },
        {
          "$ref": "#/components/parameters/DomainId"
        }
      ]
    },
    "/management/v1/tenants/{tenant_id}/domains/{domain_id}/verify": {
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        },
        {
          "$ref": "#/components/parameters/DomainId"
        }
      ],
      "post": {
        "description": "Triggers re-verification of the custom domain's DNS configuration and\nreturns its freshly resolved status and DNS records.\n",
        "operationId": "verifyCustomDomain",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-03-02T09:15:00Z",
                  "custom_domain": "auth.acme.com",
                  "id": "d4m9n2b7v5c1x8z3l6k0j",
                  "is_primary": true,
                  "status": "active",
                  "tenant_id": "k3v9q2m7x1b8n4c6d0f5h",
                  "tenant_name": "Acme Production",
                  "updated_at": "2026-03-04T14:40:12Z",
                  "verification_records": [
                    {
                      "name": "auth.acme.com",
                      "status": "verified",
                      "type": "CNAME",
                      "value": "custom.authalla.com"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/CustomDomain"
                }
              }
            },
            "description": "The custom domain with refreshed status"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Verify a custom domain",
        "tags": [
          "Custom Domains"
        ],
        "x-required-scope": "domains:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/organizations": {
      "get": {
        "description": "Lists the tenant's organizations, your business customers, with their\nemail domains, their enterprise connection's status and how many\nmembers each has.\n",
        "operationId": "listOrganizations",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "organizations": [
                    {
                      "connection": {
                        "id": "c2m7x1b8n4c6d0f5h3k9q",
                        "name": "Globex Entra ID",
                        "provider_type": "azure_ad",
                        "status": "active",
                        "type": "saml"
                      },
                      "created_at": "2026-10-07T09:15:00Z",
                      "domains": [
                        "globex.com"
                      ],
                      "external_id": "cus_4821",
                      "id": "org_8h2kd93hf0s7a1b2c3d4e",
                      "member_count": 214,
                      "name": "Globex",
                      "updated_at": "2026-10-07T09:15:00Z"
                    }
                  ],
                  "total": 1
                },
                "schema": {
                  "$ref": "#/components/schemas/OrganizationList"
                }
              }
            },
            "description": "The tenant's organizations"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "List organizations",
        "tags": [
          "Organizations"
        ],
        "x-required-scope": "organizations:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        }
      ],
      "post": {
        "description": "Adds one of your business customers. Its email domains route those\nusers' sign-ins to its enterprise connection once that is active, and\nfrom then on they can sign in only through it. A domain can belong to\nonly one organization in a tenant, and an external ID to only one;\neither clash returns 409. Domains are trusted as given: set only\ndomains you know the customer owns.\n",
        "operationId": "createOrganization",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "domains": [
                  "globex.com"
                ],
                "external_id": "cus_4821",
                "name": "Globex"
              },
              "schema": {
                "$ref": "#/components/schemas/OrganizationInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-10-07T09:15:00Z",
                  "domains": [
                    "globex.com"
                  ],
                  "external_id": "cus_4821",
                  "id": "org_8h2kd93hf0s7a1b2c3d4e",
                  "member_count": 0,
                  "name": "Globex",
                  "updated_at": "2026-10-07T09:15:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Organization"
                }
              }
            },
            "description": "The created organization"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "409": {
            "$ref": "#/components/responses/ConflictError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Create an organization",
        "tags": [
          "Organizations"
        ],
        "x-required-scope": "organizations:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/organizations/{organization_id}": {
      "delete": {
        "description": "Deletes the organization with its enterprise connection, email\ndomains, memberships and setup links. Its users stay in the tenant,\nand those who could sign in only through its connection can no longer\nsign in.\n",
        "operationId": "deleteOrganization",
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Delete an organization",
        "tags": [
          "Organizations"
        ],
        "x-required-scope": "organizations:write"
      },
      "get": {
        "description": "Returns the organization with its domains, its enterprise connection's\nstatus, how many of the users on its domains haven't signed in through\nit yet, and when its setup link expires, if it has one.\n",
        "operationId": "getOrganization",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "connection": {
                    "id": "c2m7x1b8n4c6d0f5h3k9q",
                    "name": "Globex Entra ID",
                    "provider_type": "azure_ad",
                    "status": "draft",
                    "type": "saml"
                  },
                  "created_at": "2026-10-07T09:15:00Z",
                  "domains": [
                    "globex.com"
                  ],
                  "external_id": "cus_4821",
                  "id": "org_8h2kd93hf0s7a1b2c3d4e",
                  "member_count": 0,
                  "name": "Globex",
                  "not_on_sso_count": 37,
                  "portal_link_expires_at": "2026-10-14T09:15:00Z",
                  "updated_at": "2026-10-07T09:15:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Organization"
                }
              }
            },
            "description": "The organization"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Get an organization",
        "tags": [
          "Organizations"
        ],
        "x-required-scope": "organizations:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        },
        {
          "$ref": "#/components/parameters/OrganizationId"
        }
      ],
      "put": {
        "description": "Changes the name, external ID or email domains. Fields omitted from\nthe body are left unchanged. `domains` replaces the whole list, and an\nempty `external_id` clears it.\n",
        "operationId": "updateOrganization",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "domains": [
                  "globex.com",
                  "globex.de"
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/OrganizationUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-10-07T09:15:00Z",
                  "domains": [
                    "globex.com",
                    "globex.de"
                  ],
                  "external_id": "cus_4821",
                  "id": "org_8h2kd93hf0s7a1b2c3d4e",
                  "member_count": 214,
                  "name": "Globex",
                  "updated_at": "2026-10-09T12:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Organization"
                }
              }
            },
            "description": "The updated organization"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "409": {
            "$ref": "#/components/responses/ConflictError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Update an organization",
        "tags": [
          "Organizations"
        ],
        "x-required-scope": "organizations:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/organizations/{organization_id}/connection": {
      "delete": {
        "description": "Deletes the organization's enterprise connection, whatever its\nstatus. Users on the organization's domains who could sign in only\nthrough it can't sign in until it has a connection again.\n",
        "operationId": "deleteOrganizationConnection",
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Delete an organization's connection",
        "tags": [
          "Organizations"
        ],
        "x-required-scope": "organizations:write"
      },
      "get": {
        "description": "Returns the organization's enterprise connection: its status, its last\ntest sign-in, the identity provider details being edited, and the\nvalues to give the identity provider. A client secret or certificate\nis never returned, only whether one is set. Returns 404 when the\norganization has no connection yet.\n",
        "operationId": "getOrganizationConnection",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "id": "c2m7x1b8n4c6d0f5h3k9q",
                  "last_test": {
                    "mapped": {
                      "email": "anna@globex.com",
                      "name": "Anna Virtanen"
                    },
                    "passed": true,
                    "tested_at": "2026-10-08T14:02:00Z"
                  },
                  "name": "Globex Entra ID",
                  "pending_changes": false,
                  "provider_type": "azure_ad",
                  "saml": {
                    "acs_url": "https://acme.authalla.com/saml/acs",
                    "idp_certificate_set": true,
                    "idp_entity_id": "https://sts.windows.net/0f4c2b7a-1d3e-4f5a-9b8c-7d6e5f4a3b2c/",
                    "idp_sso_url": "https://login.microsoftonline.com/0f4c2b7a-1d3e-4f5a-9b8c-7d6e5f4a3b2c/saml2",
                    "metadata_url": "https://acme.authalla.com/saml/c2m7x1b8n4c6d0f5h3k9q/metadata",
                    "sp_entity_id": "urn:authalla:sp:c2m7x1b8n4c6d0f5h3k9q"
                  },
                  "status": "active",
                  "type": "saml"
                },
                "schema": {
                  "$ref": "#/components/schemas/OrganizationConnection"
                }
              }
            },
            "description": "The connection"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Get an organization's connection",
        "tags": [
          "Organizations"
        ],
        "x-required-scope": "organizations:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        },
        {
          "$ref": "#/components/parameters/OrganizationId"
        }
      ],
      "post": {
        "description": "Creates the organization's enterprise connection as a draft, which\nsigns no one in until it passes a test sign-in or you turn it on. An\nOIDC connection needs its discovery URL and client credentials; its\nendpoints are read from the discovery document. A SAML connection can\ntake its identity provider details now or later, as a metadata URL or\nas the sign-on URL, entity ID and certificate. An organization has at\nmost one connection: a second returns 409.\n",
        "operationId": "createOrganizationConnection",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "metadata_url": "https://login.microsoftonline.com/0f4c2b7a-1d3e-4f5a-9b8c-7d6e5f4a3b2c/federationmetadata/2007-06/federationmetadata.xml?appid=5e1b2c3d-4f5a-6b7c-8d9e-0f1a2b3c4d5e",
                "name": "Globex Entra ID",
                "provider_type": "azure_ad",
                "type": "saml"
              },
              "schema": {
                "$ref": "#/components/schemas/OrganizationConnectionInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "id": "c2m7x1b8n4c6d0f5h3k9q",
                  "name": "Globex Entra ID",
                  "pending_changes": false,
                  "provider_type": "azure_ad",
                  "saml": {
                    "acs_url": "https://acme.authalla.com/saml/acs",
                    "idp_certificate_set": true,
                    "idp_entity_id": "https://sts.windows.net/0f4c2b7a-1d3e-4f5a-9b8c-7d6e5f4a3b2c/",
                    "idp_sso_url": "https://login.microsoftonline.com/0f4c2b7a-1d3e-4f5a-9b8c-7d6e5f4a3b2c/saml2",
                    "metadata_url": "https://acme.authalla.com/saml/c2m7x1b8n4c6d0f5h3k9q/metadata",
                    "sp_entity_id": "urn:authalla:sp:c2m7x1b8n4c6d0f5h3k9q"
                  },
                  "status": "draft",
                  "type": "saml"
                },
                "schema": {
                  "$ref": "#/components/schemas/OrganizationConnection"
                }
              }
            },
            "description": "The connection, as a draft"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "409": {
            "$ref": "#/components/responses/ConflictError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Set up an organization's connection",
        "tags": [
          "Organizations"
        ],
        "x-required-scope": "organizations:write"
      },
      "put": {
        "description": "Changes the identity provider details, or turns the connection on or\noff. Fields omitted from the body are left unchanged. On a draft the\ndetails apply at once. On a connection that has been on they wait as\npending changes, and sign-ins keep using the previous details until a\ntest sign-in of the new ones passes. `status: active` turns a draft on\nwithout a test sign-in, so check the setup first.\n",
        "operationId": "updateOrganizationConnection",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "status": "inactive"
              },
              "schema": {
                "$ref": "#/components/schemas/OrganizationConnectionUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "id": "k8d3m1x7b2n9c4v6z0q5w",
                  "name": "Globex Okta",
                  "oidc": {
                    "client_id": "0oa8f2k3m4n5p6q7r8s9",
                    "client_secret_set": true,
                    "discovery_url": "https://globex.okta.com/.well-known/openid-configuration",
                    "redirect_uri": "https://acme.authalla.com/oauth2/enterprise-sso-callback"
                  },
                  "pending_changes": false,
                  "provider_type": "okta",
                  "status": "inactive",
                  "type": "oidc"
                },
                "schema": {
                  "$ref": "#/components/schemas/OrganizationConnection"
                }
              }
            },
            "description": "The connection"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Update an organization's connection",
        "tags": [
          "Organizations"
        ],
        "x-required-scope": "organizations:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/organizations/{organization_id}/connection/pending-changes": {
      "delete": {
        "description": "Drops the identity provider details waiting for a test sign-in. The live details stay.",
        "operationId": "discardOrganizationConnectionPendingChanges",
        "responses": {
          "204": {
            "description": "Discarded"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Discard pending changes",
        "tags": [
          "Organizations"
        ],
        "x-required-scope": "organizations:write"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        },
        {
          "$ref": "#/components/parameters/OrganizationId"
        }
      ]
    },
    "/management/v1/tenants/{tenant_id}/organizations/{organization_id}/connection/test": {
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        },
        {
          "$ref": "#/components/parameters/OrganizationId"
        }
      ],
      "post": {
        "description": "Returns a link that runs a test sign-in through the connection, with\nits pending changes if it has any. Open it in a browser and sign in\nat the identity provider. The test signs no one in and creates no\nuser; the connection's `last_test` then shows what the identity\nprovider sent. A passing test turns a draft on, and makes pending\nchanges live. The link works once, for 15 minutes.\n",
        "operationId": "startOrganizationConnectionTest",
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "url": "https://acme.authalla.com/saml/c2m7x1b8n4c6d0f5h3k9q/test?token=q9w8e7r6t5y4u3i2o1p0a9s8d7f6g5h4j3k2l1z0x9c"
                },
                "schema": {
                  "$ref": "#/components/schemas/ConnectionTestLink"
                }
              }
            },
            "description": "The test sign-in link"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Start a test sign-in",
        "tags": [
          "Organizations"
        ],
        "x-required-scope": "organizations:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/organizations/{organization_id}/members": {
      "get": {
        "description": "Lists the organization's members, the most recent single sign-on\nfirst. A user becomes a member by signing in through its connection,\nor when added. With `not_on_sso=true` it lists instead the active\nusers on its domains who haven't signed in through its connection\nyet, with how each still signs in.\n",
        "operationId": "listOrganizationMembers",
        "parameters": [
          {
            "description": "Only users whose email or name contains this.",
            "in": "query",
            "name": "search",
            "schema": {
              "maxLength": 200,
              "type": "string"
            }
          },
          {
            "description": "List the users on its domains who aren't on its single sign-on yet, instead of its members.",
            "in": "query",
            "name": "not_on_sso",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "minimum": 0,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "members": [
                    {
                      "email": "anna@globex.com",
                      "last_login": "2026-10-09T08:12:00Z",
                      "last_sso_sign_in_at": "2026-10-09T08:12:00Z",
                      "name": "Anna Virtanen",
                      "status": "active",
                      "user_id": "u4n8c2k6x0m3b7v1z9q5w"
                    }
                  ],
                  "total": 1
                },
                "schema": {
                  "$ref": "#/components/schemas/OrganizationMemberList"
                }
              }
            },
            "description": "A page of members"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "List an organization's members",
        "tags": [
          "Organizations"
        ],
        "x-required-scope": "organizations:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        },
        {
          "$ref": "#/components/parameters/OrganizationId"
        }
      ],
      "post": {
        "description": "Makes a user a member of the organization by email, even one on\nanother domain such as a contractor. When the tenant has no user with\nthat email, one is created, so you can add someone before their first\nsign-in. With `send_email`, they are emailed that they can now sign\nin with the organization's single sign-on, unless they were a member\nalready; that is refused with 400 while the organization's connection\nisn't enabled.\n",
        "operationId": "addOrganizationMember",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "email": "anna@globex.com",
                "name": "Anna Virtanen",
                "send_email": true
              },
              "schema": {
                "$ref": "#/components/schemas/OrganizationMemberInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "email": "anna@globex.com",
                  "emailed": true,
                  "name": "Anna Virtanen",
                  "status": "active",
                  "user_created": true,
                  "user_id": "u4n8c2k6x0m3b7v1z9q5w"
                },
                "schema": {
                  "$ref": "#/components/schemas/AddedOrganizationMember"
                }
              }
            },
            "description": "The member"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Add a member",
        "tags": [
          "Organizations"
        ],
        "x-required-scope": "organization-members:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/organizations/{organization_id}/members/{user_id}": {
      "delete": {
        "description": "Removes the user from the organization. The user stays in the tenant,\nand signing in through the organization's connection makes them a\nmember again, so to offboard someone, remove their access in the\nidentity provider first. With `sign_out=true` the user is also signed\nout everywhere: their sessions end and their tokens are revoked.\n",
        "operationId": "removeOrganizationMember",
        "parameters": [
          {
            "description": "Also sign the user out everywhere.",
            "in": "query",
            "name": "sign_out",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Remove a member",
        "tags": [
          "Organizations"
        ],
        "x-required-scope": "organization-members:write"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        },
        {
          "$ref": "#/components/parameters/OrganizationId"
        },
        {
          "$ref": "#/components/parameters/UserId"
        }
      ]
    },
    "/management/v1/tenants/{tenant_id}/organizations/{organization_id}/portal-links": {
      "delete": {
        "description": "Revokes the organization's setup link, which also ends any portal\nsession opened with it.\n",
        "operationId": "revokeOrganizationPortalLinks",
        "responses": {
          "204": {
            "description": "Revoked"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Revoke the setup link",
        "tags": [
          "Organizations"
        ],
        "x-required-scope": "portal-links:write"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        },
        {
          "$ref": "#/components/parameters/OrganizationId"
        }
      ],
      "post": {
        "description": "Creates a link that lets the organization's IT admin set up, test and\nturn off its enterprise connection in the setup portal, on your\ntenant's sign-in domain, without an Authalla account. Send it to them,\nor redirect a signed-in admin of your customer to it. It lasts seven\ndays and can be opened more than once. The URL is returned only here,\nand creating a link revokes the organization's previous one. Changes\nmade in the portal show in Team activity as the setup link's.\n",
        "operationId": "createOrganizationPortalLink",
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "expires_at": "2026-10-17T09:15:00Z",
                  "url": "https://acme.authalla.com/oauth2/setup/Hq8w2e4r6t8y0u2i4o6p8a0s2d4f6g8h0Jk1"
                },
                "schema": {
                  "$ref": "#/components/schemas/PortalLink"
                }
              }
            },
            "description": "The setup link"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Create a setup link",
        "tags": [
          "Organizations"
        ],
        "x-required-scope": "portal-links:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/resource-servers": {
      "get": {
        "description": "Lists the tenant's resource servers with their scopes.",
        "operationId": "listResourceServers",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "resource_servers": [
                    {
                      "created_at": "2026-09-23T09:15:00Z",
                      "id": "r7s2v9e4r1s8e3r5v6e0x",
                      "identifier": "https://mcp.acme.com/mcp",
                      "name": "Acme CRM",
                      "protected_resource_metadata": {
                        "authorization_servers": [
                          "https://k3v9q2m7x1b8n4c6d0f5h.authalla.com"
                        ],
                        "bearer_methods_supported": [
                          "header"
                        ],
                        "resource": "https://mcp.acme.com/mcp",
                        "resource_name": "Acme CRM",
                        "scopes_supported": [
                          "openid",
                          "email",
                          "crm:read"
                        ]
                      },
                      "protected_resource_metadata_url": "https://mcp.acme.com/.well-known/oauth-protected-resource/mcp",
                      "scope_prefix": "crm",
                      "scopes": [
                        {
                          "description": "Read your contacts and deals",
                          "grant_to_all_users": true,
                          "name": "read",
                          "scope": "crm:read",
                          "user_count": 42
                        }
                      ],
                      "updated_at": "2026-09-23T09:15:00Z"
                    }
                  ],
                  "total": 1
                },
                "schema": {
                  "$ref": "#/components/schemas/ResourceServerList"
                }
              }
            },
            "description": "The tenant's resource servers"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "List resource servers",
        "tags": [
          "Resource Servers"
        ],
        "x-required-scope": "resource-servers:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        }
      ],
      "post": {
        "description": "Registers an API or MCP server in the tenant, with its first scopes.\nThe identifier becomes the `aud` of access tokens requested for it with\nthe RFC 8707 `resource` parameter; a `resource` that isn't registered\nis refused with `invalid_target`. The scope prefix is fixed after\ncreation, and each scope is issued as `\u003cscope_prefix\u003e:\u003cname\u003e`. Prefixes\nAuthalla uses for its own scopes are reserved. A clashing identifier or\nprefix in the tenant returns 409. The response carries the protected\nresource metadata (RFC 9728) the server should serve.\n",
        "operationId": "createResourceServer",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "identifier": "https://mcp.acme.com/mcp",
                "name": "Acme CRM",
                "scope_prefix": "crm",
                "scopes": [
                  {
                    "description": "Read your contacts and deals",
                    "grant_to_all_users": true,
                    "name": "read"
                  }
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/ResourceServerInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-09-23T09:15:00Z",
                  "id": "r7s2v9e4r1s8e3r5v6e0x",
                  "identifier": "https://mcp.acme.com/mcp",
                  "name": "Acme CRM",
                  "protected_resource_metadata": {
                    "authorization_servers": [
                      "https://k3v9q2m7x1b8n4c6d0f5h.authalla.com"
                    ],
                    "bearer_methods_supported": [
                      "header"
                    ],
                    "resource": "https://mcp.acme.com/mcp",
                    "resource_name": "Acme CRM",
                    "scopes_supported": [
                      "openid",
                      "email",
                      "crm:read"
                    ]
                  },
                  "protected_resource_metadata_url": "https://mcp.acme.com/.well-known/oauth-protected-resource/mcp",
                  "scope_prefix": "crm",
                  "scopes": [
                    {
                      "description": "Read your contacts and deals",
                      "grant_to_all_users": true,
                      "name": "read",
                      "scope": "crm:read",
                      "user_count": 42
                    }
                  ],
                  "updated_at": "2026-09-23T09:15:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/ResourceServer"
                }
              }
            },
            "description": "The created resource server"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "409": {
            "$ref": "#/components/responses/ConflictError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Create a resource server",
        "tags": [
          "Resource Servers"
        ],
        "x-required-scope": "resource-servers:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/resource-servers/{resource_server_id}": {
      "delete": {
        "description": "Deletes the resource server. Its scopes are deleted and taken from\nevery user who held them, and refreshes of tokens issued for it fail.\n",
        "operationId": "deleteResourceServer",
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Delete a resource server",
        "tags": [
          "Resource Servers"
        ],
        "x-required-scope": "resource-servers:write"
      },
      "get": {
        "description": "Returns the resource server with its scopes and protected resource metadata.",
        "operationId": "getResourceServer",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-09-23T09:15:00Z",
                  "id": "r7s2v9e4r1s8e3r5v6e0x",
                  "identifier": "https://mcp.acme.com/mcp",
                  "name": "Acme CRM",
                  "protected_resource_metadata": {
                    "authorization_servers": [
                      "https://k3v9q2m7x1b8n4c6d0f5h.authalla.com"
                    ],
                    "bearer_methods_supported": [
                      "header"
                    ],
                    "resource": "https://mcp.acme.com/mcp",
                    "resource_name": "Acme CRM",
                    "scopes_supported": [
                      "openid",
                      "email",
                      "crm:read"
                    ]
                  },
                  "protected_resource_metadata_url": "https://mcp.acme.com/.well-known/oauth-protected-resource/mcp",
                  "scope_prefix": "crm",
                  "scopes": [
                    {
                      "description": "Read your contacts and deals",
                      "grant_to_all_users": true,
                      "name": "read",
                      "scope": "crm:read",
                      "user_count": 42
                    }
                  ],
                  "updated_at": "2026-09-23T09:15:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/ResourceServer"
                }
              }
            },
            "description": "The resource server"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Get a resource server",
        "tags": [
          "Resource Servers"
        ],
        "x-required-scope": "resource-servers:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        },
        {
          "$ref": "#/components/parameters/ResourceServerId"
        }
      ],
      "put": {
        "description": "Changes the identifier or name. Fields omitted from the body are left\nunchanged; the scope prefix can't be changed. Changing the identifier\nmakes refreshes of tokens issued for the old one fail with\n`invalid_target`.\n",
        "operationId": "updateResourceServer",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "name": "Acme CRM (production)"
              },
              "schema": {
                "$ref": "#/components/schemas/ResourceServerUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-09-23T09:15:00Z",
                  "id": "r7s2v9e4r1s8e3r5v6e0x",
                  "identifier": "https://mcp.acme.com/mcp",
                  "name": "Acme CRM",
                  "protected_resource_metadata": {
                    "authorization_servers": [
                      "https://k3v9q2m7x1b8n4c6d0f5h.authalla.com"
                    ],
                    "bearer_methods_supported": [
                      "header"
                    ],
                    "resource": "https://mcp.acme.com/mcp",
                    "resource_name": "Acme CRM",
                    "scopes_supported": [
                      "openid",
                      "email",
                      "crm:read"
                    ]
                  },
                  "protected_resource_metadata_url": "https://mcp.acme.com/.well-known/oauth-protected-resource/mcp",
                  "scope_prefix": "crm",
                  "scopes": [
                    {
                      "description": "Read your contacts and deals",
                      "grant_to_all_users": true,
                      "name": "read",
                      "scope": "crm:read",
                      "user_count": 42
                    }
                  ],
                  "updated_at": "2026-09-23T09:15:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/ResourceServer"
                }
              }
            },
            "description": "The updated resource server"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "409": {
            "$ref": "#/components/responses/ConflictError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Update a resource server",
        "tags": [
          "Resource Servers"
        ],
        "x-required-scope": "resource-servers:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/resource-servers/{resource_server_id}/scopes": {
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        },
        {
          "$ref": "#/components/parameters/ResourceServerId"
        }
      ],
      "post": {
        "description": "Adds a scope, issued as `\u003cscope_prefix\u003e:\u003cname\u003e`. With\n`grant_to_all_users` on, every current user of the tenant gets the\nscope at once, and every user created later gets it too. A scope with\nthe same name returns 409.\n",
        "operationId": "createResourceServerScope",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "description": "Read your contacts and deals",
                "grant_to_all_users": true,
                "name": "read"
              },
              "schema": {
                "$ref": "#/components/schemas/ResourceServerScopeInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-09-23T09:15:00Z",
                  "id": "r7s2v9e4r1s8e3r5v6e0x",
                  "identifier": "https://mcp.acme.com/mcp",
                  "name": "Acme CRM",
                  "protected_resource_metadata": {
                    "authorization_servers": [
                      "https://k3v9q2m7x1b8n4c6d0f5h.authalla.com"
                    ],
                    "bearer_methods_supported": [
                      "header"
                    ],
                    "resource": "https://mcp.acme.com/mcp",
                    "resource_name": "Acme CRM",
                    "scopes_supported": [
                      "openid",
                      "email",
                      "crm:read"
                    ]
                  },
                  "protected_resource_metadata_url": "https://mcp.acme.com/.well-known/oauth-protected-resource/mcp",
                  "scope_prefix": "crm",
                  "scopes": [
                    {
                      "description": "Read your contacts and deals",
                      "grant_to_all_users": true,
                      "name": "read",
                      "scope": "crm:read",
                      "user_count": 42
                    }
                  ],
                  "updated_at": "2026-09-23T09:15:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/ResourceServer"
                }
              }
            },
            "description": "The resource server with all its scopes"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "409": {
            "$ref": "#/components/responses/ConflictError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Add a scope to a resource server",
        "tags": [
          "Resource Servers"
        ],
        "x-required-scope": "resource-servers:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/resource-servers/{resource_server_id}/scopes/{scope_name}": {
      "delete": {
        "description": "Deletes the scope and takes it from every user who held it.",
        "operationId": "deleteResourceServerScope",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-09-23T09:15:00Z",
                  "id": "r7s2v9e4r1s8e3r5v6e0x",
                  "identifier": "https://mcp.acme.com/mcp",
                  "name": "Acme CRM",
                  "protected_resource_metadata": {
                    "authorization_servers": [
                      "https://k3v9q2m7x1b8n4c6d0f5h.authalla.com"
                    ],
                    "bearer_methods_supported": [
                      "header"
                    ],
                    "resource": "https://mcp.acme.com/mcp",
                    "resource_name": "Acme CRM",
                    "scopes_supported": [
                      "openid",
                      "email"
                    ]
                  },
                  "protected_resource_metadata_url": "https://mcp.acme.com/.well-known/oauth-protected-resource/mcp",
                  "scope_prefix": "crm",
                  "scopes": [],
                  "updated_at": "2026-09-23T09:15:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/ResourceServer"
                }
              }
            },
            "description": "The resource server with its remaining scopes"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Delete a resource server scope",
        "tags": [
          "Resource Servers"
        ],
        "x-required-scope": "resource-servers:write"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        },
        {
          "$ref": "#/components/parameters/ResourceServerId"
        },
        {
          "$ref": "#/components/parameters/ScopeName"
        }
      ],
      "put": {
        "description": "Changes a scope's description or its Grant to all users switch. Fields\nomitted from the body are left unchanged; the name can't be changed.\nSwitching `grant_to_all_users` on grants the scope to every user of the\ntenant, now and later. Switching it off takes the scope from every user\nof the tenant, including users it was granted to one by one; the\nscope's `user_count` says how many that is.\n",
        "operationId": "updateResourceServerScope",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "grant_to_all_users": true
              },
              "schema": {
                "$ref": "#/components/schemas/ResourceServerScopeUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-09-23T09:15:00Z",
                  "id": "r7s2v9e4r1s8e3r5v6e0x",
                  "identifier": "https://mcp.acme.com/mcp",
                  "name": "Acme CRM",
                  "protected_resource_metadata": {
                    "authorization_servers": [
                      "https://k3v9q2m7x1b8n4c6d0f5h.authalla.com"
                    ],
                    "bearer_methods_supported": [
                      "header"
                    ],
                    "resource": "https://mcp.acme.com/mcp",
                    "resource_name": "Acme CRM",
                    "scopes_supported": [
                      "openid",
                      "email",
                      "crm:read"
                    ]
                  },
                  "protected_resource_metadata_url": "https://mcp.acme.com/.well-known/oauth-protected-resource/mcp",
                  "scope_prefix": "crm",
                  "scopes": [
                    {
                      "description": "Read your contacts and deals",
                      "grant_to_all_users": true,
                      "name": "read",
                      "scope": "crm:read",
                      "user_count": 42
                    }
                  ],
                  "updated_at": "2026-09-23T09:15:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/ResourceServer"
                }
              }
            },
            "description": "The resource server with all its scopes"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Update a resource server scope",
        "tags": [
          "Resource Servers"
        ],
        "x-required-scope": "resource-servers:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/sender-address": {
      "delete": {
        "description": "The tenant's sign-in emails come from Authalla's sign-in address again.",
        "operationId": "clearTenantSender",
        "responses": {
          "204": {
            "description": "The tenant sends from Authalla's sign-in address"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Send a tenant's emails from Authalla's sign-in address",
        "tags": [
          "Sender Addresses"
        ],
        "x-required-scope": "sender-addresses:write"
      },
      "get": {
        "description": "Returns the address the tenant's sign-in emails come from.",
        "operationId": "getTenantSender",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "email": "hi@acme.com",
                  "sender_address_id": "42"
                },
                "schema": {
                  "$ref": "#/components/schemas/TenantSender"
                }
              }
            },
            "description": "The address the tenant sends from"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Get the address a tenant sends from",
        "tags": [
          "Sender Addresses"
        ],
        "x-required-scope": "sender-addresses:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        }
      ],
      "put": {
        "description": "Makes the tenant's sign-in emails come from one of the account's sender\naddresses. Fails with 409 when the address isn't verified yet.\n",
        "operationId": "setTenantSender",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "sender_address_id": "42"
              },
              "schema": {
                "$ref": "#/components/schemas/TenantSenderInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "email": "hi@acme.com",
                  "sender_address_id": "42"
                },
                "schema": {
                  "$ref": "#/components/schemas/TenantSender"
                }
              }
            },
            "description": "The address the tenant sends from now"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "409": {
            "$ref": "#/components/responses/ConflictError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Send a tenant's emails from a sender address",
        "tags": [
          "Sender Addresses"
        ],
        "x-required-scope": "sender-addresses:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/social-logins": {
      "get": {
        "description": "Lists the tenant's social logins, enabled and disabled.",
        "operationId": "listSocialLogins",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "social_logins": [
                    {
                      "client_id": "123456789012-abc123def456.apps.googleusercontent.com",
                      "created_at": "2026-03-02T09:15:00Z",
                      "id": "s5o2c8i1a4l7l9o3g6i0n",
                      "provider_type": "google",
                      "redirect_uri": "https://k3v9q2m7x1b8n4c6d0f5h.authalla.com/oauth2/social-login-callback",
                      "status": "active",
                      "updated_at": "2026-03-04T14:40:12Z"
                    }
                  ],
                  "total": 1
                },
                "schema": {
                  "$ref": "#/components/schemas/SocialLoginList"
                }
              }
            },
            "description": "The tenant's social login providers"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "List social logins",
        "tags": [
          "Social Logins"
        ],
        "x-required-scope": "social-logins:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        }
      ],
      "post": {
        "description": "Configures a social login for the given tenant. A tenant has at most one\nsocial login per provider type; a second one returns 409. The response\nincludes the redirect URI to whitelist on the upstream provider. A\nsocial login created without a client secret starts disabled.\n",
        "operationId": "createSocialLogin",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "client_id": "123456789012-abc123def456.apps.googleusercontent.com",
                "client_secret": "GOCSPX-9f8e7d6c5b4a3f2e1d0c",
                "provider_type": "google"
              },
              "schema": {
                "$ref": "#/components/schemas/SocialLoginInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "client_id": "123456789012-abc123def456.apps.googleusercontent.com",
                  "created_at": "2026-03-02T09:15:00Z",
                  "id": "s5o2c8i1a4l7l9o3g6i0n",
                  "provider_type": "google",
                  "redirect_uri": "https://k3v9q2m7x1b8n4c6d0f5h.authalla.com/oauth2/social-login-callback",
                  "status": "active",
                  "updated_at": "2026-03-04T14:40:12Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/SocialLogin"
                }
              }
            },
            "description": "The created social login provider"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "409": {
            "$ref": "#/components/responses/ConflictError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Create a social login",
        "tags": [
          "Social Logins"
        ],
        "x-required-scope": "social-logins:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/social-logins/redirect-uri": {
      "get": {
        "description": "Returns the OAuth callback URL to whitelist on upstream providers for the\ngiven tenant. Call this before creating a provider.\n",
        "operationId": "getSocialLoginRedirectUri",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "redirect_uri": "https://k3v9q2m7x1b8n4c6d0f5h.authalla.com/oauth2/social-login-callback"
                },
                "schema": {
                  "$ref": "#/components/schemas/SocialLoginRedirectUri"
                }
              }
            },
            "description": "The redirect URI for the tenant"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Get the social login redirect URI",
        "tags": [
          "Social Logins"
        ],
        "x-required-scope": "social-logins:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        }
      ]
    },
    "/management/v1/tenants/{tenant_id}/social-logins/{social_login_id}": {
      "delete": {
        "description": "Deletes the social login from this tenant. Other tenants are not affected.",
        "operationId": "deleteSocialLogin",
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Delete a social login",
        "tags": [
          "Social Logins"
        ],
        "x-required-scope": "social-logins:write"
      },
      "get": {
        "description": "Returns the social login provider. The client secret is never returned.",
        "operationId": "getSocialLogin",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "client_id": "123456789012-abc123def456.apps.googleusercontent.com",
                  "created_at": "2026-03-02T09:15:00Z",
                  "id": "s5o2c8i1a4l7l9o3g6i0n",
                  "provider_type": "google",
                  "redirect_uri": "https://k3v9q2m7x1b8n4c6d0f5h.authalla.com/oauth2/social-login-callback",
                  "status": "active",
                  "updated_at": "2026-03-04T14:40:12Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/SocialLogin"
                }
              }
            },
            "description": "The social login provider"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Get a social login",
        "tags": [
          "Social Logins"
        ],
        "x-required-scope": "social-logins:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        },
        {
          "$ref": "#/components/parameters/SocialLoginId"
        }
      ],
      "put": {
        "description": "Updates the social login. Fields omitted from the body are left\nunchanged. Setting `status` to `active` without a client secret returns\n400.\n",
        "operationId": "updateSocialLogin",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "client_secret": "GOCSPX-0a1b2c3d4e5f6a7b8c9d"
              },
              "schema": {
                "$ref": "#/components/schemas/SocialLoginUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "client_id": "123456789012-abc123def456.apps.googleusercontent.com",
                  "created_at": "2026-03-02T09:15:00Z",
                  "id": "s5o2c8i1a4l7l9o3g6i0n",
                  "provider_type": "google",
                  "redirect_uri": "https://k3v9q2m7x1b8n4c6d0f5h.authalla.com/oauth2/social-login-callback",
                  "status": "active",
                  "updated_at": "2026-03-04T14:40:12Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/SocialLogin"
                }
              }
            },
            "description": "The updated social login provider"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "409": {
            "$ref": "#/components/responses/ConflictError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Update a social login",
        "tags": [
          "Social Logins"
        ],
        "x-required-scope": "social-logins:write"
      }
    },
    "/management/v1/tenants/{tenant_id}/theme": {
      "get": {
        "description": "Returns the login-page theme for the given tenant.",
        "operationId": "getTenantTheme",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "brand_color": "#4f46e5",
                  "card_background_color": "#ffffff",
                  "color_mode": "light_dark",
                  "corner_style": "rounded",
                  "dark": {
                    "brand_color": "#818cf8",
                    "card_background_color": "#111827",
                    "logo_url": "/oauth2/logo/acc_2x9Kf3/b83d5e01c4.png",
                    "page_background_color": "#030712"
                  },
                  "default_language": "fi",
                  "font": "inter",
                  "icon_url": "/oauth2/logo/acc_2x9Kf3/0c4d7e19aa.png",
                  "logo_url": "/oauth2/logo/acc_2x9Kf3/6f1c0e2a9b.png",
                  "page_background_color": "#f9fafb"
                },
                "schema": {
                  "$ref": "#/components/schemas/Theme"
                }
              }
            },
            "description": "The tenant's theme"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Get tenant theme",
        "tags": [
          "Theme"
        ],
        "x-required-scope": "theme:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        }
      ],
      "put": {
        "description": "Updates the login-page theme for the given tenant. Only the fields\npresent in the request body are changed; omitted fields keep their\ncurrent value.\n\nA `logo_url`, `dark.logo_url` or `icon_url` that is an absolute\nhttp(s) URL is fetched once and stored by Authalla; the response carries the stored\ncopy's `/oauth2/logo/...` path. The image must be PNG, JPEG, GIF or\nWebP, at most 2 MiB, served without a redirect.\n\nColours are 3- or 6-digit hex (`#4f46e5`, `#fff`). Text and secondary\nbuttons are derived from them, so they stay readable on the card.\nDark colours are only used when `color_mode` is `light_dark`. Sending\nthem does not change the mode. `corner_style` applies to both.\n\n`default_language` (`en`, `fi` or `sv`) is the sign-in language when\nthe app passes no supported `ui_locales`, in place of the browser's.\nAn empty string goes back to following the browser.\n",
        "operationId": "updateTenantTheme",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "brand_color": "#4f46e5",
                "color_mode": "light_dark",
                "corner_style": "pill",
                "dark": {
                  "brand_color": "#818cf8",
                  "logo_url": "https://www.acme.com/brand/logo-white.png"
                },
                "default_language": "fi",
                "font": "inter",
                "icon_url": "https://www.acme.com/favicon.png",
                "logo_url": "https://www.acme.com/brand/logo.png"
              },
              "schema": {
                "$ref": "#/components/schemas/ThemeUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "brand_color": "#4f46e5",
                  "card_background_color": "#ffffff",
                  "color_mode": "light_dark",
                  "corner_style": "rounded",
                  "dark": {
                    "brand_color": "#818cf8",
                    "card_background_color": "#111827",
                    "logo_url": "/oauth2/logo/acc_2x9Kf3/b83d5e01c4.png",
                    "page_background_color": "#030712"
                  },
                  "default_language": "fi",
                  "font": "inter",
                  "icon_url": "/oauth2/logo/acc_2x9Kf3/0c4d7e19aa.png",
                  "logo_url": "/oauth2/logo/acc_2x9Kf3/6f1c0e2a9b.png",
                  "page_background_color": "#f9fafb"
                },
                "schema": {
                  "$ref": "#/components/schemas/Theme"
                }
              }
            },
            "description": "The updated theme"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "accountApiKey": []
          }
        ],
        "summary": "Update tenant theme",
        "tags": [
          "Theme"
        ],
        "x-required-scope": "theme:write"
      }
    }
  },
  "servers": [
    {
      "description": "Production API server",
      "url": "https://api.authalla.com"
    }
  ],
  "tags": [
    {
      "description": "Account-level details",
      "name": "Account"
    },
    {
      "description": "Account-level tenant provisioning",
      "name": "Tenants"
    },
    {
      "description": "Apps (OAuth2 clients) within a tenant",
      "name": "Apps"
    },
    {
      "description": "Tenant login-page theme and branding",
      "name": "Theme"
    },
    {
      "description": "Custom login domains within a tenant",
      "name": "Custom Domains"
    },
    {
      "description": "The account's addresses its tenants send sign-in emails from",
      "name": "Sender Addresses"
    },
    {
      "description": "Social login (OAuth) providers within a tenant",
      "name": "Social Logins"
    },
    {
      "description": "A tenant's own APIs and MCP servers that its access tokens can be issued\nfor, with their scopes\n",
      "name": "Resource Servers"
    },
    {
      "description": "A tenant's business customers, each with its email domains, its\nenterprise connection, its members and the setup link its IT admin\nuses to connect their identity provider\n",
      "name": "Organizations"
    }
  ]
}