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

# Upload a version of the app's manifest

> The body is at most 128 KB (8 MB + 128 KB while Tassyir Hosting is on) and nests at most 16 levels (its own parser). The validator checks the manifest (every error at once); `handle` must be the app's; a manifest identical to the latest version's (and, hosted, the same bundle) is refused. An external version is numbered latest + 1, approved and current at once. A hosted version (`hosting.mode = "tassyir"`, while Tassyir Hosting is on) carries its `bundle`, stored before the version is created `scanning`; it is scanned, uploaded and released by the hosting pipeline. 30 uploads per developer per hour, 10 hosted a day.

Takes the dashboard session or a developer token (`tsy_dt_…`); either way the caller is the signed-in user, and only their own account, apps and tokens are reachable.



## OpenAPI

````yaml /openapi/openapi.developer.json post /developer/apps/{appId}/versions
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/apps/{appId}/versions:
    post:
      tags:
        - Versions
      summary: Upload a version of the app's manifest
      description: >-
        The body is at most 128 KB (8 MB + 128 KB while Tassyir Hosting is on)
        and nests at most 16 levels (its own parser). The validator checks the
        manifest (every error at once); `handle` must be the app's; a manifest
        identical to the latest version's (and, hosted, the same bundle) is
        refused. An external version is numbered latest + 1, approved and
        current at once. A hosted version (`hosting.mode = "tassyir"`, while
        Tassyir Hosting is on) carries its `bundle`, stored before the version
        is created `scanning`; it is scanned, uploaded and released by the
        hosting pipeline. 30 uploads per developer per hour, 10 hosted a day.


        Takes the dashboard session or a developer token (`tsy_dt_…`); either
        way the caller is the signed-in user, and only their own account, apps
        and tokens are reachable.
      operationId: uploadAppVersion
      parameters:
        - name: appId
          in: path
          required: true
          description: >-
            One of your apps' ids. Another developer's app, or a malformed id,
            is 404 `APP_NOT_FOUND`.
          schema:
            type: string
            pattern: ^\d{1,20}$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UploadAppVersionInput'
      responses:
        '201':
          description: The new version, with its normalised manifest.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AppVersionDetailResponse'
        '400':
          description: >-
            Bad request: `APP_BUNDLE_INVALID`, `APP_BUNDLE_NOT_ALLOWED`,
            `APP_BUNDLE_REQUIRED`, `APP_HOSTING_NOT_AVAILABLE`,
            `APP_MANIFEST_HANDLE_MISMATCH`, `APP_MANIFEST_INVALID`,
            `APP_MANIFEST_TOO_DEEP`, `BAD_REQUEST`; a field refused by
            validation (no `code`).
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/json:
              schema:
                anyOf:
                  - allOf:
                      - $ref: '#/components/schemas/DeveloperError'
                      - properties:
                          code:
                            enum:
                              - APP_BUNDLE_INVALID
                              - APP_BUNDLE_NOT_ALLOWED
                              - APP_BUNDLE_REQUIRED
                              - APP_HOSTING_NOT_AVAILABLE
                              - APP_MANIFEST_HANDLE_MISMATCH
                              - APP_MANIFEST_INVALID
                              - APP_MANIFEST_TOO_DEEP
                              - BAD_REQUEST
                  - $ref: '#/components/schemas/ValidationError'
        '401':
          $ref: '#/components/responses/DeveloperUnauthorized'
        '403':
          description: 'Not allowed: `DEVELOPER_ACCOUNT_SUSPENDED`.'
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/DeveloperError'
                  - properties:
                      code:
                        enum:
                          - DEVELOPER_ACCOUNT_SUSPENDED
        '404':
          description: 'Not found: `APP_NOT_FOUND`, `DEVELOPER_ACCOUNT_NOT_FOUND`.'
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/DeveloperError'
                  - properties:
                      code:
                        enum:
                          - APP_NOT_FOUND
                          - DEVELOPER_ACCOUNT_NOT_FOUND
        '409':
          description: >-
            Conflict: `APP_READ_ONLY`, `APP_VERSION_CONFLICT`,
            `APP_VERSION_UNCHANGED`.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/DeveloperError'
                  - properties:
                      code:
                        enum:
                          - APP_READ_ONLY
                          - APP_VERSION_CONFLICT
                          - APP_VERSION_UNCHANGED
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          description: >-
            Rate limited: `DEVELOPER_RATE_LIMITED`,
            `DEVELOPER_TOKEN_RATE_LIMITED`; the platform-wide rate limit (a
            string).
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/json:
              schema:
                anyOf:
                  - allOf:
                      - $ref: '#/components/schemas/DeveloperError'
                      - properties:
                          code:
                            enum:
                              - DEVELOPER_RATE_LIMITED
                              - DEVELOPER_TOKEN_RATE_LIMITED
                  - $ref: '#/components/schemas/RateLimitError'
        '500':
          $ref: '#/components/responses/InternalError'
        '503':
          description: >-
            Unavailable: `APP_BUNDLE_STORAGE_FAILED`, `APP_HOSTING_BUSY`,
            `APP_HOSTING_NOT_CONFIGURED`; 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:
                anyOf:
                  - allOf:
                      - $ref: '#/components/schemas/DeveloperError'
                      - properties:
                          code:
                            enum:
                              - APP_BUNDLE_STORAGE_FAILED
                              - APP_HOSTING_BUSY
                              - APP_HOSTING_NOT_CONFIGURED
                  - $ref: '#/components/schemas/ServerError'
      security:
        - dashboardSession: []
        - developerToken: []
