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

# Exchange a code, device_code, or refresh_token for a credential

> One endpoint, three grants. The grant only selects HOW the exchange happens; the *credential type* is selected by the `resource` (RFC 8707) and the binding, not the grant: an `account_access` consent always yields an NVM API key; otherwise a `resource` matching this API's host yields an NVM API key and any other `resource` yields an x402 payment permission; with no `resource`, a delegation-backed binding yields an x402 permission and a plan-only binding yields an NVM API key. Device-flow polling returns a top-level `error`: authorization_pending, slow_down (add 5s to your interval), access_denied, expired_token. Honour the `interval` — this endpoint is rate-limited (~60/min per client IP for device polls, shared across concurrent ceremonies from the same address), so a real HTTP 429 (distinct from the `slow_down` grant error) is reachable under a tight poll loop.



## OpenAPI

````yaml /api-reference/oauth-openapi.json post /oauth/token
openapi: 3.0.0
info:
  title: Nevermined Authentication API
  description: >-
    The OAuth 2.1 + RFC 8628 device-flow ceremony and standards-based discovery
    endpoints an agent or connector uses to obtain and manage a Nevermined
    credential. These endpoints are root-mounted (not under /api/v1).
  version: 1.0.0
  contact: {}
servers:
  - url: https://api.sandbox.nevermined.app
    description: Sandbox
  - url: https://api.live.nevermined.app
    description: Live
security: []
tags:
  - name: Authentication - Ceremony
    x-group: Ceremony
    description: Authorization Code (PKCE) and RFC 8628 device grant.
  - name: Authentication - Connections
    x-group: Connections
    description: AgentBindings — the consent receipts a user manages.
  - name: Authentication - Discovery
    x-group: Discovery
    description: RFC 8414 / 9728 / JWKS metadata.
paths:
  /oauth/token:
    post:
      tags:
        - Authentication - Ceremony
      summary: Exchange a code, device_code, or refresh_token for a credential
      description: >-
        One endpoint, three grants. The grant only selects HOW the exchange
        happens; the *credential type* is selected by the `resource` (RFC 8707)
        and the binding, not the grant: an `account_access` consent always
        yields an NVM API key; otherwise a `resource` matching this API's host
        yields an NVM API key and any other `resource` yields an x402 payment
        permission; with no `resource`, a delegation-backed binding yields an
        x402 permission and a plan-only binding yields an NVM API key.
        Device-flow polling returns a top-level `error`: authorization_pending,
        slow_down (add 5s to your interval), access_denied, expired_token.
        Honour the `interval` — this endpoint is rate-limited (~60/min per
        client IP for device polls, shared across concurrent ceremonies from the
        same address), so a real HTTP 429 (distinct from the `slow_down` grant
        error) is reachable under a tight poll loop.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenRequest'
      responses:
        '200':
          description: The minted credential
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
        '400':
          description: >-
            Grant error (RFC 6749 / RFC 8628), e.g. authorization_pending /
            slow_down
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OAuthError'
        '429':
          description: >-
            Rate limited (HTTP-level throttle, not an RFC 8628 grant error). The
            throttler emits `Retry-After-short` / `Retry-After-long` headers
            rather than a plain `Retry-After`. Back off and resume polling.
components:
  schemas:
    TokenRequest:
      type: object
      required:
        - grant_type
        - client_id
      properties:
        grant_type:
          type: string
          enum:
            - authorization_code
            - refresh_token
            - urn:ietf:params:oauth:grant-type:device_code
          example: authorization_code
        client_id:
          type: string
          example: fleet
        code:
          type: string
          nullable: true
          description: Required for authorization_code.
        redirect_uri:
          type: string
          nullable: true
          example: cursor://oauth/callback
          description: Required for authorization_code — must match the authorize request.
        code_verifier:
          type: string
          nullable: true
          example: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
          description: PKCE verifier. Required for authorization_code.
        device_code:
          type: string
          nullable: true
          description: Required for the device_code grant.
        refresh_token:
          type: string
          nullable: true
          example: nvm_rt_Zm9vYmFy…
          description: Required for refresh_token.
        resource:
          type: string
          nullable: true
          example: https://mcp-server.example.com
          description: >-
            RFC 8707 resource (audience). SELECTS the credential type on the
            `authorization_code` and `device_code` exchanges (this API's host →
            NVM API key; any other → x402 permission). On `refresh_token` it
            does NOT select: the credential is re-derived from the binding, and
            a `resource` here is only validated against the one bound at
            authorize (mismatch → BCK.OAUTH.0005) — it cannot re-target the
            credential.
    TokenResponse:
      type: object
      properties:
        access_token:
          type: string
          example: sandbox:eyJhbGciOiJFUzI1NksifQ…
          description: >-
            The credential selected by the `resource`/binding (see the endpoint
            description), NOT by the grant: an NVM API key
            (environment-prefixed, e.g. `sandbox:…` / `live:…`, as in this
            example) or an x402 payment permission (a JWT, NOT
            environment-prefixed).
        token_type:
          type: string
          enum:
            - Bearer
          example: Bearer
        expires_in:
          type: integer
          example: 3600
        refresh_token:
          type: string
          nullable: true
          example: nvm_rt_Zm9vYmFy…
        scope:
          type: string
          nullable: true
          example: openid
    OAuthError:
      type: object
      properties:
        error:
          type: string
          enum:
            - authorization_pending
            - slow_down
            - access_denied
            - expired_token
            - invalid_grant
            - invalid_request
          example: authorization_pending
        error_description:
          type: string
          nullable: true

````

## Related topics

- [Device Flow (RFC 8628)](/docs/integrate/authentication/device-flow.md)
- [Authorization Code + PKCE](/docs/integrate/authentication/oauth-authorization-code.md)
- [RFC 8628 device authorization request](/docs/api-reference/authentication--ceremony/rfc-8628-device-authorization-request.md)
- [API error codes](/docs/development-guide/api-errors/codes.md)
- [Authentication & Identity](/docs/integrate/authentication/overview.md)
