| Scope | Access | Description |
|---|---|---|
| orders.read | read | Read orders and their status. |
| orders.write | write | Create and update orders. |
| menu.read | read | Read menu, items, and modifiers. |
| menu.write | write | Manage menu, items, availability (86). |
| inventory.read | read | Read stock levels and counts. |
| inventory.write | write | Adjust stock, submit counts. |
| customers.read | read | Read customer profiles (consent-gated). |
| payments.read | read | Read payment + settlement records. |
| webhooks.manage | write | Create and manage webhook subscriptions. |
| Tier | Req/min | Burst | Daily quota | Write |
|---|---|---|---|---|
| Sandbox | 30 | 60 | 5000 | no |
| Standard partner | 120 | 240 | 200000 | yes |
| Certified partner | 600 | 1200 | 5000000 | yes |
| Internal / first-party | 6000 | 12000 | uncapped | yes |
Send an Idempotency-Key header on any mutation you might retry. A replay of the same request returns the original result instead of performing the work twice — which is what makes an offline queue or a flaky network safe.
A key is bound to the request that first used it. Reusing the same key with a CHANGED body is a conflict, not a silent success: the server refuses rather than letting one key settle two different payments.
curl -X POST https://novaryq.com/v1/orders/ORDER_ID/payments \
-H "Authorization: Bearer $NQ_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"method":"CASH","amount":"24.99"}'Every webhook carries an HMAC-SHA256 of the RAW request body in the X-NQ-Signature header, hex encoded. Compute the same HMAC with your endpoint secret over the bytes you received — not over a re-serialised object, which will not match — and compare in constant time.
Verification is fail-closed in production: an unsigned or wrongly signed delivery is rejected, never processed on the assumption it is probably fine.
import { createHmac, timingSafeEqual } from 'node:crypto';
// rawBody MUST be the exact bytes received, before any JSON parsing.
export function verify(rawBody, signatureHeader, secret) {
const expected = createHmac('sha256', secret).update(rawBody).digest('hex');
const a = Buffer.from(signatureHeader.trim(), 'utf8');
const b = Buffer.from(expected, 'utf8');
return a.length === b.length && timingSafeEqual(a, b);
}Errors are standard HTTP status codes with a JSON body carrying a human-readable message. Treat 4xx as final — a rejected request will be rejected identically on retry, so retrying it only hides the reason. Retry 5xx and network failures, with backoff.
One case deserves its own handling: a payment whose outcome is unknown returns 503 and is reconciled server-side. Do not re-tender it — that is how a guest gets charged twice.
Sandbox credentials are read-only by design: orders, menu and inventory reads, no writes. That is deliberate — an integration that cannot yet be trusted with a write should not be able to make one while it is being built.
Novaryq publishes a capability registry at /v1/capabilities with an explicit proof level for every integration — catalog, simulator, partner-approved, certified or observed in production. Build against what it says, not against what a feature list implies: a capability that is not certified is not a promise.