components:
  schemas:
    UploadAppVersionInput:
      type: object
      properties:
        manifest:
          type: object
          description: >-
            `tassyir.app.toml` exactly as a TOML parser returns it, handed to
            the manifest validator untouched (rules:
            https://developers.tassyir.io/guides/manifest; JSON Schema:
            `@tassyir/app-manifest/schemas/manifest.2026-10.schema.json`).
            Anything else is 400 `APP_MANIFEST_INVALID`.
        bundle:
          anyOf:
            - type: object
              description: >-
                A hosted version's code (required for `hosting.mode =
                "tassyir"`, refused otherwise): `{ main, files: [{ path, content
                }], lockfile? }`, text modules only, at most 100 files and 3 MB;
                checked by the bundle validator (every error at once, 400
                `APP_BUNDLE_INVALID`).
              properties:
                main:
                  type: string
                files:
                  type: array
                  minItems: 1
                  maxItems: 100
                  items:
                    type: object
                    required:
                      - path
                      - content
                    properties:
                      path:
                        maxLength: 200
                        type: string
                      content:
                        type: string
                lockfile:
                  type: object
                  required:
                    - name
                    - content
                  properties:
                    name:
                      type: string
                      enum:
                        - package-lock.json
                        - npm-shrinkwrap.json
                        - pnpm-lock.yaml
                        - yarn.lock
                        - bun.lock
                    content:
                      type: string
            - type: 'null'
      additionalProperties: false
      required:
        - manifest
    AppVersionDetailResponse:
      type: object
      properties:
        data:
          $ref: '#/components/schemas/AppVersionDetail'
      required:
        - data
      additionalProperties: false
    DeveloperError:
      type: object
      description: >-
        A coded refusal of the developer API (the dashboard API's error shape,
        not problem+json). Act on `code`, never on `message`. Other members name
        the subject of the refusal (`appId`, `limit`, `retryAfterSeconds`,
        `status`, …).
      properties:
        statusCode:
          type: integer
        code:
          type: string
          pattern: ^[A-Z][A-Z0-9_]{1,63}$
          description: 'Stable: what a client branches on.'
        message:
          type: string
          description: For a person (English).
        fix:
          type: string
          description: One sentence an AI agent can act on.
        errors:
          type: array
          items:
            $ref: '#/components/schemas/FieldError'
          description: >-
            With `APP_MANIFEST_INVALID`, `APP_FIELDS_INVALID`,
            `DEVELOPER_ACCOUNT_INVALID`: every problem at once, in the manifest
            validator's format.
        path:
          type: string
          description: 'With a body the parser refused: the request path.'
        requestId:
          type: string
          description: Also in the `X-Request-Id` header. Quote it to Tassyir.
      required:
        - code
        - message
    ValidationError:
      type: object
      description: >-
        A request that fails validation: a field missing, malformed or unknown,
        in the body or the query string. It carries NO `code`: branch on the
        status (400) and show `message`.
      properties:
        statusCode:
          type: integer
          const: 400
        message:
          type: array
          items:
            type: string
          description: >-
            One sentence per refused field (`limit must not be greater than
            100`).
        error:
          type: string
          const: Bad Request
        requestId:
          type: string
      required:
        - statusCode
        - message
        - error
    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
    AppVersionDetail:
      type: object
      properties:
        id:
          type: string
          pattern: ^\d{1,20}$
          description: 'A Tassyir id: a string of digits.'
        version:
          type: integer
          minimum: 1
          maximum: 9007199254740991
        apiVersion:
          type: string
        scopes:
          type: array
          items:
            type: string
        optionalScopes:
          type: array
          items:
            type: string
        webhookTopics:
          type: array
          items:
            type: string
        allowedHosts:
          type: array
          items:
            type: string
        reviewState:
          type: string
          description: >-
            `approved` for an external upload; a hosted one goes `scanning` →
            `scan_failed` | `ready` | `in_review` → `approved` | `rejected`;
            `draft`, `retired` are other states.
        current:
          type: boolean
          description: The version new installations get.
        releasedAt:
          anyOf:
            - type: string
              format: date-time
              pattern: >-
                ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
              description: ISO 8601 date-time, UTC.
            - type: 'null'
        createdAt:
          type: string
          format: date-time
          pattern: >-
            ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
          description: ISO 8601 date-time, UTC.
        hosting:
          anyOf:
            - type: object
              properties:
                bundleSha256:
                  anyOf:
                    - type: string
                    - type: 'null'
                  description: SHA-256 of the bundle's canonical document.
                bundleBytes:
                  type: integer
                  minimum: 0
                  maximum: 9007199254740991
                  description: The modules' UTF-8 bytes.
                files:
                  type: integer
                  minimum: 1
                  maximum: 9007199254740991
                main:
                  type: string
                  description: The module exporting the fetch handler.
                lockfile:
                  anyOf:
                    - type: string
                    - type: 'null'
                  description: The lockfile sent, if any.
                scan:
                  type: object
                  properties:
                    status:
                      type: string
                      enum:
                        - pending
                        - dispatched
                        - passed
                        - held
                        - failed
                        - error
                        - timed_out
                    dispatchedAt:
                      anyOf:
                        - type: string
                          format: date-time
                          pattern: >-
                            ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                          description: ISO 8601 date-time, UTC.
                        - type: 'null'
                    finishedAt:
                      anyOf:
                        - type: string
                          format: date-time
                          pattern: >-
                            ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                          description: ISO 8601 date-time, UTC.
                        - type: 'null'
                    failureCode:
                      anyOf:
                        - type: string
                          enum:
                            - SCAN_DISPATCH_FAILED
                            - SCAN_TIMED_OUT
                            - SCAN_INCOMPLETE
                            - SCAN_RESULT_INVALID
                            - CLOUDFLARE_REJECTED
                            - CLOUDFLARE_UNAVAILABLE
                            - CLOUDFLARE_NOT_CONFIGURED
                            - BUNDLE_INTEGRITY_FAILED
                            - STORAGE_FAILED
                        - type: 'null'
                      description: >-
                        Why a scan or an upload stopped without a verdict on the
                        code.
                    reasons:
                      type: array
                      items:
                        type: string
                      description: >-
                        `CHECK_FAILED:<id>`, `CHECK_MISSING:<id>`,
                        `CHECK_ERROR:<id>`, `REVIEW_FAILED`,
                        `REVIEW_NEEDS_HUMAN`.
                  required:
                    - status
                    - dispatchedAt
                    - finishedAt
                    - failureCode
                    - reasons
                  additionalProperties: false
                deploy:
                  type: object
                  properties:
                    staging:
                      type: object
                      properties:
                        status:
                          type: string
                          enum:
                            - none
                            - pending
                            - deployed
                            - failed
                        deployedAt:
                          anyOf:
                            - type: string
                              format: date-time
                              pattern: >-
                                ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                              description: ISO 8601 date-time, UTC.
                            - type: 'null'
                        errorCode:
                          anyOf:
                            - type: string
                              enum:
                                - SCAN_DISPATCH_FAILED
                                - SCAN_TIMED_OUT
                                - SCAN_INCOMPLETE
                                - SCAN_RESULT_INVALID
                                - CLOUDFLARE_REJECTED
                                - CLOUDFLARE_UNAVAILABLE
                                - CLOUDFLARE_NOT_CONFIGURED
                                - BUNDLE_INTEGRITY_FAILED
                                - STORAGE_FAILED
                            - type: 'null'
                          description: >-
                            Why a scan or an upload stopped without a verdict on
                            the code.
                      required:
                        - status
                        - deployedAt
                        - errorCode
                      additionalProperties: false
                    production:
                      type: object
                      properties:
                        status:
                          type: string
                          enum:
                            - none
                            - pending
                            - deployed
                            - failed
                        deployedAt:
                          anyOf:
                            - type: string
                              format: date-time
                              pattern: >-
                                ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                              description: ISO 8601 date-time, UTC.
                            - type: 'null'
                        errorCode:
                          anyOf:
                            - type: string
                              enum:
                                - SCAN_DISPATCH_FAILED
                                - SCAN_TIMED_OUT
                                - SCAN_INCOMPLETE
                                - SCAN_RESULT_INVALID
                                - CLOUDFLARE_REJECTED
                                - CLOUDFLARE_UNAVAILABLE
                                - CLOUDFLARE_NOT_CONFIGURED
                                - BUNDLE_INTEGRITY_FAILED
                                - STORAGE_FAILED
                            - type: 'null'
                          description: >-
                            Why a scan or an upload stopped without a verdict on
                            the code.
                      required:
                        - status
                        - deployedAt
                        - errorCode
                      additionalProperties: false
                  required:
                    - staging
                    - production
                  additionalProperties: false
                checks:
                  type: array
                  items:
                    type: object
                    properties:
                      id:
                        type: string
                      status:
                        type: string
                        enum:
                          - pass
                          - warn
                          - fail
                          - error
                          - skipped
                      summary:
                        type: string
                      findings:
                        type: array
                        items:
                          type: object
                          properties:
                            rule:
                              type: string
                            severity:
                              type: string
                              enum:
                                - error
                                - warning
                                - info
                            file:
                              anyOf:
                                - type: string
                                - type: 'null'
                            line:
                              anyOf:
                                - type: integer
                                  minimum: 0
                                  maximum: 9007199254740991
                                - type: 'null'
                            message:
                              type: string
                          required:
                            - rule
                            - severity
                            - file
                            - line
                            - message
                          additionalProperties: false
                      truncated:
                        type: boolean
                    required:
                      - id
                      - status
                      - summary
                      - findings
                      - truncated
                    additionalProperties: false
                review:
                  anyOf:
                    - type: object
                      properties:
                        status:
                          type: string
                          enum:
                            - pass
                            - fail
                            - needs_human
                            - skipped
                            - error
                        model:
                          anyOf:
                            - type: string
                            - type: 'null'
                        summary:
                          type: string
                        concerns:
                          type: array
                          items:
                            type: object
                            properties:
                              severity:
                                type: string
                                enum:
                                  - high
                                  - medium
                                  - low
                              file:
                                anyOf:
                                  - type: string
                                  - type: 'null'
                              message:
                                type: string
                            required:
                              - severity
                              - file
                              - message
                            additionalProperties: false
                        truncated:
                          type: boolean
                      required:
                        - status
                        - model
                        - summary
                        - concerns
                        - truncated
                      additionalProperties: false
                    - type: 'null'
                  description: The AI review; it can only add caution.
                rejectionReason:
                  anyOf:
                    - type: string
                    - type: 'null'
              required:
                - bundleSha256
                - bundleBytes
                - files
                - main
                - lockfile
                - scan
                - deploy
                - checks
                - review
                - rejectionReason
              additionalProperties: false
              description: >-
                A hosted version's scan report: every check's findings and the
                review.
            - type: 'null'
          description: A hosted version's scan report; null for an external version.
        manifest:
          type: object
          propertyNames:
            type: string
          additionalProperties: {}
          description: >-
            The normalised manifest (its JSON Schema:
            `@tassyir/app-manifest/schemas/manifest.2026-10.schema.json`).
      required:
        - id
        - version
        - apiVersion
        - scopes
        - optionalScopes
        - webhookTopics
        - allowedHosts
        - reviewState
        - current
        - releasedAt
        - createdAt
        - hosting
        - manifest
      additionalProperties: false
      description: A version of an app, with its normalised manifest.
    FieldError:
      type: object
      description: One problem, as the manifest validator reports it.
      properties:
        code:
          type: string
          pattern: ^[A-Z][A-Z0-9_]{1,63}$
        path:
          type: string
          description: 'Where: `handle`, `webhooks[0].uri`, …'
        message:
          type: string
        fix:
          type: string
      required:
        - code
        - path
        - message
    HttpError:
      type: object
      description: >-
        A refusal without a `code`: no dashboard session (no token, a revoked or
        expired session, a password changed since it was issued).
      properties:
        statusCode:
          type: integer
        message:
          anyOf:
            - type: string
            - type: array
              items:
                type: string
        error:
          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
    Retry-After:
      description: >-
        Seconds to wait before retrying, when the refusal knows (also
        `retryAfterSeconds` in a coded body).
      schema:
        type: integer
        minimum: 0
  responses:
    DeveloperUnauthorized:
      description: >-
        Not signed in: `DEVELOPER_TOKEN_INVALID`; no dashboard session (no
        `code`).
      headers:
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
        WWW-Authenticate:
          description: >-
            On `DEVELOPER_TOKEN_INVALID`: `Bearer realm="tassyir-developer",
            error="invalid_token"`.
          schema:
            type: string
      content:
        application/json:
          schema:
            anyOf:
              - allOf:
                  - $ref: '#/components/schemas/DeveloperError'
                  - properties:
                      code:
                        enum:
                          - DEVELOPER_TOKEN_INVALID
              - $ref: '#/components/schemas/HttpError'
    PayloadTooLarge:
      description: 'Body too large: `PAYLOAD_TOO_LARGE`.'
      headers:
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/DeveloperError'
              - properties:
                  code:
                    enum:
                      - PAYLOAD_TOO_LARGE
    UnsupportedMediaType:
      description: 'Unsupported body: `BAD_REQUEST`.'
      headers:
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/DeveloperError'
              - properties:
                  code:
                    enum:
                      - BAD_REQUEST
    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'
  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.