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

> ## Agent Instructions
> The OpenAPI document at /api-reference/openapi.yaml defines the target contract. Its x-reflections-availability field and each operation's availability notice identify supported test and live forms. Do not infer implementation from a schema or code sample. Live keys support account details, reads of the licenses the calling account holds as licensee or issued as talent (GET /v1/licenses?role=issuer), and GET /v1/generations?license=… for the state of outputs under such a license (summaries only: no prompt, reference, bytes or metadata). Live image checks, submissions and receipts are built but not activated: until activation, a live POST /v1/checks or /v1/generations returns 403 live_not_activated, which must not be retried. Discovery and JWKS are at the origin root (https://app.reflections.ai/.well-known/...), not under /v1.
> Three rules hold everywhere: a decision is never an error (deny and escalate are 200 responses, and a live check still evaluating is a 202 with status checking); generate nothing for a named talent without an allow; deliver nothing until the generation is allow.
> The sandbox accepts personal rk_test_ keys. Persist a separate Idempotency-Key and body for each logical /v1 POST; retry an unknown outcome with the same key and body. OAuth and Talent API MCP remain planned.

# Create an access token

> **Planned contract — unavailable in test and live modes.** This operation is a protocol reference, not a callable endpoint. Start with the [working sandbox quickstart](/quickstart).

Exchanges a grant for an access token, as OAuth 2.0 (RFC 6749) defines. The body is
form-encoded.

- `authorization_code`: an MCP client redeems the code from the
  [authorization page](/api-reference/oauth/authorize-a-client) with its `code_verifier`.
  It receives a `Bearer` access token for the MCP server, valid for one hour, and a
  refresh token.
- `refresh_token`: exchanges a refresh token for a new access token and a new refresh
  token. The old refresh token stops working. Send `scope` to narrow the new token.
- `client_credentials`: an [RSL](/standard/interop/rsl) client requests a `License`
  token for a talent's facet, sending the RSL `<license>` it wants as `license` and the
  facet's IRI as `resource`. Reflections issues it only when your account holds a license
  that covers the usage, or the talent's posture allows the usage without one. A `License`
  token is not an authorization to generate: check the use with
  [`POST /v1/checks`](/api-reference/checks/create-a-check) before you generate.

A code issued to a platform's `client_id` is refused here with `unauthorized_client`:
platforms redeem codes at [`POST /v1/connections`](/api-reference/connections/create-a-connection)
and act with their secret key, not with access tokens.

The token endpoint takes no key. A public client, such as a desktop agent, sends its
`client_id` in the body. A confidential client authenticates with its client ID and
secret, and `client_credentials` needs one. Errors are OAuth errors (RFC 6749 §5.2), not
problem details; `429` and `5xx` responses are problem details, as everywhere else.




## OpenAPI

````yaml /api-reference/openapi.yaml post /oauth/token
openapi: 3.1.0
info:
  title: Reflections Talent API
  version: '2026-10-15'
  summary: The license server for a person's name, likeness and voice in generative AI.
  description: >
    This document defines the target Talent API contract. Each operation
    declares its current

    test and live availability. Test mode serves a limited fictional sandbox.
    Live keys support

    account details and reading the caller's own licenses. The initial live
    image check,

    submission and receipt implementation is deployed, but requires separate
    live signing

    activation and production acceptance. Start at
    https://docs.reflections.ai/quickstart for test mode.


    The target API lets any product that can generate a real person check
    whether a use is licensed,

    verify the output it made, prove that verification to anyone downstream,
    report usage, and

    honor revocations.


    Protected requests use a secret key in the `Authorization` header. The
    target contract defines test keys

    (`rk_test_…`) and live keys (`rk_live_…`) use the same base URL; the key
    decides the mode.


    A decision is never an error. `deny` and `escalate` are `200` responses that
    carry a decision.

    Errors use RFC 9457 problem details.
  contact:
    name: Reflections developer support
    url: https://docs.reflections.ai/production/support
    email: developers@reflections.ai
servers:
  - url: https://app.reflections.ai/v1
    description: >-
      Fictional test sandbox and conditional live support; availability and
      activation are separate
security:
  - SecretKey: []
