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

# OAuth token endpoint

> Exchanges an authorization code or a refresh token for tokens, accepting either
application/x-www-form-urlencoded or application/json. For
grant_type=authorization_code it verifies PKCE (S256 code_verifier against the
stored challenge) and that client_id and redirect_uri match the original
authorization; for grant_type=refresh_token it rotates the presented refresh token,
carrying the original scopes forward, and detects reuse of an already-rotated token
by revoking the entire token family. Confidential clients must authenticate with
client_secret; public clients rely on PKCE. Returns an opaque short-lived Bearer
access_token (expires_in seconds), a new rotating refresh_token, the token_type, and
the space-delimited granted scope.

## Form-encoded token exchange (OAuth 2.1)

Exchanges an authorization `code` for tokens, or rotates a refresh token. The request body is **`application/x-www-form-urlencoded`** (per RFC 6749), not JSON.

* `grant_type=authorization_code` — send `code`, `redirect_uri`, `client_id`, and the PKCE `code_verifier` matching the `code_challenge` from the authorize step.
* `grant_type=refresh_token` — send `refresh_token`. Refresh tokens **rotate**: each use returns a new one and invalidates the old (reuse is detected and revokes the chain).

Returns a short-lived opaque **access token** (use it as `Authorization: Bearer ...`), a rotating **refresh token**, `token_type`, and `expires_in`. Access tokens last \~1 hour; refresh tokens \~30 days.


## OpenAPI

````yaml POST /v1/oauth/token
openapi: 3.1.0
info:
  title: HQ API
  description: >-
    Public HTTP API for HQ. Authenticate with a Personal Access Token
    (`Authorization: Bearer hq_pat_...`) for server-side integrations, or an
    OAuth 2.1 authorization-code + PKCE flow for browser apps acting on a user's
    behalf. Both grant from the same resource:action scope vocabulary; an
    endpoint's required scope is listed under its `security`.
  license:
    name: Apache-2.0
    identifier: Apache-2.0
  version: 1.0.0
servers:
  - url: https://api.hq.zone
    description: HQ API (production)
security: []
tags:
  - name: me
    description: The signed-in user's own account
  - name: conversations
    description: Conversations and their messages
  - name: documents
    description: The content-addressed documents library
  - name: schedules
    description: Scheduled prompts and recurring tasks
  - name: agents
    description: Agents, their skills and integrations
  - name: memory
    description: What the assistant remembers (L5 governance)
  - name: tokens
    description: Personal Access Token management
  - name: billing
    description: Usage and billing
  - name: notifications
    description: In-app notification center
  - name: admin
    description: Workspace administration
  - name: integrations
    description: Workspace integrations (Slack, MCP, skills)
  - name: onboarding
    description: New-workspace onboarding wizard
  - name: auth
    description: Sign-in, sessions, and OAuth
paths:
  /v1/oauth/token:
    post:
      tags:
        - auth
      summary: OAuth token endpoint
      description: >-
        Exchanges an authorization code or a refresh token for tokens, accepting
        either

        application/x-www-form-urlencoded or application/json. For

        grant_type=authorization_code it verifies PKCE (S256 code_verifier
        against the

        stored challenge) and that client_id and redirect_uri match the original

        authorization; for grant_type=refresh_token it rotates the presented
        refresh token,

        carrying the original scopes forward, and detects reuse of an
        already-rotated token

        by revoking the entire token family. Confidential clients must
        authenticate with

        client_secret; public clients rely on PKCE. Returns an opaque
        short-lived Bearer

        access_token (expires_in seconds), a new rotating refresh_token, the
        token_type, and

        the space-delimited granted scope.
      operationId: oauth_token
      requestBody:
        description: >-
          authorization_code or refresh_token grant. Accepts
          application/x-www-form-urlencoded (the OAuth default) OR
          application/json.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenReq'
        required: true
      responses:
        '200':
          description: Access token + rotating refresh token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResp'
        '400':
          description: invalid_grant / invalid_request
        '401':
          description: invalid_client
        '429':
          description: Too many requests (per-IP rate limit)
components:
  schemas:
    TokenReq:
      type: object
      required:
        - grant_type
      properties:
        client_id:
          type:
            - string
            - 'null'
        client_secret:
          type:
            - string
            - 'null'
          description: >-
            Confidential-client secret (client_secret_post). Public clients omit
            it

            and rely on PKCE.
        code:
          type:
            - string
            - 'null'
        code_verifier:
          type:
            - string
            - 'null'
        device_code:
          type:
            - string
            - 'null'
          description: >-
            Device Authorization Grant (RFC 8628): the opaque device_code the
            client

            polls with. Carries its own PKCE `code_verifier`, same as the
            auth-code

            grant.
        grant_type:
          type: string
        redirect_uri:
          type:
            - string
            - 'null'
        refresh_token:
          type:
            - string
            - 'null'
    TokenResp:
      type: object
      required:
        - access_token
        - token_type
        - expires_in
        - refresh_token
        - scope
      properties:
        access_token:
          type: string
        expires_in:
          type: integer
          format: int64
        refresh_token:
          type: string
        scope:
          type: string
          description: Space-delimited granted scopes (RFC 6749 §5.1).
        token_type:
          type: string

````