In short
nsk_live_ keys read live data and nsk_test_ keys read your test account. The wrong pairing is 401 INVALID_API_KEY.sales:read and sales:write. A write scope implies only its own module's read scope.cost:read stands alone: without it every cost and price field reads null, which means redacted, not zero.Every request carries your key as a bearer token. There is no other mechanism: no cookies, no session, no OAuth flow and no query parameter. A key in a URL ends up in logs, browser history and referrer headers.
Authorization: Bearer nsk_live_...Rotate a key from Settings, Developer and choose a grace period: none, 1 hour, 24 hours, 7 days. You get a new key, shown once, and the old key keeps working until the grace period ends. While it does, its responses carry a Sunset header with the time it stops, so you can deploy the new key without taking your integration down.
Sunset: they come from something still using the old key.A key that is already revoked, or already inside a grace period, cannot be rotated again.
| Prefix | Environment |
|---|---|
nsk_live_ | Your real business data |
nsk_test_ | Your test account. See Test mode. |
One base URL for both. The pairing is checked both ways: a test key against a live account is refused, and a live key against a test account is refused. Both come back as 401 INVALID_API_KEY with the same message as every other key failure, so an unauthenticated caller never learns why a key was rejected.
A key carries scopes, so you can issue a narrow key for a narrow job. Every operation in the reference shows the scope it needs. GET /organization needs none and reports the key's effective scopes.
| Scope | Grants |
|---|---|
catalog:read, catalog:write | Products, allergens, foods, tags, units of measure, QC checkpoints, QC units of measure, preservations, countries and nutrients |
inventory:read, inventory:write | Product lots and warehouses |
manufacturing:read, manufacturing:write | Productions, production lines, operations, equipment and employees |
sales:read, sales:write | Sales orders, invoices, customers and their shipping addresses, customer types, price lists, salespersons, shipping methods, tax rates and territories |
purchasing:read, purchasing:write | Purchase orders, bills and suppliers |
cost:read | Cost and price fields, on every resource that has them |
sales:write grants sales:read; it does not grant catalog:read.cost:read is standalone. Nothing grants it implicitly, and the "Read everything" preset in Settings leaves it out. Without it every cost and price field reads null, which means redacted, not zero. You can give a fulfilment integration full sales access without giving it your margins.The API needs a paid Enterprise plan, and test keys follow the plan of the account that owns them. If the plan lapses or moves below Enterprise, live and test keys both get 403 NOT_ENTITLED. On a plan without the API, the license administrator, and anyone with Developer access, can still list and revoke keys and delete the test account, so nothing is left behind. While no plan is active at all, NutraSoft opens only its billing page, so that cleanup waits until a plan is active again.
Repeated failed authentication from one IP address is throttled. Past the limit, every further invalid key from that address gets 429 RATE_LIMITED instead of 401 until the minute rolls over, so fix the key rather than retrying. A valid key from the same address keeps working.