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

# Get an access token

> Three grants: the token exchange of an embedded page's ID token (an offline token, or an online one bound to the dashboard user), an install code, and a refresh token (rotated on every use; a spent one presented after 60 seconds revokes its whole family).

Client authentication is HTTP Basic only. Errors are OAuth's `{ error, error_description }`, not problem+json. Limits: 300 requests a minute per client id, 30 failed authentications a minute per IP (429 `slow_down`).



## OpenAPI

````yaml /openapi/openapi.2026-10.json post /apps/oauth/token
openapi: 3.1.0
info:
  title: Tassyir Admin API
  version: 2026-10
  summary: >-
    The API installed apps call to read and change a Tassyir business, and the
    webhooks they receive.
  description: >-
    Version `2026-10` of the API installed Tassyir apps call. The routes live
    under `/apps/2026-10/`; the token endpoint and the key documents under
    `/apps/`. Guides, and a page for every error code:
    https://developers.tassyir.io.


    **Authentication.** `Authorization: Bearer tsy_at_…` from `POST
    /apps/oauth/token`. An app acts as a team member of the business: it reads
    and writes only what its granted scopes allow, only in the stores the
    merchant gave it, under the merchant's plan.


    **Conventions.** JSON fields are camelCase; ids are strings; times are ISO
    8601 UTC; money is a decimal string (`"2500.00"`) with a `currency` field,
    never a number. A list answers `{ data, nextCursor }`, newest first,
    keyset-paged: `limit` 1 to 100 (default 50), `cursor` = the previous
    `nextCursor`. A single resource answers `{ data }`. `fields` trims a
    resource to the named top-level fields. Fields without
    `read_customer_contact` (buyer name, phones, address, notes) are omitted,
    not nulled. Every answer is `Cache-Control: no-store`.


    **Writes.** Send an `Idempotency-Key` to make a write safe to retry (24
    hours per installation).


    **Errors.** `application/problem+json` with a stable `code`, a `fix`
    sentence, the `docs` page of the code and a `requestId`. Each operation
    lists every code it can answer; `x-tassyir-errors` lists every code a
    problem can carry, with its title, statuses and `fix`. HTTP 460
    `STORE_SCOPE_FORBIDDEN` means a store outside the installation's stores.


    **Rate limits.** A leaky bucket per installation, by default 40 requests
    refilled at 4 a second: every answer carries `RateLimit-Limit`,
    `RateLimit-Remaining` and `RateLimit-Reset`; a refusal is 429 `RATE_LIMITED`
    with `Retry-After`. All the apps of one business also share a ceiling, by
    default 20 requests a second: 429 `BUSINESS_RATE_LIMITED` with
    `Retry-After`. An API process that is already running its maximum of app
    requests answers 503 `API_BUSY` with `Retry-After`, having done nothing.


    **Webhooks.** Signed Standard Webhooks deliveries (`v1a`, Ed25519), one per
    topic below; verify them with `GET /apps/webhook-keys`.
servers:
  - url: https://api.tassyir.io
security:
  - appAccessToken: []
tags:
  - name: Authentication
    description: Tokens and the public keys apps verify with.
  - name: Business
    description: The business the app is installed on.
  - name: Orders
    description: Orders, their comments and the cancel reasons.
  - name: Products
    description: Products and their variants.
  - name: Collections
    description: Collections and their members.
  - name: Inventory
    description: Stock levels.
  - name: Customers
    description: The buyers behind the orders.
  - name: Stores
    description: The installation's stores.
  - name: Delivery
    description: Delivery agents and fee groups (never credentials).
  - name: Data
    description: >-
      Typed values the app keeps on the business, its stores, products, orders
      and customers, under keys its manifest declares.
  - name: Images
    description: >-
      Images re-hosted on the Tassyir CDN, for the product and collection
      writes.
  - name: Bulk exports
    description: >-
      A whole list (orders, products, customers) as one JSON Lines file, instead
      of paging through it.
  - name: Webhooks
    description: What Tassyir POSTs to the app.
