> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Token Endpoint: Code Exchange and Refresh | Didit Authentication API

> POST /token/ exchanges a PKCE authorization code or rotates a refresh token. Reusing a refresh token revokes the session.

> [!WARNING]
>
> Authentication is new. These endpoints are served by your application's issuer, such as `https://<name>.auth.didit.me`, and never take your Didit API key.

A refresh token works once. Sending a used refresh token again revokes the whole session, so run one refresh at a time for a session and replace all three tokens together. See [Refresh tokens](https://docs.didit.me/authentication/tokens-and-jwks.md#refresh-tokens).


## OpenAPI

````yaml openapi-authentication.json POST /token/
openapi: 3.0.3
info:
  title: Didit Authentication API
  version: 1.0.0
  description: >-
    The server-facing endpoints of Didit Authentication, the sign-in product for
    your application's end users. Authentication is **new** and reaches
    production in the 2026-11-01 release.


    ## Base URL


    Every application is its own OpenID Connect issuer, and every endpoint below
    is served at the root of that issuer:


    - `https://<name>.auth.didit.me` for a live application, or
    `https://<name>.sandbox.auth.didit.me` for a sandbox application.

    - Your own custom domain, once it is active. It then replaces the default
    host as the issuer: new tokens name it in `iss`, discovery points at it and
    the publishable key changes. Access tokens issued under the default host
    fail `/session/`, `/step-up/` and `/monitoring-token/` until they are
    refreshed.


    Live and sandbox are separate applications, so they are separate issuers
    with separate keys. The publishable key is `pk_live_` (live) or `pk_test_`
    (sandbox) followed by the unpadded base64url encoding of the issuer, without
    a trailing slash: decode it to find the issuer. It is a public identifier,
    not a credential.


    The same JSON endpoints are also served under
    `https://apx.didit.me/auth/v2/authentication/{application_id}/`. Tokens
    always name the issuer above, whichever address issued them.


    ## Credentials


    These endpoints never take your Didit API key or a console token.


    - Discovery, the JWKS and `POST /token/` take no credential. Clients are
    public: there is no client secret, and an authorization code is bound to its
    PKCE verifier (`S256`). `client_id` is your application ID.

    - `/session/`, `/step-up/` and `/monitoring-token/` take the person's access
    token: `Authorization: Bearer <access_token>`. Each successful call counts
    as activity on the session, so it restarts the session's idle timeout.


    ## Request bodies


    Every endpoint that takes a body accepts JSON, form-encoded and multipart
    bodies. This document lists JSON, plus form-encoded for `POST /token/` as
    OAuth clients send it. A field the endpoint does not accept gets `400
    {"<field>": "unknown_field"}`.


    ## Tokens


    - **Access token**: an ES256 JWT with header `typ: at+jwt`, valid for at
    most 5 minutes and never longer than its session. Claims:
    `AccessTokenClaims`.

    - **ID token**: an ES256 JWT with header `typ: JWT`. Claims:
    `IDTokenClaims`. It is never accepted as an access token.

    - **Refresh token**: a random string that rotates on every use. Sending a
    used refresh token again revokes the session.


    Verify a token with the keys from `GET /.well-known/jwks.json`: the
    algorithm must be `ES256`, `typ` must match the token type, `iss` must equal
    the issuer you configured and `aud` your application ID. Offline
    verification sees a revoked session only once its access token expires; `GET
    /session/` is the instant check.


    ## Errors


    | Body | When |

    | --- | --- |

    | `{"error": "<code>"}` | Errors from `POST /token/`, all with status `400`,
    in the body shape of [RFC 6749 section
    5.2](https://www.rfc-editor.org/rfc/rfc6749#section-5.2): `invalid_grant`,
    `invalid_client` and Didit's `mfa_required`. Service errors:
    `temporarily_unavailable`, `monitoring_unavailable`, `monitoring_disabled`.
    |

    | `{"<field>": ["<message>"]}` or `{"<field>": "<code>"}` | A request field
    failed validation. `non_field_errors` names a rule across fields, and
    `unknown_field` a field the endpoint does not accept. |

    | `{"detail": "<message>"}` | `401` (`invalid_token`), `403`
    (`origin_not_allowed`), `404` (unknown application or step-up) and `429`
    (rate limit). Also `400` for a body that cannot be parsed (`JSON parse error
    - ...`), `405` for a method the endpoint does not serve and `415` for an
    unsupported `Content-Type`. |


    A `401` also carries `WWW-Authenticate: Bearer`, and a `429` carries
    `Retry-After` in seconds.


    Two refusals are not JSON. An address that is not an issuer is refused
    before it reaches these endpoints, for example with an empty `404` for an
    unused `*.auth.didit.me` name. A `step_up_id` that is not a UUID gets a
    `404` page.


    ## Rate limits


    A request has to pass two layers.


    **Endpoint limits**, counted per client IP address, per application and per
    endpoint:


    - Discovery and the JWKS: 1,200 requests a minute and 12,000 an hour.

    - Every other endpoint: 30 requests a minute and 300 an hour. `GET` and
    `DELETE /session/` share one count.


    Over a limit: `429 {"detail": "Too many requests from this IP address.
    Please wait before trying again. Expected available in 60 seconds."}`, or
    `3600 seconds` for the hourly limit.


    **Bot protection**, on every `POST` and `DELETE`. Its buckets are shared by
    all of the application's `POST` and `DELETE` requests, the hosted sign-in
    steps included, and refill continuously:


    - 60 requests a minute per client IP address.

    - 30 requests a minute per device, for requests that identify their device,
    as the browser and native SDKs do.

    - 1,000 requests a minute for the whole application, across every IP
    address.


    Over a bucket: `429 {"detail": "rate_limited Expected available in N
    seconds."}` (`1 second.` when N is 1). The per-IP bucket spans endpoints, so
    one address that mixes token, step-up and sign-out calls can empty it before
    it reaches any endpoint limit. The application bucket counts every client of
    the application, browsers included.


    Both layers answer before the endpoint runs, so a request that got `429` did
    not run: retry it after `Retry-After`, a refresh included.


    ## Browser requests


    A request that carries an `Origin` header must come from one of the
    application's own origins or its allowed origins, or it gets `403
    origin_not_allowed`. Server-to-server requests send no `Origin`.


    ## Not covered here


    These routes serve the hosted pages, the browser and native SDKs, and AI
    agents. They are not part of the server contract, and server SDKs do not
    call them:


    - Sign-in steps: `POST /authorize/`, `POST /password/`, `/email/*`,
    `/phone/*`, `/password-reset/*`, `/passkeys/*`, `/mfa/*`, `/social/*` and
    `/protection/complete/`.

    - Hosted pages and callbacks: `/sign-in`, `/magic`, `/invite`, `/reset`,
    `/verify/`, `/sso/*`, `/wallet/callback/`, `/logo/` and the native app
    association files under `/.well-known/`.

    - Agent access: `/agents/*` and `/grants/`, including `GET
    /agents/session/`, the live check for an AI agent's access token. Agent
    access is not part of this version of the contract.

    - The console management API for Authentication, which takes console
    sessions.


    Authentication events are not delivered as webhooks yet, so this document
    defines no webhook.
servers:
  - url: https://{issuer}
    description: 'Your application''s issuer: its sign-in origin.'
    variables:
      issuer:
        default: your-app.auth.didit.me
        description: >-
          The issuer's host: `<name>.auth.didit.me` (live),
          `<name>.sandbox.auth.didit.me` (sandbox) or your active custom domain.
security: []
tags:
  - name: Issuer
    description: Discovery and signing keys, to verify tokens.
  - name: Tokens
    description: The authorization code and refresh grants.
  - name: Sessions
    description: Check and end the session behind an access token.
  - name: Step-ups
    description: Verification before a sensitive action.
  - name: Monitoring
    description: Device credentials for transaction monitoring events.
paths:
  /token/:
    post:
      tags:
        - Tokens
      summary: Exchange a code or refresh tokens
      description: >-
        The token endpoint, for both grants.


        - `authorization_code` exchanges the code your `redirect_uri` received
        for an access, ID and refresh token. The code works once, within 60
        seconds, with the PKCE verifier and the exact `redirect_uri` of its
        sign-in. When the application allows one session per person, the
        exchange ends the person's other sessions.

        - `refresh_token` returns a new access, ID and refresh token, and the
        refresh token you sent stops working. **Sending a used refresh token
        again revokes the session** and returns `invalid_grant`, so run one
        refresh at a time for a session and replace all three tokens together.
        After a refresh fails with `400`, or gets no answer, start sign-in again
        rather than retrying: a retry after a refresh that did succeed is a
        reuse. A `429` is answered before the refresh runs, so it is safe to
        retry after `Retry-After`. A `503` means the refresh did not happen and
        the refresh token still works: retry it with backoff.


        The body can be JSON or form-encoded.
      operationId: authentication_token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenRequest'
            examples:
              AuthorizationCode:
                summary: Exchange the authorization code
                value:
                  grant_type: authorization_code
                  code: Vb0bVt4CLlr8ZL0kE5mAxv8gS6R0rW3hYqC1pN7dJ2s
                  code_verifier: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
                  redirect_uri: https://app.example.com/auth/callback
              RefreshToken:
                summary: Refresh the tokens
                value:
                  grant_type: refresh_token
                  refresh_token: >-
                    N4yZTl6y24YKYq77X_SOrBn1q2Lit81WBQMOay7dq_IVfTnxyY8ZGcJk-6-fcmWW
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/TokenRequest'
      responses:
        '200':
          description: New tokens for the session.
          headers:
            Cache-Control:
              description: 'Always `no-store`: never cache this response.'
              schema:
                type: string
                example: no-store
            Pragma:
              description: Always `no-cache`.
              schema:
                type: string
                example: no-cache
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
              examples:
                Tokens:
                  summary: Tokens for a new session
                  value:
                    access_token: >-
                      eyJhbGciOiJFUzI1NiIsImtpZCI6ImtfOHNKMmRRdlgwbkxyNVRmWWIzSGtQdyIsInR5cCI6ImF0K2p3dCJ9.eyJpc3MiOiJodHRwczovL3lvdXItYXBwLmF1dGguZGlkaXQubWUiLCJhdWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQiLCJzdWIiOiIxYzlkOGI4OC0xYmZmLTQzZDEtYWQ3YS04OGIyYmMwMDI0ZTkiLCJzaWQiOiJjOWFiZjZjYy0yZDY0LTRiZjItYjMxZS1hNGQwOWEwNzBhYjEiLCJqdGkiOiJSZ3F1NzE3OWUtbGhKMGJ2VmVELVlnIiwiaWF0IjoxNzkyMjMzNjc0LCJleHAiOjE3OTIyMzM5NzQsInZlbmRvcl9kYXRhIjoidXNlci00MiIsIm9yZyI6bnVsbCwidmVyaWZpZWQiOnt9LCJhbXIiOlsicGFzc3dvcmQiXSwiYWN0IjpudWxsLCJjbGllbnRfaWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQifQ.AxrqN8Pm-Op-3OayZk8HcvlFQZhozRiOvQ0SgQsGgrz4UNeJlUEeSlq9B8WcYUuM8naRaY5kQt_TUofnsYcyYQ
                    id_token: >-
                      eyJhbGciOiJFUzI1NiIsImtpZCI6ImtfOHNKMmRRdlgwbkxyNVRmWWIzSGtQdyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3lvdXItYXBwLmF1dGguZGlkaXQubWUiLCJhdWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQiLCJzdWIiOiIxYzlkOGI4OC0xYmZmLTQzZDEtYWQ3YS04OGIyYmMwMDI0ZTkiLCJzaWQiOiJjOWFiZjZjYy0yZDY0LTRiZjItYjMxZS1hNGQwOWEwNzBhYjEiLCJqdGkiOiJNTHRPNHM4SHItQnJrcDd2NTlPV0Z3IiwiaWF0IjoxNzkyMjMzNjc0LCJleHAiOjE3OTIyMzM5NzQsInZlbmRvcl9kYXRhIjoidXNlci00MiIsIm9yZyI6bnVsbCwidmVyaWZpZWQiOnt9LCJhbXIiOlsicGFzc3dvcmQiXSwiYWN0IjpudWxsLCJhdXRoX3RpbWUiOjE3OTIyMzM2NzQsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJlbWFpbCI6InBlcnNvbkBleGFtcGxlLmNvbSIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.avhfDFp_aUV5_1ssYzwDcIA7H6Waf19P0bn5GCg25FhLIZBGIlwc8HiUPGj1j26r21OkP4FQnS_jM1Ho3dHtKQ
                    refresh_token: >-
                      N4yZTl6y24YKYq77X_SOrBn1q2Lit81WBQMOay7dq_IVfTnxyY8ZGcJk-6-fcmWW
                    token_type: Bearer
                    expires_in: 299
                    session_id: c9abf6cc-2d64-4bf2-b31e-a4d09a070ab1
        '400':
          description: >-
            The grant failed. `invalid_grant`: the code or refresh token is
            unknown, used or expired; the PKCE verifier or `redirect_uri` does
            not match, or the `redirect_uri` is no longer registered; the person
            was blocked or their email is no longer verified; the session ended
            or no longer meets your MFA policy; or, for a code, you turned off
            the sign-in method it came from. Turning a method off does not stop
            the refresh of sessions it already started. `invalid_client`:
            `client_id` is not your application ID. `mfa_required`: the sign-in
            did not meet your MFA policy. Field errors name a missing or invalid
            field. A body that cannot be parsed gets a `detail` message.
          headers:
            Cache-Control:
              description: 'Always `no-store`: never cache this response.'
              schema:
                type: string
                example: no-store
            Pragma:
              description: Always `no-cache`.
              schema:
                type: string
                example: no-cache
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/OAuthError'
                  - $ref: '#/components/schemas/FieldErrors'
                  - $ref: '#/components/schemas/Detail'
              examples:
                InvalidGrant:
                  summary: Code or refresh token rejected
                  value:
                    error: invalid_grant
                InvalidClient:
                  summary: Wrong client_id
                  value:
                    error: invalid_client
                MfaRequired:
                  summary: MFA policy not met
                  value:
                    error: mfa_required
                MissingFields:
                  summary: A grant without its required fields
                  value:
                    non_field_errors:
                      - invalid_request
                UnknownGrant:
                  summary: Unsupported grant_type
                  value:
                    grant_type:
                      - '"password" is not a valid choice.'
                UnknownField:
                  summary: A field the endpoint does not accept
                  value:
                    foo: unknown_field
                MalformedBody:
                  summary: A JSON body that cannot be parsed
                  value:
                    detail: >-
                      JSON parse error - Expecting property name enclosed in
                      double quotes: line 1 column 2 (char 1)
        '403':
          $ref: '#/components/responses/OriginNotAllowed'
        '404':
          $ref: '#/components/responses/ApplicationNotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/TemporarilyUnavailable'
      security:
        - {}
components:
  schemas:
    TokenRequest:
      description: >-
        One of the two grants, chosen by `grant_type`. The endpoint accepts the
        fields of both grants and validates every field it receives, so a
        refresh that also carries a malformed `code_verifier` gets a field
        error. Send only your grant's fields; any other field gets
        `unknown_field`.
      oneOf:
        - $ref: '#/components/schemas/AuthorizationCodeGrantRequest'
        - $ref: '#/components/schemas/RefreshTokenGrantRequest'
      discriminator:
        propertyName: grant_type
        mapping:
          authorization_code: '#/components/schemas/AuthorizationCodeGrantRequest'
          refresh_token: '#/components/schemas/RefreshTokenGrantRequest'
    TokenResponse:
      type: object
      description: Tokens for one session. Replace all three together after a refresh.
      required:
        - access_token
        - id_token
        - refresh_token
        - token_type
        - expires_in
        - session_id
      properties:
        access_token:
          type: string
          description: >-
            ES256 JWT, header `typ: at+jwt`. Send it as `Authorization: Bearer
            <access_token>`. Claims: `AccessTokenClaims`.
          example: >-
            eyJhbGciOiJFUzI1NiIsImtpZCI6ImtfOHNKMmRRdlgwbkxyNVRmWWIzSGtQdyIsInR5cCI6ImF0K2p3dCJ9.eyJpc3MiOiJodHRwczovL3lvdXItYXBwLmF1dGguZGlkaXQubWUiLCJhdWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQiLCJzdWIiOiIxYzlkOGI4OC0xYmZmLTQzZDEtYWQ3YS04OGIyYmMwMDI0ZTkiLCJzaWQiOiJjOWFiZjZjYy0yZDY0LTRiZjItYjMxZS1hNGQwOWEwNzBhYjEiLCJqdGkiOiJSZ3F1NzE3OWUtbGhKMGJ2VmVELVlnIiwiaWF0IjoxNzkyMjMzNjc0LCJleHAiOjE3OTIyMzM5NzQsInZlbmRvcl9kYXRhIjoidXNlci00MiIsIm9yZyI6bnVsbCwidmVyaWZpZWQiOnt9LCJhbXIiOlsicGFzc3dvcmQiXSwiYWN0IjpudWxsLCJjbGllbnRfaWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQifQ.AxrqN8Pm-Op-3OayZk8HcvlFQZhozRiOvQ0SgQsGgrz4UNeJlUEeSlq9B8WcYUuM8naRaY5kQt_TUofnsYcyYQ
          x-didit-jwt:
            header: '#/components/schemas/AccessTokenHeader'
            claims: '#/components/schemas/AccessTokenClaims'
        id_token:
          type: string
          description: >-
            ES256 JWT, header `typ: JWT`, for your application to read who
            signed in. Never send it as an access token. Claims:
            `IDTokenClaims`.
          example: >-
            eyJhbGciOiJFUzI1NiIsImtpZCI6ImtfOHNKMmRRdlgwbkxyNVRmWWIzSGtQdyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3lvdXItYXBwLmF1dGguZGlkaXQubWUiLCJhdWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQiLCJzdWIiOiIxYzlkOGI4OC0xYmZmLTQzZDEtYWQ3YS04OGIyYmMwMDI0ZTkiLCJzaWQiOiJjOWFiZjZjYy0yZDY0LTRiZjItYjMxZS1hNGQwOWEwNzBhYjEiLCJqdGkiOiJNTHRPNHM4SHItQnJrcDd2NTlPV0Z3IiwiaWF0IjoxNzkyMjMzNjc0LCJleHAiOjE3OTIyMzM5NzQsInZlbmRvcl9kYXRhIjoidXNlci00MiIsIm9yZyI6bnVsbCwidmVyaWZpZWQiOnt9LCJhbXIiOlsicGFzc3dvcmQiXSwiYWN0IjpudWxsLCJhdXRoX3RpbWUiOjE3OTIyMzM2NzQsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJlbWFpbCI6InBlcnNvbkBleGFtcGxlLmNvbSIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.avhfDFp_aUV5_1ssYzwDcIA7H6Waf19P0bn5GCg25FhLIZBGIlwc8HiUPGj1j26r21OkP4FQnS_jM1Ho3dHtKQ
          x-didit-jwt:
            header: '#/components/schemas/IDTokenHeader'
            claims: '#/components/schemas/IDTokenClaims'
        refresh_token:
          type: string
          description: >-
            Single-use. It is valid until the session ends, and each refresh
            returns a new one.
          example: N4yZTl6y24YKYq77X_SOrBn1q2Lit81WBQMOay7dq_IVfTnxyY8ZGcJk-6-fcmWW
        token_type:
          type: string
          enum:
            - Bearer
        expires_in:
          type: integer
          minimum: 0
          maximum: 300
          description: >-
            Seconds until the access token expires: at most 300, less when the
            session ends sooner.
          example: 299
        session_id:
          type: string
          format: uuid
          description: >-
            The session these tokens belong to, the same value as the `sid`
            claim.
          example: c9abf6cc-2d64-4bf2-b31e-a4d09a070ab1
    OAuthError:
      title: OAuth error
      type: object
      description: An OAuth error (RFC 6749 section 5.2) or a service error.
      required:
        - error
      properties:
        error:
          type: string
          example: invalid_grant
    FieldErrors:
      title: Field errors
      type: object
      description: >-
        Validation errors by field. A value is a list of messages, or a single
        code (`unknown_field`, `invalid_redirect_uri`). `non_field_errors` holds
        rules across fields.
      additionalProperties:
        oneOf:
          - type: string
          - type: array
            items:
              type: string
    Detail:
      type: object
      required:
        - detail
      properties:
        detail:
          type: string
          example: invalid_token
    AuthorizationCodeGrantRequest:
      title: Authorization code grant
      type: object
      description: >-
        Exchange the authorization code from your redirect for tokens. The code
        works once, for 60 seconds.
      required:
        - grant_type
        - code
        - code_verifier
        - redirect_uri
      properties:
        grant_type:
          type: string
          enum:
            - authorization_code
        code:
          type: string
          maxLength: 128
          description: >-
            The `code` query parameter your `redirect_uri` received. Check the
            callback's `state` before you exchange it.
          example: Vb0bVt4CLlr8ZL0kE5mAxv8gS6R0rW3hYqC1pN7dJ2s
        code_verifier:
          type: string
          minLength: 43
          maxLength: 128
          pattern: ^[A-Za-z0-9._~-]{43,128}$
          description: >-
            The PKCE verifier whose S256 hash was the sign-in's
            `code_challenge`.
          example: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
        redirect_uri:
          type: string
          maxLength: 2048
          description: >-
            The exact `redirect_uri` the sign-in started with. It must still be
            one of the application's redirect URIs.
          example: https://app.example.com/auth/callback
        client_id:
          type: string
          maxLength: 64
          description: >-
            Optional. When sent, it must equal your application ID, or the
            request fails with `invalid_client`.
          example: 7b0a6c2e-3f41-4c9a-8d55-1e2f3a4b5c6d
        scope:
          type: string
          maxLength: 512
          description: >-
            Optional and accepted from standard OpenID Connect clients. It never
            widens what the sign-in granted.
          example: openid email
    RefreshTokenGrantRequest:
      title: Refresh token grant
      type: object
      description: >-
        Swap a refresh token for a new access, ID and refresh token. The refresh
        token works once: sending it again revokes the session, so run one
        refresh at a time for a session.
      required:
        - grant_type
        - refresh_token
      properties:
        grant_type:
          type: string
          enum:
            - refresh_token
        refresh_token:
          type: string
          maxLength: 128
          description: The `refresh_token` from the previous token response.
          example: N4yZTl6y24YKYq77X_SOrBn1q2Lit81WBQMOay7dq_IVfTnxyY8ZGcJk-6-fcmWW
        client_id:
          type: string
          maxLength: 64
          description: >-
            Optional. When sent, it must equal your application ID, or the
            request fails with `invalid_client`.
          example: 7b0a6c2e-3f41-4c9a-8d55-1e2f3a4b5c6d
        scope:
          type: string
          maxLength: 512
          description: >-
            Optional and accepted from standard OpenID Connect clients. A
            refresh never widens what the session holds.
          example: openid email
  responses:
    OriginNotAllowed:
      description: >-
        The request carried an `Origin` that is neither the application's own
        nor in its allowed origins. Server requests send no `Origin`.
      headers:
        Cache-Control:
          description: 'Always `no-store`: never cache this response.'
          schema:
            type: string
            example: no-store
        Pragma:
          description: Always `no-cache`.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Detail'
          examples:
            OriginNotAllowed:
              summary: Origin not allowed
              value:
                detail: origin_not_allowed
    ApplicationNotFound:
      description: >-
        The address belongs to an application, but Authentication is not active
        for it; on the `apx.didit.me` address, the application ID is unknown. An
        address that belongs to no application is refused before it reaches the
        endpoint, and that response is not JSON.
      headers:
        Cache-Control:
          description: 'Always `no-store`: never cache this response.'
          schema:
            type: string
            example: no-store
        Pragma:
          description: Always `no-cache`.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Detail'
          examples:
            ApplicationNotFound:
              summary: Unknown application
              value:
                detail: No Configuration matches the given query.
    RateLimited:
      description: >-
        Too many requests from this IP address for this endpoint, or, on a
        `POST` or `DELETE`, a bot protection bucket is empty. Nothing ran: wait
        `Retry-After` seconds, then retry.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
            example: 60
        Cache-Control:
          description: 'Always `no-store`: never cache this response.'
          schema:
            type: string
            example: no-store
        Pragma:
          description: Always `no-cache`.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Detail'
          examples:
            RateLimited:
              summary: Endpoint limit
              value:
                detail: >-
                  Too many requests from this IP address. Please wait before
                  trying again. Expected available in 60 seconds.
            BotProtection:
              summary: Bot protection limit (POST and DELETE)
              value:
                detail: rate_limited Expected available in 2 seconds.
    TemporarilyUnavailable:
      description: >-
        A service the request depends on is briefly unavailable. Retry with
        backoff.
      headers:
        Cache-Control:
          description: 'Always `no-store`: never cache this response.'
          schema:
            type: string
            example: no-store
        Pragma:
          description: Always `no-cache`.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/OAuthError'
          examples:
            TemporarilyUnavailable:
              summary: Retry later
              value:
                error: temporarily_unavailable

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.