> ## 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.

# Endpoint de token: código y renovación | API Didit

> POST /token/ de Didit intercambia un código de autorización PKCE o rota un refresh token. Reutilizar un refresh token revoca la sesión.

> [!WARNING]
>
> La autenticación es nueva. Estos endpoints los sirve el emisor de tu aplicación, como `https://<name>.auth.didit.me`, y nunca reciben tu clave de API de Didit.

Un refresh token solo funciona una vez. Si vuelves a enviar un refresh token ya usado, se revoca toda la sesión, así que ejecuta una sola renovación a la vez por sesión y reemplaza los tres tokens juntos. Consulta [Refresh tokens](https://docs.didit.me/es/authentication/tokens-and-jwks.md#tokens-de-actualización).


## OpenAPI

````yaml es/openapi-authentication.json POST /token/
openapi: 3.0.3
info:
  title: Didit Authentication API
  version: 1.0.0
  description: >-
    Los endpoints de servidor de Didit Authentication, el producto de inicio de
    sesión para los usuarios finales de tu aplicación. Authentication es
    **nuevo** y llega a producción en la versión 2026-11-01.


    ## URL base


    Cada aplicación es su propio emisor de OpenID Connect, y cada endpoint que
    aparece a continuación se sirve en la raíz de ese emisor:


    - `https://<name>.auth.didit.me` para una aplicación de producción, o
    `https://<name>.sandbox.auth.didit.me` para una aplicación de sandbox.

    - Tu propio dominio personalizado, una vez que esté activo. A partir de ese
    momento sustituye al host predeterminado como emisor: los nuevos tokens lo
    indican en `iss`, el discovery apunta a él y la clave publicable cambia. Los
    tokens de acceso emitidos con el host predeterminado fallan en `/session/`,
    `/step-up/` y `/monitoring-token/` hasta que se renuevan.


    Producción y sandbox son aplicaciones distintas, por lo que son emisores
    distintos con claves distintas. La clave publicable es `pk_live_`
    (producción) o `pk_test_` (sandbox) seguida de la codificación base64url sin
    relleno del emisor, sin barra final: decodifícala para obtener el emisor. Es
    un identificador público, no una credencial.


    Los mismos endpoints JSON también se sirven en
    `https://apx.didit.me/auth/v2/authentication/{application_id}/`. Los tokens
    siempre indican el emisor anterior, sea cual sea la dirección que los
    emitió.


    ## Credenciales


    Estos endpoints nunca aceptan tu clave de API de Didit ni un token de la
    consola.


    - El discovery, el JWKS y `POST /token/` no requieren credenciales. Los
    clientes son públicos: no hay secreto de cliente, y un código de
    autorización está vinculado a su verificador PKCE (`S256`). `client_id` es
    el ID de tu aplicación.

    - `/session/`, `/step-up/` y `/monitoring-token/` requieren el token de
    acceso de la persona: `Authorization: Bearer <access_token>`. Cada llamada
    exitosa cuenta como actividad en la sesión, por lo que reinicia el tiempo de
    inactividad de la sesión.


    ## Cuerpos de las solicitudes


    Todos los endpoints que aceptan un cuerpo admiten cuerpos JSON, codificados
    como formulario y multipart. Este documento describe JSON, además de la
    codificación como formulario para `POST /token/`, tal como la envían los
    clientes OAuth. Un campo que el endpoint no acepta recibe `400 {"<field>":
    "unknown_field"}`.


    ## Tokens


    - **Token de acceso**: un JWT ES256 con el encabezado `typ: at+jwt`, válido
    durante 5 minutos como máximo y nunca más que su sesión. Claims:
    `AccessTokenClaims`.

    - **Token de ID**: un JWT ES256 con el encabezado `typ: JWT`. Claims:
    `IDTokenClaims`. Nunca se acepta como token de acceso.

    - **Token de actualización**: una cadena aleatoria que rota en cada uso.
    Volver a enviar un token de actualización ya usado revoca la sesión.


    Verifica un token con las claves de `GET /.well-known/jwks.json`: el
    algoritmo debe ser `ES256`, `typ` debe coincidir con el tipo de token, `iss`
    debe ser igual al emisor que configuraste y `aud`, al ID de tu aplicación.
    La verificación offline solo detecta una sesión revocada cuando su token de
    acceso expira; `GET /session/` es la comprobación inmediata.


    ## Errores


    | Cuerpo | Cuándo |

    | --- | --- |

    | `{"error": "<code>"}` | Errores de `POST /token/`, todos con estado `400`,
    con la forma de cuerpo de la [sección 5.2 de RFC
    6749](https://www.rfc-editor.org/rfc/rfc6749#section-5.2): `invalid_grant`,
    `invalid_client` y `mfa_required` de Didit. Errores del servicio:
    `temporarily_unavailable`, `monitoring_unavailable`, `monitoring_disabled`.
    |

    | `{"<field>": ["<message>"]}` o `{"<field>": "<code>"}` | Un campo de la
    solicitud no superó la validación. `non_field_errors` indica una regla que
    afecta a varios campos, y `unknown_field`, un campo que el endpoint no
    acepta. |

    | `{"detail": "<message>"}` | `401` (`invalid_token`), `403`
    (`origin_not_allowed`), `404` (aplicación o step-up desconocidos) y `429`
    (límite de solicitudes). También `400` para un cuerpo que no se puede
    analizar (`JSON parse error - ...`), `405` para un método que el endpoint no
    sirve y `415` para un `Content-Type` no admitido. |


    Un `401` también incluye `WWW-Authenticate: Bearer`, y un `429` incluye
    `Retry-After` en segundos.


    Dos rechazos no son JSON. Una dirección que no es un emisor se rechaza antes
    de llegar a estos endpoints, por ejemplo con un `404` vacío para un nombre
    `*.auth.didit.me` sin usar. Un `step_up_id` que no es un UUID recibe una
    página `404`.


    ## Límites de solicitudes


    Una solicitud tiene que superar dos capas.


    **Límites por endpoint**, contados por dirección IP del cliente, por
    aplicación y por endpoint:


    - Discovery y el JWKS: 1,200 solicitudes por minuto y 12,000 por hora.

    - Todos los demás endpoints: 30 solicitudes por minuto y 300 por hora. `GET`
    y `DELETE /session/` comparten un mismo recuento.


    Al superar un límite: `429 {"detail": "Too many requests from this IP
    address. Please wait before trying again. Expected available in 60
    seconds."}`, o `3600 seconds` para el límite por hora.


    **Protección contra bots**, en cada `POST` y `DELETE`. Sus cubetas las
    comparten todas las solicitudes `POST` y `DELETE` de la aplicación,
    incluidos los pasos del inicio de sesión alojado, y se rellenan de forma
    continua:


    - 60 solicitudes por minuto por dirección IP del cliente.

    - 30 solicitudes por minuto por dispositivo, para las solicitudes que
    identifican su dispositivo, como hacen los SDK de navegador y nativos.

    - 1,000 solicitudes por minuto para toda la aplicación, sumando todas las
    direcciones IP.


    Al superar una cubeta: `429 {"detail": "rate_limited Expected available in N
    seconds."}` (`1 second.` cuando N es 1). La cubeta por IP abarca todos los
    endpoints, por lo que una misma dirección que combine llamadas de token,
    step-up y cierre de sesión puede vaciarla antes de alcanzar cualquier límite
    por endpoint. La cubeta de la aplicación cuenta todos los clientes de la
    aplicación, navegadores incluidos.


    Ambas capas responden antes de que se ejecute el endpoint, por lo que una
    solicitud que recibió `429` no se ejecutó: reinténtala después de
    `Retry-After`, incluida una renovación.


    ## Solicitudes desde el navegador


    Una solicitud que incluye un encabezado `Origin` debe proceder de uno de los
    orígenes propios de la aplicación o de sus orígenes permitidos; de lo
    contrario, recibe `403 origin_not_allowed`. Las solicitudes de servidor a
    servidor no envían `Origin`.


    ## Fuera del alcance de este documento


    Estas rutas sirven a las páginas alojadas, a los SDK de navegador y nativos,
    y a los agentes de IA. No forman parte del contrato de servidor, y los SDK
    de servidor no las llaman:


    - Pasos de inicio de sesión: `POST /authorize/`, `POST /password/`,
    `/email/*`, `/phone/*`, `/password-reset/*`, `/passkeys/*`, `/mfa/*`,
    `/social/*` y `/protection/complete/`.

    - Páginas alojadas y callbacks: `/sign-in`, `/magic`, `/invite`, `/reset`,
    `/verify/`, `/sso/*`, `/wallet/callback/`, `/logo/` y los archivos de
    asociación de apps nativas en `/.well-known/`.

    - Acceso de agentes: `/agents/*` y `/grants/`, incluido `GET
    /agents/session/`, la comprobación en tiempo real del token de acceso de un
    agente de IA. El acceso de agentes no forma parte de esta versión del
    contrato.

    - La API de gestión de la consola para Authentication, que utiliza sesiones
    de la consola.


    Los eventos de Authentication todavía no se entregan como webhooks, por lo
    que este documento no define ningún webhook.
servers:
  - url: https://{issuer}
    description: 'El emisor de tu aplicación: su origen de inicio de sesión.'
    variables:
      issuer:
        default: your-app.auth.didit.me
        description: >-
          El host del emisor: `<name>.auth.didit.me` (producción),
          `<name>.sandbox.auth.didit.me` (sandbox) o tu dominio personalizado
          activo.
security: []
tags:
  - name: Issuer
    description: Claves de descubrimiento y de firma para verificar tokens.
  - name: Tokens
    description: Las concesiones de código de autorización y de actualización.
  - name: Sessions
    description: Comprueba y finaliza la sesión asociada a un token de acceso.
  - name: Step-ups
    description: Verificación antes de una acción sensible.
  - name: Monitoring
    description: >-
      Credenciales del dispositivo para los eventos de monitorización de
      transacciones.
paths:
  /token/:
    post:
      tags:
        - Tokens
      summary: Intercambiar un código o renovar tokens
      description: >-
        El endpoint de token, para ambos tipos de concesión.


        - `authorization_code` canjea el código que recibió tu `redirect_uri`
        por un token de acceso, un token de ID y un token de actualización. El
        código funciona una sola vez, en un plazo de 60 segundos, con el
        verificador PKCE y la `redirect_uri` exacta de su inicio de sesión.
        Cuando la aplicación permite una sola sesión por persona, el canje
        cierra las demás sesiones de esa persona.

        - `refresh_token` devuelve un nuevo token de acceso, token de ID y token
        de actualización, y el token de actualización que enviaste deja de
        funcionar. **Volver a enviar un token de actualización ya usado revoca
        la sesión** y devuelve `invalid_grant`, así que ejecuta una sola
        actualización a la vez por sesión y reemplaza los tres tokens juntos. Si
        una actualización falla con `400` o no recibe respuesta, vuelve a
        iniciar sesión en lugar de reintentar: un reintento después de una
        actualización que sí se completó cuenta como reutilización. Un `429` se
        responde antes de que se ejecute la actualización, así que es seguro
        reintentar después de `Retry-After`. Un `503` significa que la
        actualización no se realizó y que el token de actualización sigue
        funcionando: reinténtala con espera exponencial.


        El cuerpo puede ser JSON o codificado como formulario.
      operationId: authentication_token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenRequest'
            examples:
              AuthorizationCode:
                summary: Intercambiar el código de autorización
                value:
                  grant_type: authorization_code
                  code: Vb0bVt4CLlr8ZL0kE5mAxv8gS6R0rW3hYqC1pN7dJ2s
                  code_verifier: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
                  redirect_uri: https://app.example.com/auth/callback
              RefreshToken:
                summary: Renovar los 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: Nuevos tokens para la sesión.
          headers:
            Cache-Control:
              description: 'Siempre `no-store`: nunca almacenes en caché esta respuesta.'
              schema:
                type: string
                example: no-store
            Pragma:
              description: Siempre `no-cache`.
              schema:
                type: string
                example: no-cache
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
              examples:
                Tokens:
                  summary: Tokens para una sesión nueva
                  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: >-
            La concesión falló. `invalid_grant`: el código o el token de
            actualización es desconocido, ya se usó o expiró; el verificador
            PKCE o el `redirect_uri` no coincide, o el `redirect_uri` ya no está
            registrado; la persona fue bloqueada o su correo electrónico ya no
            está verificado; la sesión terminó o ya no cumple tu política de
            MFA; o, en el caso de un código, desactivaste el método de inicio de
            sesión del que procede. Desactivar un método no detiene la
            actualización de las sesiones que ya inició. `invalid_client`: el
            `client_id` no es el ID de tu aplicación. `mfa_required`: el inicio
            de sesión no cumplió tu política de MFA. Los errores de campo
            indican un campo que falta o no es válido. Un cuerpo que no se puede
            analizar recibe un mensaje `detail`.
          headers:
            Cache-Control:
              description: 'Siempre `no-store`: nunca almacenes en caché esta respuesta.'
              schema:
                type: string
                example: no-store
            Pragma:
              description: Siempre `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: Código o token de actualización rechazado
                  value:
                    error: invalid_grant
                InvalidClient:
                  summary: client_id incorrecto
                  value:
                    error: invalid_client
                MfaRequired:
                  summary: No se cumple la política de MFA
                  value:
                    error: mfa_required
                MissingFields:
                  summary: Una concesión sin sus campos obligatorios
                  value:
                    non_field_errors:
                      - invalid_request
                UnknownGrant:
                  summary: grant_type no admitido
                  value:
                    grant_type:
                      - '"password" is not a valid choice.'
                UnknownField:
                  summary: Un campo que el endpoint no acepta
                  value:
                    foo: unknown_field
                MalformedBody:
                  summary: Un cuerpo JSON que no se puede analizar
                  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: >-
        Una de las dos concesiones (grants), elegida según `grant_type`. El
        endpoint acepta los campos de ambas concesiones y valida todos los
        campos que recibe, por lo que una renovación (refresh) que además
        incluya un `code_verifier` mal formado recibe un error de campo. Envía
        solo los campos de tu concesión; cualquier otro campo recibe
        `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 de una sesión. Sustituye los tres a la vez después de una
        renovación.
      required:
        - access_token
        - id_token
        - refresh_token
        - token_type
        - expires_in
        - session_id
      properties:
        access_token:
          type: string
          description: >-
            JWT ES256, con el encabezado `typ: at+jwt`. Envíalo como
            `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: >-
            JWT ES256, con el encabezado `typ: JWT`, para que tu aplicación sepa
            quién inició sesión. Nunca lo envíes como token de acceso. 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: >-
            De un solo uso. Es válido hasta que finaliza la sesión, y cada
            actualización devuelve uno nuevo.
          example: N4yZTl6y24YKYq77X_SOrBn1q2Lit81WBQMOay7dq_IVfTnxyY8ZGcJk-6-fcmWW
        token_type:
          type: string
          enum:
            - Bearer
        expires_in:
          type: integer
          minimum: 0
          maximum: 300
          description: >-
            Segundos hasta que caduca el token de acceso: como máximo 300, menos
            si la sesión termina antes.
          example: 299
        session_id:
          type: string
          format: uuid
          description: >-
            La sesión a la que pertenecen estos tokens, el mismo valor que el
            claim `sid`.
          example: c9abf6cc-2d64-4bf2-b31e-a4d09a070ab1
    OAuthError:
      title: OAuth error
      type: object
      description: Un error de OAuth (RFC 6749, sección 5.2) o un error del servicio.
      required:
        - error
      properties:
        error:
          type: string
          example: invalid_grant
    FieldErrors:
      title: Field errors
      type: object
      description: >-
        Errores de validación por campo. Un valor es una lista de mensajes o un
        único código (`unknown_field`, `invalid_redirect_uri`).
        `non_field_errors` contiene las reglas que afectan a varios campos.
      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: >-
        Intercambia por tokens el código de autorización recibido en tu
        redirección. El código funciona una sola vez, durante 60 segundos.
      required:
        - grant_type
        - code
        - code_verifier
        - redirect_uri
      properties:
        grant_type:
          type: string
          enum:
            - authorization_code
        code:
          type: string
          maxLength: 128
          description: >-
            El parámetro de consulta `code` que recibió tu `redirect_uri`.
            Comprueba el `state` del callback antes de canjearlo.
          example: Vb0bVt4CLlr8ZL0kE5mAxv8gS6R0rW3hYqC1pN7dJ2s
        code_verifier:
          type: string
          minLength: 43
          maxLength: 128
          pattern: ^[A-Za-z0-9._~-]{43,128}$
          description: >-
            El verificador PKCE cuyo hash S256 fue el `code_challenge` del
            inicio de sesión.
          example: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
        redirect_uri:
          type: string
          maxLength: 2048
          description: >-
            El `redirect_uri` exacto con el que empezó el inicio de sesión. Debe
            seguir siendo una de las URI de redirección de la aplicación.
          example: https://app.example.com/auth/callback
        client_id:
          type: string
          maxLength: 64
          description: >-
            Opcional. Si se envía, debe ser igual al ID de tu aplicación; de lo
            contrario, la solicitud falla con `invalid_client`.
          example: 7b0a6c2e-3f41-4c9a-8d55-1e2f3a4b5c6d
        scope:
          type: string
          maxLength: 512
          description: >-
            Opcional; se acepta de clientes OpenID Connect estándar. Nunca
            amplía lo que concedió el inicio de sesión.
          example: openid email
    RefreshTokenGrantRequest:
      title: Refresh token grant
      type: object
      description: >-
        Intercambia un token de actualización por un nuevo token de acceso, de
        ID y de actualización. El token de actualización solo funciona una vez:
        si lo vuelves a enviar, se revoca la sesión, así que ejecuta una sola
        actualización a la vez por sesión.
      required:
        - grant_type
        - refresh_token
      properties:
        grant_type:
          type: string
          enum:
            - refresh_token
        refresh_token:
          type: string
          maxLength: 128
          description: El `refresh_token` de la respuesta de token anterior.
          example: N4yZTl6y24YKYq77X_SOrBn1q2Lit81WBQMOay7dq_IVfTnxyY8ZGcJk-6-fcmWW
        client_id:
          type: string
          maxLength: 64
          description: >-
            Opcional. Si se envía, debe ser igual al ID de tu aplicación; de lo
            contrario, la solicitud falla con `invalid_client`.
          example: 7b0a6c2e-3f41-4c9a-8d55-1e2f3a4b5c6d
        scope:
          type: string
          maxLength: 512
          description: >-
            Opcional; se acepta de clientes OpenID Connect estándar. Una
            renovación nunca amplía lo que contiene la sesión.
          example: openid email
  responses:
    OriginNotAllowed:
      description: >-
        La solicitud incluía un `Origin` que no es el de la propia aplicación ni
        está entre sus orígenes permitidos. Las solicitudes de servidor no
        envían `Origin`.
      headers:
        Cache-Control:
          description: 'Siempre `no-store`: nunca almacenes en caché esta respuesta.'
          schema:
            type: string
            example: no-store
        Pragma:
          description: Siempre `no-cache`.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Detail'
          examples:
            OriginNotAllowed:
              summary: Origen no permitido
              value:
                detail: origin_not_allowed
    ApplicationNotFound:
      description: >-
        La dirección pertenece a una aplicación, pero Authentication no está
        activa para ella; en la dirección `apx.didit.me`, el ID de aplicación es
        desconocido. Una dirección que no pertenece a ninguna aplicación se
        rechaza antes de llegar al endpoint, y esa respuesta no es JSON.
      headers:
        Cache-Control:
          description: 'Siempre `no-store`: nunca almacenes en caché esta respuesta.'
          schema:
            type: string
            example: no-store
        Pragma:
          description: Siempre `no-cache`.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Detail'
          examples:
            ApplicationNotFound:
              summary: Aplicación desconocida
              value:
                detail: No Configuration matches the given query.
    RateLimited:
      description: >-
        Demasiadas solicitudes desde esta dirección IP para este endpoint o, en
        un `POST` o un `DELETE`, un bucket de protección contra bots está vacío.
        No se ejecutó nada: espera `Retry-After` segundos y vuelve a intentarlo.
      headers:
        Retry-After:
          description: Segundos que hay que esperar antes de reintentar.
          schema:
            type: integer
            example: 60
        Cache-Control:
          description: 'Siempre `no-store`: nunca almacenes en caché esta respuesta.'
          schema:
            type: string
            example: no-store
        Pragma:
          description: Siempre `no-cache`.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Detail'
          examples:
            RateLimited:
              summary: Límite del endpoint
              value:
                detail: >-
                  Too many requests from this IP address. Please wait before
                  trying again. Expected available in 60 seconds.
            BotProtection:
              summary: Límite de protección contra bots (POST y DELETE)
              value:
                detail: rate_limited Expected available in 2 seconds.
    TemporarilyUnavailable:
      description: >-
        Un servicio del que depende la solicitud no está disponible
        momentáneamente. Reintenta con backoff.
      headers:
        Cache-Control:
          description: 'Siempre `no-store`: nunca almacenes en caché esta respuesta.'
          schema:
            type: string
            example: no-store
        Pragma:
          description: Siempre `no-cache`.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/OAuthError'
          examples:
            TemporarilyUnavailable:
              summary: Reintentar más tarde
              value:
                error: temporarily_unavailable

````

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