paths:
  /apps/oauth/token:
    post:
      tags:
        - Authentication
      summary: Get an access token
      description: >-
        Three grants: the token exchange of an embedded page's ID token (an
        offline token, or an online one bound to the dashboard user), an install
        code, and a refresh token (rotated on every use; a spent one presented
        after 60 seconds revokes its whole family).


        Client authentication is HTTP Basic only. Errors are OAuth's `{ error,
        error_description }`, not problem+json. Limits: 300 requests a minute
        per client id, 30 failed authentications a minute per IP (429
        `slow_down`).
      operationId: requestAccessToken
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/TokenRequest'
          application/json:
            schema:
              $ref: '#/components/schemas/TokenRequest'
      responses:
        '200':
          description: The tokens.
          headers:
            Cache-Control:
              $ref: '#/components/headers/Cache-Control-No-Store'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
        '400':
          description: >-
            `invalid_request`, `invalid_grant`, `unauthorized_client`,
            `unsupported_grant_type`.
          headers:
            Cache-Control:
              $ref: '#/components/headers/Cache-Control-No-Store'
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/OAuthError'
                  - properties:
                      error:
                        enum:
                          - invalid_grant
                          - invalid_request
                          - unauthorized_client
                          - unsupported_grant_type
        '401':
          description: '`invalid_client`.'
          headers:
            Cache-Control:
              $ref: '#/components/headers/Cache-Control-No-Store'
            WWW-Authenticate:
              description: The Basic challenge.
              schema:
                type: string
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/OAuthError'
                  - properties:
                      error:
                        enum:
                          - invalid_client
        '413':
          description: '`invalid_request`.'
          headers:
            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: '`slow_down`.'
          headers:
            Cache-Control:
              $ref: '#/components/headers/Cache-Control-No-Store'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/OAuthError'
                  - properties:
                      error:
                        enum:
                          - slow_down
        '500':
          description: 'An unexpected failure: the API''s generic body, never an OAuth one.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerError'
        '503':
          description: '`temporarily_unavailable`.'
          headers:
            Cache-Control:
              $ref: '#/components/headers/Cache-Control-No-Store'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/OAuthError'
                  - properties:
                      error:
                        enum:
                          - temporarily_unavailable
      security:
        - appClient: []
components:
  schemas:
    TokenRequest:
      description: >-
        Form-encoded (`application/x-www-form-urlencoded`, parsed flat) or a
        flat JSON object; at most 8 KB. Every value is one string; a repeated
        parameter is refused. Unknown parameters and `scope` are ignored. Never
        send `client_secret` in the body (`invalid_client`).
      oneOf:
        - title: Token exchange (embedded page)
          type: object
          properties:
            grant_type:
              type: string
              const: urn:ietf:params:oauth:grant-type:token-exchange
            subject_token:
              type: string
              minLength: 1
              maxLength: 4096
              description: >-
                The 60-second ID token the dashboard handed the page. Single
                use.
            subject_token_type:
              type: string
              const: urn:ietf:params:oauth:token-type:id_token
            requested_token_type:
              type: string
              enum:
                - urn:tassyir:params:oauth:token-type:offline-access-token
                - urn:tassyir:params:oauth:token-type:online-access-token
              default: urn:tassyir:params:oauth:token-type:offline-access-token
            client_id:
              type: string
              minLength: 1
              maxLength: 4096
              description: Optional; must equal the client id of the Basic credentials.
          required:
            - grant_type
            - subject_token
            - subject_token_type
        - title: Install code (external app without an embedded page)
          type: object
          properties:
            grant_type:
              type: string
              const: authorization_code
            code:
              type: string
              pattern: ^tsy_ic_
              maxLength: 4096
              description: >-
                The `code` your `auth.callback_url` received. 5 minutes, one
                use.
            client_id:
              type: string
              minLength: 1
              maxLength: 4096
              description: Optional; must equal the client id of the Basic credentials.
          required:
            - grant_type
            - code
        - title: Refresh
          type: object
          properties:
            grant_type:
              type: string
              const: refresh_token
            refresh_token:
              type: string
              pattern: ^tsy_rt_
              maxLength: 4096
            client_id:
              type: string
              minLength: 1
              maxLength: 4096
              description: Optional; must equal the client id of the Basic credentials.
          required:
            - grant_type
            - refresh_token
    TokenResponse:
      type: object
      properties:
        access_token:
          type: string
          pattern: ^tsy_at_[0-9A-Za-z]{43}$
          description: 'Send as `Authorization: Bearer <access_token>`.'
        token_type:
          type: string
          const: Bearer
        expires_in:
          type: integer
          description: Seconds the access token lives (3600).
        refresh_token:
          type: string
          pattern: ^tsy_rt_[0-9A-Za-z]{43}$
          description: Offline tokens only. Rotated on every use; the one used is spent.
        refresh_token_expires_in:
          type: integer
          description: Seconds the refresh token lives (90 days).
        issued_token_type:
          type: string
          enum:
            - urn:tassyir:params:oauth:token-type:offline-access-token
            - urn:tassyir:params:oauth:token-type:online-access-token
          description: Token exchanges only (RFC 8693).
        scope:
          type: string
          description: The installation's granted scopes, space-separated.
        installation_id:
          type: string
        business_id:
          type: string
        user_id:
          type: string
          description: 'Online tokens only: the user the token acts for.'
      required:
        - access_token
        - token_type
        - expires_in
        - scope
        - installation_id
        - business_id
    OAuthError:
      type: object
      description: An OAuth error (RFC 6749 §5.2), never problem+json.
      properties:
        error:
          type: string
          enum:
            - invalid_request
            - invalid_client
            - invalid_grant
            - unauthorized_client
            - unsupported_grant_type
            - slow_down
            - temporarily_unavailable
        error_description:
          type: string
      required:
        - error
        - error_description
    ServerError:
      type: object
      description: The API's generic error body (outside `/apps/<version>/`).
      properties:
        statusCode:
          type: integer
        message:
          type: string
        requestId:
          type: string
  headers:
    Cache-Control-No-Store:
      description: '`no-store`: never cache an answer.'
      schema:
        type: string
        const: no-store
    Retry-After:
      description: Seconds to wait before retrying.
      schema:
        type: integer
        minimum: 0
  securitySchemes:
    appAccessToken:
      type: http
      scheme: bearer
      bearerFormat: tsy_at_<43 base62>
      description: >-
        An app access token from `POST /apps/oauth/token`, valid 1 hour. An
        offline token acts as the app; an online token acts as the app on behalf
        of one dashboard user (the intersection of both permission sets).
    appClient:
      type: http
      scheme: basic
      description: >-
        The app's client id (`app_…`) as the user name and a live client secret
        (`tsy_cs_…`) as the password.

````

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