Queek docs

Authentication & keys

A private key alone, bound to one store, carrying scopes — and every refusal it can meet.

Base URL:

https://api.usequeek.com/api/v1/merchant

Every request carries the store's private API key in X-Client-Key, and should ask for JSON:

curl https://api.usequeek.com/api/v1/merchant/store \
  -H "X-Client-Key: sk_live_..." \
  -H "Accept: application/json"

That header is the whole credential. There is no OAuth flow, no token exchange, no session beside it — a key presented next to a Bearer [REDACTED] or a session only narrows that credential, exactly as in the dashboard. And the key belongs to one store: no vendor header and no vendor_id is sent, because there is nothing to choose. A header naming another store is refused outright.

One kind of key

The merchant mints the key in the dashboard under Settings → API keys (the Connections list) and chooses its scopes at mint time. Only a private key reaches this API:

KeyReaches the Merchant API
sk_live_…yes — its one store, within its scopes
sk_test_…yes — its dev store only; keys never cross modes
pk_live_…, pk_test_…never — a public key is a browser credential

A private key needs no Origin, and it must never appear in a browser bundle, a public repository, or a client-side environment variable. A pk_ key sent here is a 403 private_key_required, with the fix named: mint an sk_ key.

Going live is swapping the key — a _test_ key on a live store, or a _live_ key on a dev store, is a 401 api_key_mode_mismatch. No path, header or body changes.

Scopes are the whole permission

A key's scopes decide every operation it may call — each operation requires exactly one scope, stated on the operation itself in the reference and collected in the scopes table. A key acts for its whole store, never only for the records one person created; a write scope also returns the record it wrote in that write's own response, while listing or reading records needs the matching read scope.

Two limits on what a key can hold, enforced at mint time and on every call:

  • The minter can only grant scopes they hold themselves — nobody can mint a key broader than their own permission.
  • Some families are never grantable: api_keys (a key that could mint a broader key would make every scope cosmetic), roles, users, employees and pos. Those routes stay dashboard-only.

Customer contact is a separate scope

Customer rows arrive with their contact fields — name, email, phone and address — set to null unless the key deliberately holds merchant-customers-contact-read. Ids, counts, dates and flags are unaffected.

The masking is not just cosmetic: a masked key cannot recover contact through filters either. Using search, or sorting by name, email or phone on the customer or follower list without the scope, is refused with 403 insufficient_scope naming the missing scope — rather than returning wrong-looking data.

When authentication fails

The reply carries error_code — it names exactly what to fix.

error_codeStatusWhat it means
invalid_client_key401The key is unknown. Check it under Dashboard → Settings → API keys.
api_key_mode_mismatch401A _test_ key on a live store, or a _live_ key on a dev store. Keys never cross modes.
api_key_revoked403The key was revoked. Mint a fresh one.
api_key_expired403The key expired. Mint a fresh one.
private_key_required403A public pk_ key was sent. Mint a private sk_ key.
insufficient_scope403The key does not carry the scope this operation needs. The message names it.
route_not_available403Not part of the Merchant API — the endpoints are exactly those in the reference. A key sent to a retired api/v1/biz/… path is refused here too, and the message names the new /api/v1/merchant/… path to call instead. Anything else stays dashboard-only.
VENDOR_ACCESS_DENIED403A vendor header named a different store than the key's. Drop the header: the key already names its store.
plan_inactive403The store's plan does not include API access.

VENDOR_ACCESS_DENIED answers in the older {"error", "message"} shape rather than the error.code envelope — see Errors for both shapes. An insufficient_scope failure is the merchant's to fix, not yours: they mint a key with the scope, or narrow the integration to the scopes the key holds.

Refused credentials are throttled per IP — past the limit the address answers 429 before the refusal is even worked out. A live key is never throttled there, not even from an address that is over the limit. See Idempotency & rate limits for the quotas a live key draws from.

On this page