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

# Poll for the developer token (RFC 8628 §3.4)

> Every `interval` seconds: `authorization_pending` until the developer decides; `slow_down` (HTTP 400) when a poll comes too soon, after which the interval is 5 seconds longer (at most 60); stop on `access_denied`, `expired_token` or `invalid_grant`. A device code yields one token, once. 30 unknown device codes per IP a minute (429 `slow_down`).

Public: the CLI holds nothing yet.



## OpenAPI

````yaml /openapi/openapi.developer.json post /developer/cli/token
openapi: 3.1.0
info:
  title: Tassyir developer API
  version: '1'
  summary: >-
    The API a developer calls to build Tassyir apps: their account, apps, client
    secrets, versions, the webhook console, dev stores and the CLI sign-in.
  description: >-
    The routes under `/developer/` that a DEVELOPER calls: the developer
    console, the `tassyir` CLI and the dev MCP. Not an app's API: installed apps
    call the Admin API, which has its own document. Guides:
    https://developers.tassyir.io.


    **Authentication.** `Authorization: Bearer <dashboard JWT>` (the console) or
    `Authorization: Bearer tsy_dt_…` (a developer token from the CLI sign-in),
    on every route but the approval routes (the dashboard session only) and the
    two public RFC 8628 routes. A developer account belongs to a USER, not a
    business: no route takes a business id, and an id that is not yours answers
    404, exactly like one that does not exist.


    **Conventions.** JSON fields are camelCase (snake_case on the two OAuth
    routes, as RFC 8628 has them); ids are strings; times are ISO 8601 UTC. One
    resource answers `{ data }`, a paged list `{ data, nextCursor }` (pass
    `nextCursor` back as `cursor`), a short list `{ data }`.


    **Errors.** Not problem+json: the dashboard API's shape. A coded refusal is
    `{ statusCode, code, message, fix?, …subject, requestId }`
    (`DeveloperError`); act on `code`. Three refusals carry no `code` today: a
    request that fails validation (400, `message` is a list of sentences), a
    missing or ended dashboard session (401), and the platform-wide rate limit
    (429, a bare JSON string). The two OAuth routes answer `{ error,
    error_description }`. Each operation lists every code it can answer, by
    status.


    **Limits.** Each developer has caps (apps, secrets, versions, dev stores),
    and the console's replays and tests and the CLI sign-in have rate limits of
    their own, which apply whether or not the platform-wide rate limit is
    enforced; each operation says which apply.
servers:
  - url: https://api.tassyir.io
security:
  - dashboardSession: []
  - developerToken: []
tags:
  - name: Account
    description: 'Developer mode: your developer account.'
  - name: Apps
    description: 'Your apps: handle, client id, names.'
  - name: Secrets
    description: 'Client secrets: minted once, rotated with a 24-hour overlap.'
  - name: Versions
    description: Manifest uploads.
  - name: Installations
    description: Where your app is installed, and its webhooks' health.
  - name: Webhooks
    description: The delivery log, replay, test deliveries, re-enable.
  - name: Dev stores
    description: Test businesses with synthetic data that reach no one.
  - name: CLI sign-in
    description: >-
      The OAuth 2.0 Device Authorization Grant (RFC 8628) the `tassyir` CLI
      signs in with, and the approval page's routes.
  - name: CLI tokens
    description: 'Your developer tokens: list and revoke.'
paths:
  /developer/cli/token:
    post:
      tags:
        - CLI sign-in
      summary: Poll for the developer token (RFC 8628 §3.4)
      description: >-
        Every `interval` seconds: `authorization_pending` until the developer
        decides; `slow_down` (HTTP 400) when a poll comes too soon, after which
        the interval is 5 seconds longer (at most 60); stop on `access_denied`,
        `expired_token` or `invalid_grant`. A device code yields one token,
        once. 30 unknown device codes per IP a minute (429 `slow_down`).


        Public: the CLI holds nothing yet.
      operationId: pollCliToken
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/DeviceTokenRequest'
          application/json:
            schema:
              $ref: '#/components/schemas/DeviceTokenRequest'
      responses:
        '200':
          description: The developer token.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control-No-Store'
            Pragma:
              $ref: '#/components/headers/Pragma-No-Cache'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeveloperTokenResponse'
        '400':
          description: >-
            Bad request: OAuth `access_denied`, `authorization_pending`,
            `expired_token`, `invalid_grant`, `invalid_request`, `slow_down`,
            `unsupported_grant_type`.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control-No-Store'
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/OAuthError'
                  - properties:
                      error:
                        enum:
                          - access_denied
                          - authorization_pending
                          - expired_token
                          - invalid_grant
                          - invalid_request
                          - slow_down
                          - unsupported_grant_type
        '413':
          description: 'Body too large: OAuth `invalid_request`.'
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control-No-Store'
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/OAuthError'
                  - properties:
                      error:
                        enum:
                          - invalid_request
        '429':
          description: >-
            Rate limited: OAuth `slow_down`; the platform-wide rate limit (a
            string).
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control-No-Store'
          content:
            application/json:
              schema:
                anyOf:
                  - allOf:
                      - $ref: '#/components/schemas/OAuthError'
                      - properties:
                          error:
                            enum:
                              - slow_down
                  - $ref: '#/components/schemas/RateLimitError'
        '500':
          $ref: '#/components/responses/InternalError'
        '503':
          $ref: '#/components/responses/Unavailable'
      security: []