tags:
  - name: Account
    description: The account that owns the calling key.
  - name: Talent
    description: >-
      People whose name, likeness and voice are protected and licensed through
      Reflections.
  - name: Facets
    description: >-
      The parts of a talent's identity a license can cover, such as a voice or a
      likeness.
  - name: Resolutions
    description: Find which talent a prompt, image or voice sample invokes.
  - name: Licenses
    description: Signed licenses and their revisions.
  - name: License requests
    description: Ask a talent for a new license or a wider one.
  - name: Connections
    description: A licensee's permission for a platform to use its licenses.
  - name: Files
    description: References and outputs uploaded for checks and generations.
  - name: Checks
    description: Decide whether a use may proceed, before anything is generated.
  - name: Generations
    description: Check an output against the license its check relied on.
  - name: Sessions
    description: Authorize live voice and video with short-lived tokens and sampled checks.
  - name: Receipts
    description: Signed, portable proof of a decision.
  - name: Verifications
    description: Verify a file, manifest or receipt.
  - name: Revocations
    description: Every narrowing of consent, and the platform's acknowledgement of it.
  - name: Usage records
    description: Report usage for settlement and audit.
  - name: Statements
    description: Monthly amounts owed under a license, reconciled against observed usage.
  - name: Mandates
    description: A representative's authority to act for a talent, and its powers.
  - name: Escalations
    description: >-
      The review queue. Checks and held outputs waiting for the talent's side to
      decide.
  - name: License drafts
    description: Terms prepared for a talent's signature. Only a person signs.
  - name: Webhook endpoints
    description: Where Reflections sends events.
  - name: Events
    description: The record of everything that happened to your objects.
  - name: Test helpers
    description: Act as a talent or reviewer in test mode.
  - name: Discovery
    description: The public discovery document and signing keys. Neither needs a key.
  - name: OAuth
    description: >
      Authorization for connections, MCP clients and RSL clients, and the
      metadata that describes it.
