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

# Update an offer

> Changes the fields sent (`targets`, when sent, replaces the whole target set; `kind` and `method` never change). A live offer must stay publishable. The change applies to the offer as it is now unless `expectedVersion` is sent (the dashboard's compare-and-set, 409 `OFFER_VERSION_CONFLICT` with `currentVersion`). An app changes, and reads the codes and redemptions of, only the offers its own installation created (`ownerInstallationId` is set from the token when it creates one). A merchant's offer, or another app's, is 403 `OFFER_NOT_OWNED`; an offer the token cannot see (another business, a store outside the installation's stores) is 404.

Needs the `write_offers` scope (permission `offers:update`).



## OpenAPI

````yaml /openapi/openapi.2026-10.json patch /apps/2026-10/offers/{id}
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. An app on Tassyir Hosting holds
    no token: it calls these routes through `ctx.props.TASSYIR.fetch`, which
    signs each call as its installation (the same scopes, stores and plan;
    Idempotency-Key passes through), and a call that cannot be accepted is 401
    `HOSTED_CALL_REFUSED`.


    **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: Offers
    description: >-
      The merchant's per-store pricing offers (read them with read_offers), and
      the offers an app creates and manages itself, with their codes and
      redemptions (write_offers).
  - 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/2026-10/offers/{id}:
    patch:
      tags:
        - Offers
      summary: Update an offer
      description: >-
        Changes the fields sent (`targets`, when sent, replaces the whole target
        set; `kind` and `method` never change). A live offer must stay
        publishable. The change applies to the offer as it is now unless
        `expectedVersion` is sent (the dashboard's compare-and-set, 409
        `OFFER_VERSION_CONFLICT` with `currentVersion`). An app changes, and
        reads the codes and redemptions of, only the offers its own installation
        created (`ownerInstallationId` is set from the token when it creates
        one). A merchant's offer, or another app's, is 403 `OFFER_NOT_OWNED`; an
        offer the token cannot see (another business, a store outside the
        installation's stores) is 404.


        Needs the `write_offers` scope (permission `offers:update`).
      operationId: updateOffer
      parameters:
        - name: id
          in: path
          required: true
          description: >-
            A resource id. One this installation cannot read, or a malformed
            one, is 404 `NOT_FOUND`.
          schema:
            type: string
            minLength: 1
        - name: fields
          in: query
          required: false
          schema:
            maxLength: 1000
            type: string
          description: >-
            Return only these top-level fields, separated by commas; the id is
            always kept. The response schema describes the whole resource. An
            unknown name is 400 `INVALID_FIELDS`. Fields: `id`, `storeId`,
            `kind`, `method`, `title`, `label`, `status`, `currency`,
            `quantity`, `freeDelivery`, `productDiscount`, `orderDiscount`,
            `bxgy`, `addon`, `appRule`, `combinesWith`, `eligibility`,
            `targets`, `codeCount`, `totalUsageLimit`, `perBuyerLimit`,
            `startsAt`, `endsAt`, `publishedAt`, `pausedAt`, `archivedAt`,
            `appOwned`, `createdAt`, `updatedAt`.
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateOfferInput'
      responses:
        '200':
          description: The offer as it is now.
          headers:
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimit-Limit'
            RateLimit-Remaining:
              $ref: '#/components/headers/RateLimit-Remaining'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimit-Reset'
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control-No-Store'
            Idempotent-Replayed:
              $ref: '#/components/headers/Idempotent-Replayed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OfferResponse'
        '400':
          description: >-
            Bad request: `BAD_REQUEST`, `IDEMPOTENCY_KEY_INVALID`,
            `INVALID_FIELDS`, `OFFER_INVALID`, `OFFER_KIND_IMMUTABLE`,
            `OFFER_TARGET_NOT_FOUND`, `OFFER_TARGET_OUTSIDE_STORE`,
            `VALIDATION_FAILED`.
          headers:
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimit-Limit'
            RateLimit-Remaining:
              $ref: '#/components/headers/RateLimit-Remaining'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimit-Reset'
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/problem+json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Problem'
                  - properties:
                      status:
                        const: 400
                      code:
                        enum:
                          - BAD_REQUEST
                          - IDEMPOTENCY_KEY_INVALID
                          - INVALID_FIELDS
                          - OFFER_INVALID
                          - OFFER_KIND_IMMUTABLE
                          - OFFER_TARGET_NOT_FOUND
                          - OFFER_TARGET_OUTSIDE_STORE
                          - VALIDATION_FAILED
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: >-
            Not allowed: `OFFER_NOT_OWNED`, `PERMISSION_DENIED`,
            `PLAN_LIMIT_REACHED`.
          headers:
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimit-Limit'
            RateLimit-Remaining:
              $ref: '#/components/headers/RateLimit-Remaining'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimit-Reset'
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/problem+json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Problem'
                  - properties:
                      status:
                        const: 403
                      code:
                        enum:
                          - OFFER_NOT_OWNED
                          - PERMISSION_DENIED
                          - PLAN_LIMIT_REACHED
        '404':
          description: 'Not found: `NOT_FOUND`, `OFFER_NOT_FOUND`.'
          headers:
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimit-Limit'
            RateLimit-Remaining:
              $ref: '#/components/headers/RateLimit-Remaining'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimit-Reset'
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/problem+json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Problem'
                  - properties:
                      status:
                        const: 404
                      code:
                        enum:
                          - NOT_FOUND
                          - OFFER_NOT_FOUND
        '409':
          description: >-
            Conflict: `IDEMPOTENCY_KEY_IN_USE`, `OFFER_ARCHIVED`,
            `OFFER_VERSION_CONFLICT`.
          headers:
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimit-Limit'
            RateLimit-Remaining:
              $ref: '#/components/headers/RateLimit-Remaining'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimit-Reset'
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/problem+json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Problem'
                  - properties:
                      status:
                        const: 409
                      code:
                        enum:
                          - IDEMPOTENCY_KEY_IN_USE
                          - OFFER_ARCHIVED
                          - OFFER_VERSION_CONFLICT
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          description: 'Unprocessable: `IDEMPOTENCY_KEY_REUSED`.'
          headers:
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimit-Limit'
            RateLimit-Remaining:
              $ref: '#/components/headers/RateLimit-Remaining'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimit-Reset'
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/problem+json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Problem'
                  - properties:
                      status:
                        const: 422
                      code:
                        enum:
                          - IDEMPOTENCY_KEY_REUSED
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
        '503':
          $ref: '#/components/responses/UnavailableForWrite'
components:
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: >-
        Makes a write safe to retry: the same key with the same method, path,
        query and body within 24 hours answers the stored result
        (`Idempotent-Replayed: true`) without running the write again. Only a
        successful answer is stored, so a retry after a failure runs. A key
        reused for a different request is 422 `IDEMPOTENCY_KEY_REUSED`; one
        whose first request still runs is 409 `IDEMPOTENCY_KEY_IN_USE`.
      schema:
        type: string
        minLength: 1
        maxLength: 255
        pattern: ^[\x21-\x7E]+$
      example: 3f2a1c9e-8a4b-4f5e-9d61-2c7b0e4a91d3
  schemas:
    UpdateOfferInput:
      type: object
      properties:
        expectedVersion:
          anyOf:
            - minimum: 1
              type: integer
            - type: 'null'
        title:
          anyOf:
            - maxLength: 200
              type: string
            - type: 'null'
        kind:
          anyOf:
            - type: string
              enum:
                - quantity
                - free_delivery
                - product_discount
                - order_discount
                - bxgy
                - addon
                - app
            - type: 'null'
        method:
          anyOf:
            - type: string
              enum:
                - automatic
                - code
            - type: 'null'
        label:
          anyOf:
            - type: object
              additionalProperties: true
              description: >-
                Label is the offer's stored settings for its kind, exactly as
                the Offers page keeps them.
            - type: 'null'
        config:
          anyOf:
            - type: object
              additionalProperties: true
              description: >-
                Config is the offer's stored settings for its kind, exactly as
                the Offers page keeps them.
            - type: 'null'
        combinesWith:
          anyOf:
            - type: object
              additionalProperties: true
              description: >-
                CombinesWith is the offer's stored settings for its kind,
                exactly as the Offers page keeps them.
            - type: 'null'
        eligibility:
          anyOf:
            - type: object
              additionalProperties: true
              description: >-
                Eligibility is the offer's stored settings for its kind, exactly
                as the Offers page keeps them.
            - type: 'null'
        totalUsageLimit:
          anyOf:
            - minimum: 1
              type: integer
            - type: 'null'
        perBuyerLimit:
          anyOf:
            - minimum: 1
              type: integer
            - type: 'null'
        startsAt:
          anyOf:
            - maxLength: 40
              type: string
            - type: 'null'
        endsAt:
          anyOf:
            - maxLength: 40
              type: string
            - type: 'null'
        targets:
          anyOf:
            - maxItems: 101
              type: array
              items:
                $ref: '#/components/schemas/OfferTargetInput'
            - type: 'null'
      additionalProperties: false
    OfferResponse:
      type: object
      properties:
        data:
          $ref: '#/components/schemas/Offer'
      required:
        - data
      additionalProperties: false
    Problem:
      type: object
      description: >-
        An Admin API error (RFC 9457 problem details), served as
        `application/problem+json`. Act on `code`, never on `detail`. Other
        members name the subject of the refusal (`permission`, `storeId`,
        `feature`, `limit`, `errors`, …).
      properties:
        type:
          type: string
          format: uri
          description: >-
            `https://developers.tassyir.io/errors/<code in kebab case>`:
            identifies the code, and is the code's page.
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
          description: >-
            For a person. A 5xx never carries an internal message: `requestId`
            names it.
        code:
          type: string
          pattern: ^[A-Z][A-Z0-9_]{1,63}$
          description: 'Stable: what the app acts on.'
        fix:
          type: string
          description: One sentence an AI agent can act on.
        docs:
          type: string
          format: uri
          description: >-
            The page that explains the code and what to do: the same URL as
            `type`.
        requestId:
          type: string
          description: Also in the `X-Request-Id` header. Quote it to Tassyir.
        errors:
          type: array
          items:
            type: string
          description: 'With `VALIDATION_FAILED`: one entry per invalid parameter.'
      required:
        - type
        - title
        - status
        - detail
        - code
        - docs
    OfferTargetInput:
      type: object
      properties:
        role:
          anyOf:
            - type: string
              enum:
                - buy
                - get
                - addon
            - type: 'null'
        refType:
          type: string
          enum:
            - all
            - product
            - variant
            - collection
        refId:
          anyOf:
            - type: string
              pattern: ^\d{1,24}$
              description: An id (a string of digits).
            - type: 'null'
      additionalProperties: false
      required:
        - refType
    Offer:
      type: object
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 64
          description: A resource id (a snowflake id, as a string).
        storeId:
          type: string
          minLength: 1
          maxLength: 64
          description: The store the offer belongs to.
        kind:
          type: string
          description: >-
            quantity, free_delivery, product_discount, order_discount, bxgy,
            addon or app. Ignore a kind you do not know.
        method:
          type: string
          description: automatic or code.
        title:
          type: string
          description: The merchant's name for the offer.
        label:
          anyOf:
            - type: object
              properties:
                ar:
                  anyOf:
                    - type: string
                    - type: 'null'
                fr:
                  anyOf:
                    - type: string
                    - type: 'null'
                en:
                  anyOf:
                    - type: string
                    - type: 'null'
              required:
                - ar
                - fr
                - en
              additionalProperties: false
            - type: 'null'
          description: What the buyer reads, per language.
        status:
          type: string
          description: >-
            draft, scheduled, active, paused or ended (derived from the dates
            and the lifecycle timestamps; an archived offer is ended).
        currency:
          type: string
          pattern: ^[A-Z]{3}$
          description: ISO 4217 currency code of every amount in this resource.
        quantity:
          anyOf:
            - type: object
              properties:
                priceMode:
                  type: string
                  description: total, percent or amount_each.
                tiers:
                  type: array
                  items:
                    type: object
                    properties:
                      pieces:
                        type: integer
                        minimum: -9007199254740991
                        maximum: 9007199254740991
                        description: Pieces the tier is for.
                      label:
                        type: object
                        properties:
                          ar:
                            anyOf:
                              - type: string
                              - type: 'null'
                          fr:
                            anyOf:
                              - type: string
                              - type: 'null'
                          en:
                            anyOf:
                              - type: string
                              - type: 'null'
                        required:
                          - ar
                          - fr
                          - en
                        additionalProperties: false
                        description: What the buyer reads on the tier card.
                      total:
                        anyOf:
                          - type: string
                            pattern: ^-?\d+\.\d{2}$
                            description: >-
                              A decimal amount with two decimals, as a string
                              ("2500.00").
                          - type: 'null'
                        description: 'priceMode total: the whole price of the tier.'
                      percentOff:
                        anyOf:
                          - type: number
                          - type: 'null'
                        description: 'priceMode percent: 1 to 100, off each piece.'
                      amountOffEach:
                        anyOf:
                          - type: string
                            pattern: ^-?\d+\.\d{2}$
                            description: >-
                              A decimal amount with two decimals, as a string
                              ("2500.00").
                          - type: 'null'
                        description: 'priceMode amount_each: DZD off each piece.'
                      preselected:
                        type: boolean
                      freeDelivery:
                        type: boolean
                    required:
                      - pieces
                      - label
                      - total
                      - percentOff
                      - amountOffEach
                      - preselected
                      - freeDelivery
                    additionalProperties: false
              required:
                - priceMode
                - tiers
              additionalProperties: false
            - type: 'null'
          description: kind quantity only.
        freeDelivery:
          anyOf:
            - type: object
              properties:
                thresholdKind:
                  anyOf:
                    - type: string
                    - type: 'null'
                  description: pieces or amount; null while the offer has none (a draft).
                thresholdPieces:
                  anyOf:
                    - type: integer
                      minimum: -9007199254740991
                      maximum: 9007199254740991
                    - type: 'null'
                thresholdAmount:
                  anyOf:
                    - type: string
                      pattern: ^-?\d+\.\d{2}$
                      description: >-
                        A decimal amount with two decimals, as a string
                        ("2500.00").
                    - type: 'null'
                delivery:
                  type: string
                  description: home, desk or both.
                wilayas:
                  type: array
                  items:
                    type: string
                  description: Two-digit wilaya codes; empty = every wilaya.
              required:
                - thresholdKind
                - thresholdPieces
                - thresholdAmount
                - delivery
                - wilayas
              additionalProperties: false
            - type: 'null'
          description: kind free_delivery only.
        productDiscount:
          anyOf:
            - type: object
              properties:
                valueType:
                  type: string
                  description: percent or fixed.
                percent:
                  anyOf:
                    - type: number
                    - type: 'null'
                  description: 'valueType percent: 1 to 100.'
                amount:
                  anyOf:
                    - type: string
                      pattern: ^-?\d+\.\d{2}$
                      description: >-
                        A decimal amount with two decimals, as a string
                        ("2500.00").
                    - type: 'null'
                  description: 'valueType fixed: DZD off each unit.'
                maxAmount:
                  anyOf:
                    - type: string
                      pattern: ^-?\d+\.\d{2}$
                      description: >-
                        A decimal amount with two decimals, as a string
                        ("2500.00").
                    - type: 'null'
                  description: Cap on a percentage saving per unit, DZD.
                showCountdown:
                  type: boolean
                  description: A countdown to the offer's end date is shown.
              required:
                - valueType
                - percent
                - amount
                - maxAmount
                - showCountdown
              additionalProperties: false
            - type: 'null'
          description: kind product_discount only.
        orderDiscount:
          anyOf:
            - type: object
              properties:
                valueType:
                  type: string
                  description: percent or fixed.
                percent:
                  anyOf:
                    - type: number
                    - type: 'null'
                  description: 'valueType percent: 1 to 100.'
                amount:
                  anyOf:
                    - type: string
                      pattern: ^-?\d+\.\d{2}$
                      description: >-
                        A decimal amount with two decimals, as a string
                        ("2500.00").
                    - type: 'null'
                  description: 'valueType fixed: DZD off the order.'
                maxAmount:
                  anyOf:
                    - type: string
                      pattern: ^-?\d+\.\d{2}$
                      description: >-
                        A decimal amount with two decimals, as a string
                        ("2500.00").
                    - type: 'null'
                  description: 'valueType percent: the most the order''s saving may reach.'
                minimumKind:
                  anyOf:
                    - type: string
                    - type: 'null'
                  description: pieces or amount; null = every order.
                minimumPieces:
                  anyOf:
                    - type: integer
                      minimum: -9007199254740991
                      maximum: 9007199254740991
                    - type: 'null'
                minimumAmount:
                  anyOf:
                    - type: string
                      pattern: ^-?\d+\.\d{2}$
                      description: >-
                        A decimal amount with two decimals, as a string
                        ("2500.00").
                    - type: 'null'
              required:
                - valueType
                - percent
                - amount
                - maxAmount
                - minimumKind
                - minimumPieces
                - minimumAmount
              additionalProperties: false
            - type: 'null'
          description: kind order_discount only.
        bxgy:
          anyOf:
            - type: object
              properties:
                buyPieces:
                  type: integer
                  minimum: -9007199254740991
                  maximum: 9007199254740991
                  description: Paid pieces of the buy targets per set.
                getPieces:
                  type: integer
                  minimum: -9007199254740991
                  maximum: 9007199254740991
                  description: Discounted pieces per set.
                getDiscountType:
                  type: string
                  description: >-
                    percent (100 = free) or fixed_price (the gift price per
                    piece).
                getPercent:
                  anyOf:
                    - type: number
                    - type: 'null'
                getPrice:
                  anyOf:
                    - type: string
                      pattern: ^-?\d+\.\d{2}$
                      description: >-
                        A decimal amount with two decimals, as a string
                        ("2500.00").
                    - type: 'null'
                maxPerOrder:
                  anyOf:
                    - type: integer
                      minimum: -9007199254740991
                      maximum: 9007199254740991
                    - type: 'null'
                  description: Sets per order; null = as many as the cart holds.
              required:
                - buyPieces
                - getPieces
                - getDiscountType
                - getPercent
                - getPrice
                - maxPerOrder
              additionalProperties: false
            - type: 'null'
          description: kind bxgy only (buy X get Y).
        addon:
          anyOf:
            - type: object
              properties:
                priceType:
                  anyOf:
                    - type: string
                    - type: 'null'
                  description: >-
                    fixed_price, percent or amount_off; null while a draft has
                    none.
                price:
                  anyOf:
                    - type: string
                      pattern: ^-?\d+\.\d{2}$
                      description: >-
                        A decimal amount with two decimals, as a string
                        ("2500.00").
                    - type: 'null'
                  description: priceType fixed_price.
                percent:
                  anyOf:
                    - type: number
                    - type: 'null'
                  description: priceType percent.
                amountOff:
                  anyOf:
                    - type: string
                      pattern: ^-?\d+\.\d{2}$
                      description: >-
                        A decimal amount with two decimals, as a string
                        ("2500.00").
                    - type: 'null'
                  description: priceType amount_off.
                maxQuantity:
                  type: integer
                  minimum: -9007199254740991
                  maximum: 9007199254740991
                beforeSubmit:
                  type: boolean
                  description: Offered in the order form.
                afterSubmit:
                  type: boolean
                  description: Offered on the thank-you page.
              required:
                - priceType
                - price
                - percent
                - amountOff
                - maxQuantity
                - beforeSubmit
                - afterSubmit
              additionalProperties: false
            - type: 'null'
          description: kind addon only.
        appRule:
          anyOf:
            - type: object
              properties:
                typeHandle:
                  type: string
                  description: The owning app's [[offers.types]] handle.
                rule:
                  description: >-
                    The declarative rule as the app wrote it: { class, when,
                    then } (see the offers guide).
              required:
                - typeHandle
                - rule
              additionalProperties: false
            - type: 'null'
          description: 'kind app only: an app-owned offer type and its rule.'
        combinesWith:
          type: object
          properties:
            product:
              type: boolean
            order:
              type: boolean
            delivery:
              type: boolean
          required:
            - product
            - order
            - delivery
          additionalProperties: false
          description: Which classes of offer this one stacks with.
        eligibility:
          type: object
          properties:
            audience:
              type: string
              description: everyone, first_order or returning.
            channels:
              type: array
              items:
                type: string
              description: store, landing, shopify, dashboard, apps; empty = all.
            wilayas:
              type: array
              items:
                type: string
              description: Empty = every wilaya.
            deliveryMethods:
              type: array
              items:
                type: string
              description: home, desk; empty = both.
          required:
            - audience
            - channels
            - wilayas
            - deliveryMethods
          additionalProperties: false
        targets:
          type: array
          items:
            type: object
            properties:
              role:
                type: string
                description: buy (what the offer applies to), get or addon.
              type:
                type: string
                description: all, product, variant or collection.
              id:
                anyOf:
                  - type: string
                    minLength: 1
                    maxLength: 64
                    description: A resource id (a snowflake id, as a string).
                  - type: 'null'
                description: Null when type is all.
            required:
              - role
              - type
              - id
            additionalProperties: false
          description: What the offer applies to (empty for a free-delivery offer).
        codeCount:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
          description: Codes buyers can still use (0 for an automatic offer).
        totalUsageLimit:
          anyOf:
            - type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            - type: 'null'
          description: Total uses; null = unlimited.
        perBuyerLimit:
          anyOf:
            - type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            - type: 'null'
          description: Uses per buyer (phone number); null = unlimited.
        startsAt:
          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.
        endsAt:
          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'
        publishedAt:
          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'
          description: Null while the offer is a draft.
        pausedAt:
          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'
        archivedAt:
          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'
        appOwned:
          type: boolean
          description: The offer belongs to an installed app (phase 2 kinds).
        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.
        updatedAt:
          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.
      required:
        - id
        - storeId
        - kind
        - method
        - title
        - label
        - status
        - currency
        - quantity
        - freeDelivery
        - productDiscount
        - orderDiscount
        - bxgy
        - addon
        - appRule
        - combinesWith
        - eligibility
        - targets
        - codeCount
        - totalUsageLimit
        - perBuyerLimit
        - startsAt
        - endsAt
        - publishedAt
        - pausedAt
        - archivedAt
        - appOwned
        - createdAt
        - updatedAt
      additionalProperties: false
      description: An offer (API 2026-10).
  headers:
    RateLimit-Limit:
      description: >-
        The installation's request bucket (default 40); on a 429
        `BUSINESS_RATE_LIMITED`, the business's ceiling (default 20 a second).
        Absent when a request is refused before the rate limits run (an
        unparsable body, a 503 `API_BUSY`).
      schema:
        minimum: 1
        type: integer
    RateLimit-Remaining:
      description: Requests still accepted right now.
      schema:
        type: integer
        minimum: 0
    RateLimit-Reset:
      description: >-
        Seconds until the bucket is empty again (it drains at 4 requests a
        second by default).
      schema:
        type: integer
        minimum: 0
    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 an answer.'
      schema:
        type: string
        const: no-store
    Idempotent-Replayed:
      description: >-
        Present on the stored answer of a write repeated with the same
        `Idempotency-Key`: the write did not run again.
      schema:
        type: string
        const: 'true'
    Retry-After:
      description: Seconds to wait before retrying.
      schema:
        type: integer
        minimum: 0
  responses:
    Unauthorized:
      description: >-
        No usable credential: `APP_TOKEN_INVALID`, `APP_TOKEN_REQUIRED`,
        `HOSTED_CALL_REFUSED`.
      headers:
        RateLimit-Limit:
          $ref: '#/components/headers/RateLimit-Limit'
        RateLimit-Remaining:
          $ref: '#/components/headers/RateLimit-Remaining'
        RateLimit-Reset:
          $ref: '#/components/headers/RateLimit-Reset'
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
        WWW-Authenticate:
          description: >-
            The challenge: `Bearer` (RFC 6750) for an access token,
            `Tassyir-Hosted` for a hosted app's call through
            `ctx.props.TASSYIR`.
          schema:
            type: string
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 401
                  code:
                    enum:
                      - APP_TOKEN_INVALID
                      - APP_TOKEN_REQUIRED
                      - HOSTED_CALL_REFUSED
    PayloadTooLarge:
      description: 'Body too large: `PAYLOAD_TOO_LARGE`.'
      headers:
        RateLimit-Limit:
          $ref: '#/components/headers/RateLimit-Limit'
        RateLimit-Remaining:
          $ref: '#/components/headers/RateLimit-Remaining'
        RateLimit-Reset:
          $ref: '#/components/headers/RateLimit-Reset'
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 413
                  code:
                    enum:
                      - PAYLOAD_TOO_LARGE
    UnsupportedMediaType:
      description: 'Unsupported body: `UNSUPPORTED_MEDIA_TYPE`.'
      headers:
        RateLimit-Limit:
          $ref: '#/components/headers/RateLimit-Limit'
        RateLimit-Remaining:
          $ref: '#/components/headers/RateLimit-Remaining'
        RateLimit-Reset:
          $ref: '#/components/headers/RateLimit-Reset'
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 415
                  code:
                    enum:
                      - UNSUPPORTED_MEDIA_TYPE
    RateLimited:
      description: 'Rate limited: `BUSINESS_RATE_LIMITED`, `RATE_LIMITED`.'
      headers:
        RateLimit-Limit:
          $ref: '#/components/headers/RateLimit-Limit'
        RateLimit-Remaining:
          $ref: '#/components/headers/RateLimit-Remaining'
        RateLimit-Reset:
          $ref: '#/components/headers/RateLimit-Reset'
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
        Retry-After:
          $ref: '#/components/headers/Retry-After'
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 429
                  code:
                    enum:
                      - BUSINESS_RATE_LIMITED
                      - RATE_LIMITED
    InternalError:
      description: 'Internal error: `INTERNAL_ERROR`.'
      headers:
        RateLimit-Limit:
          $ref: '#/components/headers/RateLimit-Limit'
        RateLimit-Remaining:
          $ref: '#/components/headers/RateLimit-Remaining'
        RateLimit-Reset:
          $ref: '#/components/headers/RateLimit-Reset'
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 500
                  code:
                    enum:
                      - INTERNAL_ERROR
    UnavailableForWrite:
      description: >-
        Unavailable: `API_BUSY`, `IDEMPOTENCY_UNAVAILABLE`,
        `SERVICE_UNAVAILABLE`.
      headers:
        RateLimit-Limit:
          $ref: '#/components/headers/RateLimit-Limit'
        RateLimit-Remaining:
          $ref: '#/components/headers/RateLimit-Remaining'
        RateLimit-Reset:
          $ref: '#/components/headers/RateLimit-Reset'
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
        Retry-After:
          $ref: '#/components/headers/Retry-After'
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 503
                  code:
                    enum:
                      - API_BUSY
                      - IDEMPOTENCY_UNAVAILABLE
                      - SERVICE_UNAVAILABLE
  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).

````

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