Skip to main content
The Admin API is the versioned REST API your app calls to read and change a business. Every route follows the conventions on this page, so learn them once: how lists page, how a write is made safe to retry, how errors read and how fast you may call.

Calling it

https://api.tassyir.io/apps/2026-10/<resource>, with an access token:
The business comes from the token: no route takes a business id. GET /business needs no scope; every other route needs the scope its guide names. With @tassyir/app, paths are relative to /apps/2026-10, and paths, parameters, bodies and answers are typed:

Versions

The version is in the path: 2026-10 is the first and, today, the only one. Within a version, fields may be added, never renamed, retyped or removed, so ignore fields you do not know. A version that does not exist answers 404. Versions are released quarterly and supported for 12 months. Tassyir emails you 90 days before a version you use is retired; once it is, its requests are answered by the oldest supported version, with the headers Tassyir-Api-Version-Served and Deprecation.

Values

  • JSON fields are camelCase. Ids are strings: never parse them as numbers. Times are ISO 8601 in UTC (2026-10-11T09:12:44.120Z).
  • Money is a decimal string with two decimals ("2500.00") next to a currency field (DZD today), never a number. Keep it a string; for arithmetic, use a decimal library or integer cents, never parseFloat. An amount Tassyir does not know is null, never "0.00". When you send money (a product’s price), send a decimal string too.
  • Open vocabularies (an order’s status, a carrier’s status) are strings, not enums: a merchant or a carrier can add values.
  • Contact fields (a buyer’s name, phones, email, address, an order’s notes) are present only with read_customer_contact. Without it the keys are absent, not null (Customers and contact data).

One resource, and lists

A single resource is { "data": { … } }. A list is { "data": [ … ], "nextCursor": "…" | null }:
  • Lists are newest first and keyset-paged: a row added while you walk a list never shifts the pages to come, and no row is repeated or skipped. New rows appear on a new walk from the first page.
  • nextCursor is opaque: never build or edit one. It belongs to its list and its filters; with another list or other filters it is 400 INVALID_CURSOR. nextCursor: null is the last page.
  • A page can hold fewer than limit items while nextCursor is set (app data drops values whose owner you can no longer read). Stop on null, not on a short page.
  • Unknown parameters are refused (400 VALIDATION_FAILED, naming them): a misspelt filter never silently returns everything.
admin.paginate walks every page for you:

Fields

?fields=status,total,buyer trims each resource to the named top-level fields; id is always kept. An unknown name is 400 INVALID_FIELDS, with the allowedFields. Trimming happens after masking: naming a field your installation may not read returns nothing for it. With the SDK, fields is a typed list: fields: ['status', 'total'].

Idempotent writes

Every write (POST, PUT, PATCH, DELETE) accepts an Idempotency-Key header: 1 to 255 visible ASCII characters, usually a UUID. Send one, and a retry of the same write never runs it twice. Keys belong to your installation and are kept 24 hours. @tassyir/app sends a new key with every write and reuses it for its own retries. Pass your own when a retry may come from elsewhere (a job that can run twice), or false to send none:

Errors

Every error under /apps/2026-10/ is application/problem+json (RFC 9457):
  • Act on code, never on detail or title: code is stable.
  • fix is one sentence saying what to do, when there is one.
  • Other members name what was refused: permission, storeId, limit, errors, allowedFields, …
  • requestId (also the X-Request-Id header) names the request: quote it when you ask Tassyir for help.
  • A 5xx never carries an internal message: retry with backoff.
@tassyir/app turns a problem into a TassyirApiError, with code, fix, status, requestId, retryAfterSeconds and the whole problem. The codes each call can answer are typed:
Errors lists every code, with what to do about the common ones. The token endpoint is the one exception: it answers OAuth errors (Tokens).

Stores: 460, and 404

An installation reaches only the stores its merchant gave it. A store your request names (a storeId filter or field) outside them is HTTP 460 STORE_SCOPE_FORBIDDEN. A resource of another store, named by its id, reads as 404 NOT_FOUND: the API never says it exists.

Rate limits

Every answer carries your installation’s bucket:
  • Your installation’s bucket: by default 40 requests at once, refilled at 4 a second. Past it: 429 RATE_LIMITED, with Retry-After (seconds until one more request fits). Online and offline tokens of an installation share one bucket.
  • The business’s ceiling: all the apps of one business share, by default, 20 requests at once and 20 a second. Past it: 429 BUSINESS_RATE_LIMITED, with Retry-After; then other apps of the business are using the room, so spread your calls out.
  • A busy API: when the API is already running its maximum of app requests, it answers 503 API_BUSY with Retry-After: 1, having done nothing: any call, a write included, can be retried as it was.
  • Image uploads have their own daily quota (Products and collections).
The numbers are defaults Tassyir may tune: read the headers rather than hard-coding them. Retries are off by default in @tassyir/app; with retries: 3 it waits Retry-After and retries 429 and 503 API_BUSY (writes included), any other 503, network errors, 502 and 504 for calls safe to repeat (reads, and writes with a key), and 500 for reads only.

Plans

No plan is needed to install an app or call the API. A plan’s limits still apply to what your app does, as they apply to the merchant: the service refuses with the code the dashboard would show (PLAN_FEATURE_REQUIRED, ORDER_LIMIT_REACHED, SUBSCRIPTION_REQUIRED).

Masking

  • Without read_customer_contact, contact fields are absent.
  • When the merchant’s plan hides phones (no active subscription, or a billing grace that hides them), phones read **********.
  • A waiting order (past the plan’s order allowance, waiting: true) is masked whatever your scopes: phones ********** and notes null, until it unlocks (orders/updated with waiting).

Caching

Every answer is Cache-Control: no-store: it is a merchant’s private data. Cache only what your app needs, and drop it on app/uninstalled.