paths:
  /oauth/token:
    post:
      tags:
        - OAuth
      summary: Create an access token
      description: >
        **Planned contract — unavailable in test and live modes.** This
        operation is a protocol reference, not a callable endpoint. Start with
        the [working sandbox quickstart](/quickstart).


        Exchanges a grant for an access token, as OAuth 2.0 (RFC 6749) defines.
        The body is

        form-encoded.


        - `authorization_code`: an MCP client redeems the code from the
          [authorization page](/api-reference/oauth/authorize-a-client) with its `code_verifier`.
          It receives a `Bearer` access token for the MCP server, valid for one hour, and a
          refresh token.
        - `refresh_token`: exchanges a refresh token for a new access token and
        a new refresh
          token. The old refresh token stops working. Send `scope` to narrow the new token.
        - `client_credentials`: an [RSL](/standard/interop/rsl) client requests
        a `License`
          token for a talent's facet, sending the RSL `<license>` it wants as `license` and the
          facet's IRI as `resource`. Reflections issues it only when your account holds a license
          that covers the usage, or the talent's posture allows the usage without one. A `License`
          token is not an authorization to generate: check the use with
          [`POST /v1/checks`](/api-reference/checks/create-a-check) before you generate.

        A code issued to a platform's `client_id` is refused here with
        `unauthorized_client`:

        platforms redeem codes at [`POST
        /v1/connections`](/api-reference/connections/create-a-connection)

        and act with their secret key, not with access tokens.


        The token endpoint takes no key. A public client, such as a desktop
        agent, sends its

        `client_id` in the body. A confidential client authenticates with its
        client ID and

        secret, and `client_credentials` needs one. Errors are OAuth errors (RFC
        6749 §5.2), not

        problem details; `429` and `5xx` responses are problem details, as
        everywhere else.
      operationId: createAccessToken
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/AccessTokenCreate'
            examples:
              authorization_code:
                summary: An MCP client redeems a code
                value:
                  grant_type: authorization_code
                  code: ac_8Xgcqs0ZRUe0M57eyADP9yT4
                  redirect_uri: http://127.0.0.1:33418/oauth/callback
                  code_verifier: 6l_CpK7wrBmjR_geDznNZuKR9du8tNl4p8wxTb6jJ9o
                  client_id: cl_ra8t5tsberxpax0351jdyqgrb7
                  resource: https://app.reflections.ai/mcp
              refresh_token:
                summary: An MCP client refreshes its token
                value:
                  grant_type: refresh_token
                  refresh_token: rrt_0so8SAbeT5SQ2Zn97lnj_3usT_hvHXda
                  client_id: cl_ra8t5tsberxpax0351jdyqgrb7
                  resource: https://app.reflections.ai/mcp
              rsl_license:
                summary: An RSL client requests a License token
                value:
                  grant_type: client_credentials
                  resource: https://app.reflections.ai/fct_5zba2q8v1m3n7p4r6s9t0wxyhk
                  license: >-
                    <license><permits type="usage">ai-input</permits><permits
                    type="user">commercial</permits><permits type="geo">US
                    CA</permits></license>
      responses:
        '200':
          description: The access token.
          headers:
            Request-Id:
              $ref: '#/components/headers/RequestId'
            Cache-Control:
              description: Always `no-store`.
              schema:
                type: string
                example: no-store
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccessToken'
              examples:
                bearer:
                  summary: A token for the MCP server
                  value:
                    access_token: rat_x6U8znpCLsAr4BolEF6a8WP4tYxIDW6A
                    token_type: Bearer
                    expires_in: 3600
                    refresh_token: rrt_Hq2Lw7Vb4Nc9Xk1Mz6Tr3Ys8Pd5Gf0Aj
                    scope: talent:read licenses:read checks:write generations:write
                    livemode: false
                license:
                  summary: An RSL License token
                  value:
                    access_token: rlt_vSJ3bKIxXFSmOQpwEb0SZrAhc4eOBrwV
                    token_type: License
                    expires_in: 86400
                    livemode: true
        '400':
          description: >
            The grant is invalid. `error` is `invalid_request` (a missing or
            repeated parameter),

            `invalid_grant` (an unknown, expired or used code, a `code_verifier`
            or `redirect_uri`

            that does not match, a revoked refresh token, or an RSL usage no
            license or posture

            covers), `unauthorized_client` (a platform's code, or a grant the
            client did not

            register), `unsupported_grant_type`, `invalid_scope` or
            `invalid_target` (a `resource`

            this server does not serve).
          headers:
            Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OAuthError'
              examples:
                invalid_grant:
                  summary: The code was already used
                  value:
                    error: invalid_grant
                    error_description: >-
                      The authorization code was already used. Send the person
                      through the authorization page again.
                platform_code:
                  summary: A platform's code
                  value:
                    error: unauthorized_client
                    error_description: >-
                      Codes issued to a platform are redeemed at POST
                      /v1/connections.
        '401':
          description: The client ID or secret is wrong. `error` is `invalid_client`.
          headers:
            Request-Id:
              $ref: '#/components/headers/RequestId'
            WWW-Authenticate:
              description: '`Basic realm="reflections"`.'
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OAuthError'
              example:
                error: invalid_client
                error_description: Client authentication failed.
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
        '503':
          $ref: '#/components/responses/Unavailable'
      security:
        - {}
        - ClientCredentials: []
      servers:
        - url: https://app.reflections.ai
      x-codeSamples:
        - lang: bash
          label: cURL
          source: >
            curl --fail-with-body --max-time 30 -X POST
            'https://app.reflections.ai/oauth/token' \
              -H "Content-Type: application/x-www-form-urlencoded" \
              --data-urlencode 'grant_type=authorization_code' \
              --data-urlencode 'code=ac_8Xgcqs0ZRUe0M57eyADP9yT4' \
              --data-urlencode 'redirect_uri=http://127.0.0.1:33418/oauth/callback' \
              --data-urlencode 'code_verifier=6l_CpK7wrBmjR_geDznNZuKR9du8tNl4p8wxTb6jJ9o' \
              --data-urlencode 'client_id=cl_ra8t5tsberxpax0351jdyqgrb7' \
              --data-urlencode 'resource=https://app.reflections.ai/mcp'
        - lang: typescript
          label: TypeScript
          source: >
            import { exit } from "node:process";


            const response = await
            fetch("https://app.reflections.ai/oauth/token", {
              method: "POST",
              headers: {
                "Content-Type": "application/x-www-form-urlencoded",
              },
              body: new URLSearchParams({
                "grant_type": "authorization_code",
                "code": "ac_8Xgcqs0ZRUe0M57eyADP9yT4",
                "redirect_uri": "http://127.0.0.1:33418/oauth/callback",
                "code_verifier": "6l_CpK7wrBmjR_geDznNZuKR9du8tNl4p8wxTb6jJ9o",
                "client_id": "cl_ra8t5tsberxpax0351jdyqgrb7",
                "resource": "https://app.reflections.ai/mcp"
              }),
              signal: AbortSignal.timeout(30_000),
            });

            const responseBody = await response.text();

            if (!response.ok) {
              console.error(`HTTP ${response.status}: ${responseBody}`);
              exit(1);
            }

            console.log(responseBody);
        - lang: python
          label: Python
          source: |
            from urllib.error import HTTPError
            from urllib.parse import urlencode
            from urllib.request import Request, urlopen

            headers = {
                "Content-Type": "application/x-www-form-urlencoded",
            }
            data = urlencode({
                "grant_type": "authorization_code",
                "code": "ac_8Xgcqs0ZRUe0M57eyADP9yT4",
                "redirect_uri": "http://127.0.0.1:33418/oauth/callback",
                "code_verifier": "6l_CpK7wrBmjR_geDznNZuKR9du8tNl4p8wxTb6jJ9o",
                "client_id": "cl_ra8t5tsberxpax0351jdyqgrb7",
                "resource": "https://app.reflections.ai/mcp",
            }).encode("utf-8")
            request = Request("https://app.reflections.ai/oauth/token",
                              method="POST", headers=headers, data=data)
            try:
                with urlopen(request, timeout=30) as response:
                    print(response.read().decode("utf-8"))
            except HTTPError as error:
                error_body = error.read().decode("utf-8")
                raise RuntimeError(f"HTTP {error.code}: {error_body}") from error
        - lang: go
          label: Go
          source: |
            package main

            import (
                "fmt"
                "io"
                "net/http"
                "net/url"
                "strings"
                "time"
            )

            func main() {
                form := url.Values{
                    "grant_type": {"authorization_code"},
                    "code": {"ac_8Xgcqs0ZRUe0M57eyADP9yT4"},
                    "redirect_uri": {"http://127.0.0.1:33418/oauth/callback"},
                    "code_verifier": {"6l_CpK7wrBmjR_geDznNZuKR9du8tNl4p8wxTb6jJ9o"},
                    "client_id": {"cl_ra8t5tsberxpax0351jdyqgrb7"},
                    "resource": {"https://app.reflections.ai/mcp"},
                }
                body := strings.NewReader(form.Encode())
                request, err := http.NewRequest("POST", "https://app.reflections.ai/oauth/token", body)
                if err != nil {
                    panic(err)
                }
                request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
                client := &http.Client{Timeout: 30 * time.Second}
                response, err := client.Do(request)
                if err != nil {
                    panic(err)
                }
                defer response.Body.Close()
                responseBody, err := io.ReadAll(response.Body)
                if err != nil {
                    panic(err)
                }
                if response.StatusCode < 200 || response.StatusCode >= 300 {
                    panic(fmt.Sprintf("HTTP %d: %s", response.StatusCode, responseBody))
                }
                fmt.Print(string(responseBody))
            }
