Skip to main content

Qashier Open API (2026-10-31)

Push paid orders into Qashier, pull products and stock, and receive webhooks when stock moves or an order is cancelled.

Authentication. A bearer API key, created by the account owner in HQ under Settings → API. The key is bound to one client and to a set of that client's stores. The client is never sent — it comes from the key — and every storeId, in a path or a body, must be inside the key's scope: a store that is not is 403 STORE_NOT_IN_KEY_SCOPE, and a store id that does not exist or belongs to someone else is 404 STORE_NOT_FOUND. A key can only ever act on its own merchant's data.

Money is an integer in minor units throughout — 4550 is MYR 45.50. No floats cross this boundary.

Idempotency. POST /orders requires X-Idempotency-Key. A replay returns the original order; it does not create a second transaction, deduct stock again, or print again.

Rate limits. Every response to an authenticated request carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. A 429 also carries Retry-After in seconds. A 401 carries none of them: the bucket is per credential, and there is no credential.

Request ids. Every response carries X-Request-Id. Quote it to support — it resolves to one trace on our side.

Webhooks. inventory.changed fires for every stock movement in the store whatever caused it, and transaction.cancelled when a cancellation finalizes — which is not the moment staff start one, so read the order back for in-flight state. Delivery is at-least-once and unordered: dedupe on eventId, and for stock keep the latest occurredAt per SKU and drop anything older — resultingBalance is absolute, which makes that comparison enough, but it does not make it optional. Each delivery is signed as X-Qashier-Signature: t=<unix>,v1=<hex>, an HMAC-SHA256 over "{timestamp}.{rawBody}"; reject anything older than five minutes. Payload shapes are the InventoryChangedEvent and TransactionCancelledEvent components.

If a delivery fails we retry with exponential backoff for 24 hours. Reconcile daily against GET /stores/{storeId}/stock — that pull is the recovery path for any event we gave up on.

Stores

Stores reachable by your key

List stores

Every store this credential can reach. Lets a partner discover store ids instead of being sent a list by email, which goes stale the first time the merchant opens an outlet.

Authorizations:
apiKey
query Parameters
limit
integer [ 1 .. 200 ]
Default: 100
cursor
string non-empty

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "pagination": {
    }
}

Catalogue

Products, variants, modifiers, stock

List products for a store

The catalogue a partner builds its menu from: per-store price, modifiers, variants, SKUs and the track-inventory flag. duplicateSku marks a SKU that matches more than one product, because such a line becomes a custom item with no stock movement and the partner should see that here rather than as stock drift later.

Authorizations:
apiKey
path Parameters
storeId
required
string [ 1 .. 64 ] characters
query Parameters
limit
integer [ 1 .. 200 ]
Default: 100
cursor
string non-empty

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "pagination": {
    }
}

Latest stock for a store

Current quantity per SKU. This is also the daily reconciliation path: webhook delivery is at-least-once and unordered, so a partner that missed events converges by pulling this.

Authorizations:
apiKey
path Parameters
storeId
required
string [ 1 .. 64 ] characters
query Parameters
limit
integer [ 1 .. 200 ]
Default: 100
cursor
string non-empty
string or Array of strings

Repeatable. Omit for every tracked item in the store

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "pagination": {
    }
}

Orders

Create and read paid orders

Create a paid order

Creates the transaction already paid, in one call. Qashier recomputes the total from the store's own tax, service-charge and rounding settings and rejects the order if it differs from amountCharged — nothing is persisted, deducted or printed on a rejection. X-Idempotency-Key is required; send the partner order id.

Authorizations:
apiKey
header Parameters
x-idempotency-key
required
string [ 1 .. 200 ] characters

Required. Send your own order id. A replay returns the original order rather than creating a second one; the same key with a different body is a 409.

Request Body schema: application/json
required
storeId
required
string [ 1 .. 64 ] characters
externalOrderReference
required
string [ 1 .. 100 ] characters
fulfilmentType
required
string
Enum: "PICKUP" "DELIVERY"
required
Array of objects [ 1 .. 200 ] items
Array ([ 1 .. 200 ] items)
sku
string [ 1 .. 64 ] characters