components:
  schemas:
    DeviceTokenRequest:
      type: object
      description: >-
        Form-encoded (`application/x-www-form-urlencoded`, parsed flat) or a
        flat JSON object: at most 8 KB and 20 parameters, every value one string
        of at most 4,096 characters, a parameter sent twice refused
        (`invalid_request`).
      properties:
        grant_type:
          type: string
          const: urn:ietf:params:oauth:grant-type:device_code
        device_code:
          type: string
          maxLength: 4096
          pattern: ^tsy_dc_
          description: The `device_code` the first call answered.
        client_id:
          type: string
          enum:
            - tassyir-cli
          description: The client starting the sign-in; another is `invalid_client`.
      required:
        - grant_type
        - device_code
        - client_id
    DeveloperTokenResponse:
      type: object
      properties:
        access_token:
          type: string
          pattern: ^tsy_dt_[0-9A-Za-z]{43}$
          description: >-
            Send as `Authorization: Bearer <access_token>` to `developer/*`
            (nowhere else). Store it like a password.
        token_type:
          type: string
          const: Bearer
        expires_in:
          type: integer
          minimum: 1
          maximum: 9007199254740991
          description: Seconds of idle expiry (90 days); every use extends it.
        scope:
          type: string
          const: developer
        token_id:
          type: string
          pattern: ^\d{1,20}$
          description: 'A Tassyir id: a string of digits.'
        user_id:
          type: string
          pattern: ^\d{1,20}$
          description: 'A Tassyir id: a string of digits.'
        email:
          type: string
          description: Who the CLI signed in as.
      required:
        - access_token
        - token_type
        - expires_in
        - scope
        - token_id
        - user_id
        - email
      additionalProperties: false
      description: 'RFC 6749 §5.1: the developer token, once.'
    OAuthError:
      type: object
      description: >-
        An OAuth error (RFC 6749 §5.2, RFC 8628 §3.5), on the two public CLI
        routes only.
      properties:
        error:
          type: string
          enum:
            - access_denied
            - authorization_pending
            - expired_token
            - invalid_client
            - invalid_grant
            - invalid_request
            - slow_down
            - unsupported_grant_type
        error_description:
          type: string
      required:
        - error
        - error_description
    RateLimitError:
      type: string
      description: >-
        The platform-wide rate limit's refusal: a bare JSON string, not an
        object, sent only while that limit is enforced. A developer token's
        calls are counted by IP.
    ServerError:
      type: object
      description: >-
        An unexpected failure (500), or a database that cannot be reached (503:
        retry). Never an internal message: `requestId` names it.
      properties:
        statusCode:
          type: integer
        message:
          type: string
        path:
          type: string
        requestId:
          type: string
      required:
        - statusCode
        - message
  headers:
    X-Request-Id:
      description: The request's id, also in an error's `requestId`.
      schema:
        type: string
    Cache-Control-No-Store:
      description: '`no-store`: never cache this answer (it carries a secret or a token).'
      schema:
        type: string
        const: no-store
    Pragma-No-Cache:
      description: '`no-cache`, for HTTP/1.0 caches.'
      schema:
        type: string
        const: no-cache
    Retry-After:
      description: >-
        Seconds to wait before retrying, when the refusal knows (also
        `retryAfterSeconds` in a coded body).
      schema:
        type: integer
        minimum: 0
  responses:
    InternalError:
      description: 'Internal error: an unexpected failure.'
      headers:
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ServerError'
    Unavailable:
      description: 'Unavailable: a database that cannot be reached.'
      headers:
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
        Retry-After:
          $ref: '#/components/headers/Retry-After'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ServerError'
  securitySchemes:
    dashboardSession:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        The dashboard's session token (`Authorization: Bearer <JWT>`), as the
        console sends it. Never paste it into a terminal: it opens every
        business the user belongs to.
    developerToken:
      type: http
      scheme: bearer
      bearerFormat: tsy_dt_<43 base62>
      description: >-
        A developer token from the CLI sign-in (`POST
        /developer/cli/device-code`, then `POST /developer/cli/token`): accepted
        on `developer/*` ONLY, scope `developer`. Valid while not revoked, used
        within 90 days (each use extends it) and issued less than 365 days ago;
        refused once the user changes their password. Signing out of the
        dashboard does not revoke it.

````

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