components:
  schemas:
    AccessTokenCreate:
      description: |
        A token request. Its fields follow from `grant_type`.
      oneOf:
        - title: Authorization code
          type: object
          description: An MCP client redeems a code from the authorization page.
          required:
            - grant_type
            - code
            - redirect_uri
            - code_verifier
          properties:
            grant_type:
              type: string
              enum:
                - authorization_code
              description: '`authorization_code`.'
            code:
              type: string
              description: The `code` the redirect carried.
            redirect_uri:
              type: string
              format: uri
              description: The `redirect_uri` sent to the authorization page.
            code_verifier:
              type: string
              minLength: 43
              maxLength: 128
              description: The PKCE verifier whose S256 hash was the `code_challenge`.
            client_id:
              type: string
              description: >-
                The client's ID. Required for a public client, which sends no
                secret.
            resource:
              type: string
              format: uri
              description: >-
                The resource the token is for, as RFC 8707 defines,
                `https://app.reflections.ai/mcp`.
        - title: Refresh token
          type: object
          description: >-
            A client exchanges a refresh token for a new access token and
            refresh token.
          required:
            - grant_type
            - refresh_token
          properties:
            grant_type:
              type: string
              enum:
                - refresh_token
              description: '`refresh_token`.'
            refresh_token:
              type: string
              description: The refresh token to exchange.
            client_id:
              type: string
              description: >-
                The client's ID. Required for a public client, which sends no
                secret.
            scope:
              type: string
              description: >
                Space-separated scopes to narrow the new token to. Each must be
                one the person

                approved.
            resource:
              type: string
              format: uri
              description: The resource the token is for, `https://app.reflections.ai/mcp`.
        - title: RSL license token
          type: object
          description: >-
            A confidential RSL client requests a `License` token for a talent's
            facet.
          required:
            - grant_type
            - resource
            - license
          properties:
            grant_type:
              type: string
              enum:
                - client_credentials
              description: '`client_credentials`.'
            resource:
              type: string
              format: uri
              description: >-
                The `url` of the RSL `<content>` element, which is the facet's
                IRI.
            license:
              type: string
              description: >
                The complete RSL `<license>` element you request, as XML. It
                must be one the

                talent's RSL document offers for `resource`.
    AccessToken:
      type: object
      description: An access token, as RFC 6749 returns it.
      required:
        - access_token
        - token_type
        - expires_in
        - livemode
      properties:
        access_token:
          type: string
          description: >
            Send a `Bearer` token to the MCP server as `Authorization: Bearer
            <token>`. Send a

            `License` token to the content host as `Authorization: License
            <token>`.
        token_type:
          type: string
          enum:
            - Bearer
            - License
          description: '`Bearer` for the MCP server. `License` for an RSL license token.'
        expires_in:
          type: integer
          description: >
            Seconds until the access token expires: 3600 for `Bearer`. For
            `License`, 86400, or less

            when the license it draws on ends sooner.
        refresh_token:
          type: string
          description: >
            For `authorization_code` and `refresh_token` grants. Each use
            returns a new refresh token

            and ends the old one. A refresh token expires after 30 days unused,
            or when the person

            removes the client.
        scope:
          type: string
          description: >-
            The space-separated scopes the token carries, which the person
            approved. Absent for `License` tokens.
        livemode:
          type: boolean
          description: >
            `true` when the token acts in live mode. For `Bearer` tokens, the
            person chose the mode on

            the authorization page.
    OAuthError:
      type: object
      description: >
        An OAuth error, as RFC 6749 §5.2 and RFC 7591 §3.2.2 define it. The
        OAuth endpoints return

        it for `400` and `401`.
      required:
        - error
      properties:
        error:
          type: string
          enum:
            - invalid_request
            - invalid_client
            - invalid_grant
            - unauthorized_client
            - unsupported_grant_type
            - invalid_scope
            - invalid_target
            - invalid_redirect_uri
            - invalid_client_metadata
          description: The error code. Each endpoint lists the codes it returns.
        error_description:
          type: string
          description: >-
            A sentence for the developer. Do not show it to the person signing
            in.
    Problem:
      type: object
      description: An RFC 9457 problem detail. Every error has this shape.
      required:
        - type
        - title
        - status
        - code
        - request_id
      properties:
        type:
          type: string
          format: uri
          description: A URL for this error, under `https://docs.reflections.ai/errors/`.
        title:
          type: string
          description: A short summary of the error.
        status:
          type: integer
          description: The HTTP status.
        detail:
          type: string
          description: What happened in this request.
        code:
          $ref: '#/components/schemas/ErrorCode'
          description: The stable error code. Branch on it, then on `status`.
        param:
          type: string
          description: The field at fault, as a path such as `use.territories[1]`.
        request_id:
          type: string
          description: >
            `req_…`, the same value as the `Request-Id` header. Include it when
            you contact support.
        doc_url:
          type: string
          format: uri
          description: A link to the documentation for this error.
    ErrorCode:
      type: string
      description: A stable error code. See the error code catalog.
      enum:
        - invalid_request
        - parameter_missing
        - parameter_invalid
        - unsupported_modality
        - unsupported_media_type
        - media_unreadable
        - version_unsupported
        - authorization_code_invalid
        - authentication_required
        - invalid_api_key
        - token_invalid
        - insufficient_scope
        - account_not_connected
        - mandate_required
        - livemode_mismatch
        - live_not_activated
        - platform_not_permitted
        - resource_not_found
        - idempotency_key_in_use
        - generation_already_submitted
        - authorization_consumed
        - check_not_allowed
        - check_expired
        - escalation_closed
        - license_draft_closed
        - invalid_state_transition
        - session_not_active
        - payload_too_large
        - idempotency_key_reused
        - artifact_digest_mismatch
        - rate_limited
        - internal_error
        - service_unavailable
        - evaluation_timeout
  headers:
    RequestId:
      description: Identifies this request. Include it when you contact support.
      schema:
        type: string
        example: req_8f2k4m6p8r0t2v4x6z8b0d2f4h
    RetryAfter:
      description: Seconds to wait before retrying.
      schema:
        type: integer
  responses:
    RateLimited:
      description: Too many requests.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          example:
            type: https://docs.reflections.ai/errors/rate_limited
            title: Too many requests
            status: 429
            code: rate_limited
            request_id: req_8f2k4m6p8r0t2v4x6z8b0d2f4h
    ServerError:
      description: Something failed on our side. Retry with the same idempotency key.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    Unavailable:
      description: >
        Reflections is temporarily unavailable. Do not generate a named talent
        without a valid

        authorization. Retry with the same idempotency key.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
  securitySchemes:
    SecretKey:
      type: http
      scheme: bearer
      description: >
        Your secret key, `rk_test_…` or `rk_live_…`, sent as `Authorization:
        Bearer <key>`. Keep it

        on your server. Restricted keys carry only the scopes you give them.
    ClientCredentials:
      type: http
      scheme: basic
      description: >
        A confidential OAuth client's `client_id` and `client_secret`, sent as
        HTTP Basic

        credentials. Register the client with your secret key to receive them;
        see

        [`POST /oauth/register`](/api-reference/oauth/register-a-client).

````

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