The merchant's SKU. Optional and not unique on a Qashier product

name
required
string [ 1 .. 200 ] characters
quantity
required
integer [ 1 .. 999 ]
unitPrice
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

Array of objects <= 50 items
Default: []
Array of objects <= 20 items
Default: []
note
string <= 500 characters
Array of objects <= 20 items
Default: []
Array (<= 20 items)
type
required
string
Enum: "PERCENTAGE" "FIXED_AMOUNT"
value
required
number
reason
string <= 200 characters
code
string <= 64 characters
amountCharged
required
integer >= 0

What the partner actually collected. Rejected if it differs from our computed total

currency
required
string^[A-Z]{3}$

ISO 4217, uppercase

required
object
reference
required
string [ 1 .. 200 ] characters
paidAt
required
string <date-time>
note
string <= 1000 characters
printOrderChit
boolean
Default: true
showInKds
boolean
Default: true

Responses

Request samples

Content type
application/json
{
  • "storeId": "string",
  • "externalOrderReference": "string",
  • "fulfilmentType": "PICKUP",
  • "lineItems": [
    ],
  • "discounts": [ ],
  • "amountCharged": 0,
  • "currency": "string",
  • "payment": {
    },
  • "note": "string",
  • "printOrderChit": true,
  • "showInKds": true
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Read an order back

What we stored, keyed by the transactionId the create answered with. Needed for partner-side reconciliation and so support can answer a question without a Qashier login.

Keep that id against your own order. If you lose it, replay the create with the same X-Idempotency-Key — a replay returns the original order rather than creating a second one, so the id comes back. ORDER_NOT_FOUND also answers a transaction belonging to another client, so this read cannot be used to discover whether an id exists elsewhere.

This is also where a cancellation in progress is visible. A cancellation reverses several things and finalizes only when all of them are done, and the refund leg of an order paid on your own gateway waits for a cashier — so status: CANCELLING with per-task detail in cancellation.tasks is the normal path, not a rare window. transaction.cancelled is delivered when the cancellation finalizes, so poll this to see where a cancellation has got to before then.

Authorizations:
apiKey
path Parameters
transactionId
required
string [ 1 .. 100 ] characters

The transactionId the create answered with. It is Qashier's id for the order, not your own reference.

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Webhooks

Events we POST to the URL on your credential. Requests you serve, not requests you make.

Stock moved in a store Webhook

Fires for every stock movement in a store whose credential has a webhook URL, whatever caused it: a sale through this API, a sale on the POS, a restock or adjustment in HQ, a stock take, a transfer, or a cancellation putting quantities back. reason says which. A product that does not track inventory never produces one, and neither does a custom line whose SKU matched nothing.

Ordering. Two events for the same SKU can arrive in either order. quantityChange is what moved and resultingBalance is the quantity after it moved, so never apply quantityChange as a delta — but an absolute value does not make order irrelevant: if you store whatever arrives last, a reversed pair leaves you on the older number. Keep the occurredAt you last applied per SKU and drop any event whose occurredAt is earlier. Compare occurredAt (when stock moved), not createdAt (when we emitted the event).

If you miss events, GET /stores/{storeId}/stock is the recovery path and the daily reconciliation path. It is the authority; a webhook is a notification.

Delivery is at-least-once and unordered. Dedupe on eventId, which is stable across every retry of the same event. Order between different events is not guaranteed either; what that means for you is specific to the event and described above.

Retries. An attempt that does not return 2xx is retried with exponential backoff and equal jitter: the window doubles from 10 seconds to a ceiling of 1 hour, and each attempt fires at a random point in the second half of its window, so a fleet of failed deliveries does not return as a thundering herd. We give up after 30 attempts or 24 hours, whichever comes first.

Sustained failure. After 20 consecutive failures across all events we pause delivery for your credential and probe every 5 minutes; two successful probes resume it. Queued events are not discarded while paused — they drain once delivery resumes, subject to the 24-hour limit above.

header Parameters
X-Qashier-Signature
required
string

Signature of this delivery: t=<unix>,v1=<hex>, an HMAC-SHA256 over "{timestamp}.{rawBody}" keyed with the webhook secret on your credential. Verify it against the raw body before parsing, and reject a timestamp older than five minutes — otherwise a captured delivery can be replayed at you. Compare with a constant-time function.

X-Request-Id
string

The trace id of the delivery attempt. Quote it to support and it resolves to one trace on our side.

Request Body schema: application/json
required
eventId
required
string <uuid>
type
required
string
Value: "inventory.changed"
createdAt
required
string <date-time>
required
object
storeId
required
string [ 1 .. 64 ] characters
sku
required
string or null [ 1 .. 64 ] characters

The merchant's SKU. Optional and not unique on a Qashier product

productId
required
string non-empty
variantId
required
string or null non-empty
productName
required
string non-empty
quantityChange
required
number
resultingBalance
required
number
reason
required
string
Enum: "SALE" "RESTOCK" "ADJUSTMENT" "STOCK_TAKE" "CANCELLATION_RESTORE" "TRANSFER"
transactionId
required
string or null non-empty
occurredAt
required
string <date-time>

Responses

Request samples

Content type
application/json
{
  • "eventId": "d6703cc8-9e79-415d-ac03-a4dc7f6ab43c",
  • "type": "inventory.changed",
  • "createdAt": "2019-08-24T14:15:22Z",
  • "data": {
    }
}

An order was cancelled Webhook

Fires when a cancellation finalizes, not when staff start one. A cancellation reverses several things — stock, the refund leg, loyalty, a table session — and finalizes once every one of them is terminal; until then the order reads status: CANCELLING on GET /orders/{transactionId} with a task breakdown. Because Qashier never held the money for your orders, the refund leg waits for a cashier to record how it was returned, so expect a gap between the staff action and this event rather than treating it as immediate.

It reports that the order is cancelled in Qashier and what staff recorded as refunded, which may be 0 — it does not move money. Refunding the customer is yours to do on the gateway that took the payment. Each restored line also produces an inventory.changed with reason: CANCELLATION_RESTORE.

outcome says whether every reversal succeeded. COMPLETED_WITH_FAILURES means the order is cancelled but at least one reversal did not go through and staff have to finish it by hand — read the order back for cancellation.tasks to see which.

Ordering. One order cancels once, so there is nothing to order between deliveries of this event. It can arrive before or after the inventory.changed events for the same cancellation; neither implies the other has been delivered.

If you miss it, GET /orders/{transactionId} shows status: CANCELLED and the cancellation block with the same outcome. That read is the authority; this event is a notification.

Delivery is at-least-once and unordered. Dedupe on eventId, which is stable across every retry of the same event. Order between different events is not guaranteed either; what that means for you is specific to the event and described above.

Retries. An attempt that does not return 2xx is retried with exponential backoff and equal jitter: the window doubles from 10 seconds to a ceiling of 1 hour, and each attempt fires at a random point in the second half of its window, so a fleet of failed deliveries does not return as a thundering herd. We give up after 30 attempts or 24 hours, whichever comes first.

Sustained failure. After 20 consecutive failures across all events we pause delivery for your credential and probe every 5 minutes; two successful probes resume it. Queued events are not discarded while paused — they drain once delivery resumes, subject to the 24-hour limit above.

header Parameters
X-Qashier-Signature
required
string

Signature of this delivery: t=<unix>,v1=<hex>, an HMAC-SHA256 over "{timestamp}.{rawBody}" keyed with the webhook secret on your credential. Verify it against the raw body before parsing, and reject a timestamp older than five minutes — otherwise a captured delivery can be replayed at you. Compare with a constant-time function.

X-Request-Id
string

The trace id of the delivery attempt. Quote it to support and it resolves to one trace on our side.

Request Body schema: application/json
required
eventId
required
string <uuid>
type
required
string
Value: "transaction.cancelled"
createdAt
required
string <date-time>
required
object
storeId
required
string [ 1 .. 64 ] characters
transactionId
required
string non-empty
externalOrderReference
required
string non-empty
receiptNumber
required
string non-empty
orderTotal
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

refundAmount
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

currency
required
string^[A-Z]{3}$

ISO 4217, uppercase

reason
required
string or null <= 500 characters
cancelledBy
required
string non-empty
cancelledAt
required
string <date-time>
outcome
required
string
Enum: "COMPLETED" "COMPLETED_WITH_FAILURES"

Same values as cancellation.status on the order once finalized. COMPLETED_WITH_FAILURES: at least one reversal failed and staff have to finish it by hand; read GET /orders/{transactionId} for cancellation.tasks.

Responses

Request samples

Content type
application/json
{
  • "eventId": "d6703cc8-9e79-415d-ac03-a4dc7f6ab43c",
  • "type": "transaction.cancelled",
  • "createdAt": "2019-08-24T14:15:22Z",
  • "data": {
    }
}

Schemas

ErrorResponse

code
required
string^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*$
message
required
string non-empty
details
object
{
  • "code": "string",
  • "message": "string",
  • "details": { }
}

ResponseMeta

timestamp
required
string <date-time>
requestId
required
string non-empty
{
  • "timestamp": "2019-08-24T14:15:22Z",
  • "requestId": "string"
}

OffsetPagination

page
required
integer >= 1
size
required
integer [ 1 .. 100 ]
total
required
integer >= 0
pages
required
integer >= 0
{
  • "page": 1,
  • "size": 1,
  • "total": 0,
  • "pages": 0
}

CursorPagination

limit
required
integer >= 1
next
required
string or null
more
required
boolean
{
  • "limit": 1,
  • "next": "string",
  • "more": true
}

InventoryChangedEvent

eventId
required
string <uuid>
type
required
string
Value: "inventory.changed"
createdAt
required
string <date-time>
required
object
storeId
required
string [ 1 .. 64 ] characters
sku
required
string or null [ 1 .. 64 ] characters

The merchant's SKU. Optional and not unique on a Qashier product

productId
required
string non-empty
variantId
required
string or null non-empty
productName
required
string non-empty
quantityChange
required
number
resultingBalance
required
number
reason
required
string
Enum: "SALE" "RESTOCK" "ADJUSTMENT" "STOCK_TAKE" "CANCELLATION_RESTORE" "TRANSFER"
transactionId
required
string or null non-empty
occurredAt
required
string <date-time>
{
  • "eventId": "d6703cc8-9e79-415d-ac03-a4dc7f6ab43c",
  • "type": "inventory.changed",
  • "createdAt": "2019-08-24T14:15:22Z",
  • "data": {
    }
}

TransactionCancelledEvent

eventId
required
string <uuid>
type
required
string
Value: "transaction.cancelled"
createdAt
required
string <date-time>
required
object
storeId
required
string [ 1 .. 64 ] characters
transactionId
required
string non-empty
externalOrderReference
required
string non-empty
receiptNumber
required
string non-empty
orderTotal
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

refundAmount
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

currency
required
string^[A-Z]{3}$

ISO 4217, uppercase

reason
required
string or null <= 500 characters
cancelledBy
required
string non-empty
cancelledAt
required
string <date-time>
outcome
required
string
Enum: "COMPLETED" "COMPLETED_WITH_FAILURES"

Same values as cancellation.status on the order once finalized. COMPLETED_WITH_FAILURES: at least one reversal failed and staff have to finish it by hand; read GET /orders/{transactionId} for cancellation.tasks.

{
  • "eventId": "d6703cc8-9e79-415d-ac03-a4dc7f6ab43c",
  • "type": "transaction.cancelled",
  • "createdAt": "2019-08-24T14:15:22Z",
  • "data": {
    }
}

AmountMismatchDetails

amountCharged
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

computedTotal
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

currency
required
string^[A-Z]{3}$

ISO 4217, uppercase

required
Array of objects
Array
index
required
integer >= 0
sku
required
string or null [ 1 .. 64 ] characters

The merchant's SKU. Optional and not unique on a Qashier product

computedAmount
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

{
  • "amountCharged": 0,
  • "computedTotal": 0,
  • "currency": "string",
  • "lines": [
    ]
}

StoreNotInKeyScopeError

code
required
string
Value: "STORE_NOT_IN_KEY_SCOPE"
message
required
string non-empty
details
object
{
  • "code": "STORE_NOT_IN_KEY_SCOPE",
  • "message": "string",
  • "details": { }
}

StoreNotFoundError

code
required
string
Value: "STORE_NOT_FOUND"
message
required
string non-empty
details
object
{
  • "code": "STORE_NOT_FOUND",
  • "message": "string",
  • "details": { }
}

OrderNotFoundError

code
required
string
Value: "ORDER_NOT_FOUND"
message
required
string non-empty
details
object
{
  • "code": "ORDER_NOT_FOUND",
  • "message": "string",
  • "details": { }
}

IdempotencyConflictError

code
required
string
Value: "IDEMPOTENCY_KEY_CONFLICT"
message
required
string non-empty
details
object
{
  • "code": "IDEMPOTENCY_KEY_CONFLICT",
  • "message": "string",
  • "details": { }
}

AmountMismatchError

code
required
string
Value: "AMOUNT_MISMATCH"
message
required
string non-empty
required
object (AmountMismatchDetails)
amountCharged
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

computedTotal
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

currency
required
string^[A-Z]{3}$

ISO 4217, uppercase

required
Array of objects
Array
index
required
integer >= 0
sku
required
string or null [ 1 .. 64 ] characters

The merchant's SKU. Optional and not unique on a Qashier product

computedAmount
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

{
  • "code": "AMOUNT_MISMATCH",
  • "message": "string",
  • "details": {
    }
}

UpstreamUnavailableError

code
required
string
Value: "UPSTREAM_UNAVAILABLE"
message
required
string non-empty
details
object
{
  • "code": "UPSTREAM_UNAVAILABLE",
  • "message": "string",
  • "details": { }
}

Store

storeId
required
string [ 1 .. 64 ] characters
name
required
string non-empty
timezone
required
string non-empty

IANA name, e.g. Asia/Kuala_Lumpur

currency
required
string^[A-Z]{3}$

ISO 4217, uppercase

{
  • "storeId": "string",
  • "name": "string",
  • "timezone": "string",
  • "currency": "string"
}

StoreListResponse

required
Array of objects (Store)
Array
storeId
required
string [ 1 .. 64 ] characters
name
required
string non-empty
timezone
required
string non-empty

IANA name, e.g. Asia/Kuala_Lumpur

currency
required
string^[A-Z]{3}$

ISO 4217, uppercase

required
object
limit
required
integer >= 1
next
required
string or null
more
required
boolean
{
  • "data": [
    ],
  • "pagination": {
    }
}

Product

sku
required
string or null [ 1 .. 64 ] characters

Null when the merchant has not set one

name
required
string non-empty
price
required
integer

Price at this store, after any store tier

trackInventory
required
boolean
productId
required
string non-empty
category
required
string or null
tab
required
string or null
required
Array of objects
Array
sku
required
string or null [ 1 .. 64 ] characters

Null when the merchant has not set one

name
required
string non-empty
price
required
integer

Price at this store, after any store tier

trackInventory
required
boolean
variantId
required
string non-empty
required
Array of objects
Array
sku
required
string or null [ 1 .. 64 ] characters

Null when the merchant has not set one

name
required
string non-empty
price
required
integer

Price at this store, after any store tier

trackInventory
required
boolean
duplicateSku
required
boolean
{
  • "sku": "string",
  • "name": "string",
  • "price": 0,
  • "trackInventory": true,
  • "productId": "string",
  • "category": "string",
  • "tab": "string",
  • "variants": [
    ],
  • "modifiers": [
    ],
  • "duplicateSku": true
}

ProductListResponse

required
Array of objects (Product)
Array
sku
required
string or null [ 1 .. 64 ] characters

Null when the merchant has not set one

name
required
string non-empty
price
required
integer

Price at this store, after any store tier

trackInventory
required
boolean
productId
required
string non-empty
category
required
string or null
tab
required
string or null
required
Array of objects
required
Array of objects
duplicateSku
required
boolean
required
object
limit
required
integer >= 1
next
required
string or null
more
required
boolean
{
  • "data": [
    ],
  • "pagination": {
    }
}

StockLevel

sku
required
string or null [ 1 .. 64 ] characters

The merchant's SKU, or null when they have not set one. Such a row cannot be filtered by sku and has to be matched on productId.

productId
required
string non-empty
variantId
required
string or null non-empty
available
required
number

Quantity a customer can still buy: on hand minus what open bills are holding. A hold counts until it is confirmed or released.

trackInventory
required
boolean
updatedAt
required
string <date-time>
{
  • "sku": "string",
  • "productId": "string",
  • "variantId": "string",
  • "available": 0,
  • "trackInventory": true,
  • "updatedAt": "2019-08-24T14:15:22Z"
}

StockListResponse

required
Array of objects (StockLevel)
Array
sku
required
string or null [ 1 .. 64 ] characters

The merchant's SKU, or null when they have not set one. Such a row cannot be filtered by sku and has to be matched on productId.

productId
required
string non-empty
variantId
required
string or null non-empty
available
required
number

Quantity a customer can still buy: on hand minus what open bills are holding. A hold counts until it is confirmed or released.

trackInventory
required
boolean
updatedAt
required
string <date-time>
required
object
limit
required
integer >= 1
next
required
string or null
more
required
boolean
{
  • "data": [
    ],
  • "pagination": {
    }
}

Order

transactionId
required
string non-empty
receiptNumber
required
string non-empty
externalOrderReference
required
string non-empty
storeId
required
string [ 1 .. 64 ] characters
status
required
string
Enum: "PAID" "CANCELLING" "CANCELLED"

CANCELLING means a cancellation has been started and at least one reversal is still outstanding — read cancellation.tasks for which. Stock may already be back while the refund is not, so do not treat CANCELLING as either state.

required
object
subtotal
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

itemDiscount
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

billDiscount
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

serviceCharge
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

tax
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

rounding
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

total
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

currency
required
string^[A-Z]{3}$

ISO 4217, uppercase

required
Array of objects
Array
index
required
integer >= 0
sku
required
string or null [ 1 .. 64 ] characters

The merchant's SKU. Optional and not unique on a Qashier product

productId
required
string or null non-empty

The Qashier product the line resolved to. Null when the SKU matched nothing, in which case the line was stored as a custom item with no stock movement.

computedAmount
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

required
object or null

Present once a cancellation has been started, and kept afterwards so the outcome stays readable. Null for an order nobody has cancelled.

status
required
string
Enum: "PENDING" "IN_PROGRESS" "COMPLETED" "COMPLETED_WITH_FAILURES"

Where the cancellation as a whole has got to, and what the order's top-level status reads as a result. PENDING and IN_PROGRESS: at least one task is outstanding and the order is CANCELLING — a cancellation never fails as a whole; a stuck task keeps it here until a person resolves it. COMPLETED: every task is COMPLETED or SKIPPED and the order is CANCELLED. COMPLETED_WITH_FAILURES: the order is CANCELLED but at least one task is FAILED and staff have to finish that reversal by hand.

initiatedAt
required
string <date-time>
completedAt
required
string or null <date-time>

Null until every task is terminal.

required
Array of objects
Array
task
required
string
Enum: "REFUND" "INVENTORY" "DISCOUNT" "LOYALTY" "TABLE" "INVOICE" "OTHER"

What is being reversed. OTHER covers internal reversals this contract does not name, so a task list is never silently incomplete — if something is outstanding you can always see that it is.

status
required
string
Enum: "PENDING" "IN_PROGRESS" "COMPLETED" "FAILED" "SKIPPED" "MANUAL_ACTION_REQUIRED"

MANUAL_ACTION_REQUIRED is not a failure and will not clear on its own: a person in the store has to act, and until they do the order stays CANCELLING. FAILED is terminal — the cancellation finalizes without this reversal, which is what COMPLETED_WITH_FAILURES reports.

createdAt
required
string <date-time>
{
  • "transactionId": "string",
  • "receiptNumber": "string",
  • "externalOrderReference": "string",
  • "storeId": "string",
  • "status": "PAID",
  • "totals": {
    },
  • "lines": [
    ],
  • "cancellation": {
    },
  • "createdAt": "2019-08-24T14:15:22Z"
}

OrderResponse

required
object (Order)
transactionId
required
string non-empty
receiptNumber
required
string non-empty
externalOrderReference
required
string non-empty
storeId
required
string [ 1 .. 64 ] characters
status
required
string
Enum: "PAID" "CANCELLING" "CANCELLED"

CANCELLING means a cancellation has been started and at least one reversal is still outstanding — read cancellation.tasks for which. Stock may already be back while the refund is not, so do not treat CANCELLING as either state.

required
object
subtotal
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

itemDiscount
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

billDiscount
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

serviceCharge
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

tax
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

rounding
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

total
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

currency
required
string^[A-Z]{3}$

ISO 4217, uppercase

required
Array of objects
Array
index
required
integer >= 0
sku
required
string or null [ 1 .. 64 ] characters

The merchant's SKU. Optional and not unique on a Qashier product

productId
required
string or null non-empty

The Qashier product the line resolved to. Null when the SKU matched nothing, in which case the line was stored as a custom item with no stock movement.

computedAmount
required
integer

Amount in minor units, e.g. 4550 is MYR 45.50

required
object or null

Present once a cancellation has been started, and kept afterwards so the outcome stays readable. Null for an order nobody has cancelled.

status
required
string
Enum: "PENDING" "IN_PROGRESS" "COMPLETED" "COMPLETED_WITH_FAILURES"

Where the cancellation as a whole has got to, and what the order's top-level status reads as a result. PENDING and IN_PROGRESS: at least one task is outstanding and the order is CANCELLING — a cancellation never fails as a whole; a stuck task keeps it here until a person resolves it. COMPLETED: every task is COMPLETED or SKIPPED and the order is CANCELLED. COMPLETED_WITH_FAILURES: the order is CANCELLED but at least one task is FAILED and staff have to finish that reversal by hand.

initiatedAt
required
string <date-time>
completedAt
required
string or null <date-time>

Null until every task is terminal.

required
Array of objects
createdAt
required
string <date-time>
{
  • "data": {
    }
}

InvalidRequestError

code
required
string
Value: "INVALID_REQUEST"
message
required
string non-empty
details
object
{
  • "code": "INVALID_REQUEST",
  • "message": "string",
  • "details": { }
}

UnauthenticatedError

code
required
string
Value: "UNAUTHENTICATED"
message
required
string non-empty
details
object
{
  • "code": "UNAUTHENTICATED",
  • "message": "string",
  • "details": { }
}

RateLimitedError

code
required
string
Value: "RATE_LIMITED"
message
required
string non-empty
details
object
{
  • "code": "RATE_LIMITED",
  • "message": "string",
  • "details": { }
}

InternalError

code
required
string
Value: "INTERNAL"
message
required
string non-empty
details
object
{
  • "code": "INTERNAL",
  • "message": "string",
  • "details": { }
}

ServiceUnavailableError

code
required
string
Value: "SERVICE_UNAVAILABLE"
message
required
string non-empty
details
object
{
  • "code": "SERVICE_UNAVAILABLE",
  • "message": "string",
  • "details": { }
}