Querying — business rules

The constraints, mutual-exclusivity rules, and reconciliation gotchas that govern transaction queries.

These rules govern every call you make to the Merchant Tx API endpoints. The biggest one — polling and webhooks are mutually exclusive — is enforced by the platform; the rest are guidelines that keep your reconciliation clean.

For the API itself, see Querying transactions.


Request rules

RuleDetail
Authentication requiredBasic Auth or JWT Bearer with EXTERNAL role. See Authentication.
HTTPS onlyPlain HTTP is rejected.
Username conventionmerchant-{id} for merchants, psp-{id} for PSPs. The same credentials used for create-code and refunds.
Content-Typeapplication/json on POST requests.
Field lengthsDetermined by the underlying parameter types — code is 10 digits, merchantReference up to 45 characters, transactionId is a signed 64-bit integer.

The polling-vs-webhook mutex

This is the most important rule on this page.

⚠️

If your merchant has an HTTP notification URL configured in the Portal, queryRef returns HTTP 406 Not Acceptable.

The platform forces you to pick one outcome-delivery mechanism per merchant. Either:

  • HTTP webhooks, with the receiver acknowledging within 45 seconds and the platform reversing on missed acks (see Webhooks) — or
  • Polling via queryRef, with no webhook configured.

You can't combine them. The reason is that the platform's reversal-on-failure safety net depends on knowing whether you've received the outcome — if you can both poll and receive a webhook, the platform doesn't know which one's authoritative.

Exceptions

EndpointAffected by the mutex?
POST /code/queryRef✓ Yes — returns 406 if webhook is set
POST /code/queryRef/refund✓ Yes — same behaviour
GET /code/transaction/state/{transactionId}✗ No — works regardless of webhook configuration. Use this as your recovery endpoint when a webhook was missed.
POST /code/transaction/stateByQr✗ No
POST /code/deviceInfo/{txId}✗ No

The recovery pattern: if you have webhooks configured and miss a delivery, use getTransactionState (the GET endpoint) to pull the missing notification. It returns the same shape as the webhook payload would have delivered.


Reference-uniqueness considerations

Your reconciliation depends on merchantReference being meaningful. Two failure modes the legacy docs called out specifically:

RiskMitigation
Same code used for multiple transactions with the same merchantReferenceThe platform tracks the latest state only — earlier transactions are not surfaced via queryRef. Always change the merchantReference between purchases on a re-usable code (useOnce: false). The update-amount endpoint accepts a fresh reference for each call — use it.
Same code paid multiple times because partial refunds are allowedWhen reconciling, check amount, transactionId, date, and reference together — not just the code value. Two transactions can share a code if it's re-usable.

Rule of thumb

For useOnce: true codes, the relationship is 1:1 — one code, one transaction. Safe to query by code + original merchantReference.

For useOnce: false codes (static QR, counter sticker), each payment should use a unique merchantReference via the update-amount endpoint. Query by that per-transaction reference, not by the original code-create reference.


Reconciliation checks before releasing goods

If you're using polling (no webhook), apply these checks before treating a transaction as paid:

  1. status === 'SUCCESS' — never trust any other value
  2. Amount matches what you expected for that order
  3. transactionId is one you haven't already processed (de-duplication)
  4. date is within an expected window (catches replay attacks)
  5. reference matches the order record you're settling against

The same checks apply to the webhook path — see Webhooks > Best practices.


Partial-refund considerations when querying

When a transaction has been partially refunded:

  • queryRef on the original code + reference returns the original transaction's state — typically SUCCESS (the original purchase is still successful).
  • queryRefund on the same code + reference returns the refund's state — REFUNDED for the most recent refund.
  • The webhook for the refund itself uses referenceTransactionId to link back to the original — queryRef doesn't give you a refund-by-refund history.

For full refund auditing, store every refund webhook you receive. For ad-hoc checks, use queryRefund to confirm the most recent refund completed.


Status of "N/A"

When the transaction doesn't yet exist in the platform (the customer hasn't paid), queryRef returns status: "N/A". This is not an error — it means "no transaction found for that code + reference yet."

Treat N/A as "keep polling" rather than as "abandoned." The customer might be mid-scan when you queried.

A genuinely failed lookup (wrong credentials, malformed request) returns a 4xx HTTP error, not N/A. See Errors.


Authentication failure

If you call any query endpoint with incorrect credentials, you receive 513 SECURITY_VIOLATION rather than 401 Unauthorized. The platform treats credential failures on the Merchant Tx API more strictly than on other endpoints — it's a higher-trust set of operations because the response can leak transaction details.


What's next