Skip to content

Authentication ​

Every request carries one credential: an API key, as a bearer token.

bash
curl "https://api.app.tightly.io/api/v1/inventory/table" \
  -H "Authorization: Bearer tly_live_..."

There is no second header. The key carries the organisation, so X-Organization-ID is not sent on a keyed request and is ignored if you send it. That is deliberate: an integrator holding one key cannot address a tenant they were not given, whatever they put in a header.

Use your organisation's regional API endpoint.

Minting a key ​

Settings, then Developer, then API keys. Admins mint, replace and revoke. Viewers and members see the table and the reason they cannot.

A key is shown in full exactly once, at mint. After that a face prints the prefix and the last four:

tly_live_a1b2c3d…wxyz

Only a SHA-256 digest is stored, compared in constant time. Tightly cannot show you the key again and cannot recover it: if it is lost, replace it.

On the New key drawerWhat it decides
LabelWhat you will recognise in a month. Required.
ReachPer resource: None, Read, or Read and write. See Scopes.
ExpiresIn 30 days, in 90 days, in a year, or Never. A year by default.
Allowed addressesAn optional CIDR list. Empty means any address.

What a key is not ​

  • Not a person. A key has no user behind it, so nothing it does is attributed to one. It records created_by and nothing else about people.
  • Optionally one account. A key minted with partner_id reaches that wholesale account only: its reads narrow to that account, and a write naming another is refused 403 with both accounts named. Omitted, the key reaches the whole organisation.
  • Not cross-organisation. One key reaches one organisation. An agency working across ten clients holds ten keys, each minted by that client's own admin, each revocable without touching the other nine.
  • Not above its plan, and not free of one. Entitlement is re-asked on every request rather than frozen at mint, so a key minted while the organisation was on Pro stops reaching Pro resources the day its plan moves down. The public API is itself something a plan includes, sold with Essentials, and it is asked before the resource behind the path is looked at: an organisation moved down below it keeps its minted keys and every one of them stops on every operation, ungated resources included. That second question is asked of keys minted since the public API became something a plan includes; a key older than that keeps reading whatever the plan holds, and still meets what its own resource is sold with. Both refusals are plan_excludes and the sentence says which.
  • Not exempt from the access ladder. A key is treated as a read-and-write member. Operations gated at admin level are not part of the public API and never will be under a key.

Expiry, replacement, revocation ​

Expiry is the cheap half of a leak. A key with expires_at set stops working on that date with no action from anybody.

Replacement is how a key is rotated without a maintenance window. Open the row menu, choose Replace, tick the reach the new key should have, and choose when the old one stops: now, in an hour, in 24 hours, or in seven days. Both keys work until the cutover, so a fleet can be updated at its own pace. The old row then reads Stops Sep 4, 2026.

Revocation is immediate and the row stays. Anything using the key stops at once, and the record of what it did stays behind.

Every unusable key gets the same refusal ​

Malformed, unknown, revoked, expired, or stopped after a replacement: all five answer 401 with key_invalid and the sentence "The API key is not valid." A request with no Authorization header at all never reaches a key: it is refused 400 first, with the sentence "The provided request does not have required Authorization header. Please verify the request."

That is on purpose. A refusal that said "revoked" would confirm the key had existed, and a key that existed belonged to a tenant somebody could then go and name. The holder of a genuine key learns which of the five it is from the Developer page, where they are already signed in as somebody entitled to know.

What you know about a request afterwards ​

Header, on every response the key reachedWhat it is
X-Request-IdThe handle on this call. Paste it into the key's Usage drawer to find it.
X-Tightly-RegionThe home that served it. us for every organisation today.
X-RateLimit-LimitThe allowance this request spent from, per minute.
X-RateLimit-RemainingWhat is left in the current minute.
X-RateLimit-ResetWhen the window resets, in unix seconds.

Every keyed call is recorded against the key for 90 days: the operation, the method, the status, the latency, the source address, the request id, and whether it arrived over REST or MCP. The key's Usage drawer is where you read it.

Sending a request to the wrong organisation ​

Some paths carry an organisation id: /organizations/{organization_id}/purchase-orders. On a keyed request that id must be the key's own organisation. If it is not, the call is refused 403organization_mismatch, never quietly answered with the key's own rows.

json
{
  "code": "organization_mismatch",
  "message": {
    "code": "organization_mismatch",
    "desc": "The organization in the path is not this key's organization.",
    "doc_url": "https://docs.tightly.io/api/guides/errors#organization_mismatch"
  }
}

An integrator who typed the wrong id should find out from the refusal, not from a reconciliation three weeks later.

Test keys ​

Two prefixes. tly_live_ reaches your real organisation. tly_test_ reaches a sandbox: a twin organisation under your own account, with the same plan and the same words, its own tenant, and no reach into any outside system.

Make one with POST /api/v1/developer/sandbox from an admin seat. It appears in your organisation switcher. It arrives empty and correctly shaped, so a test key can read every route the moment it returns. To add starter products, locations and stock, call POST /api/v1/sandbox/seed with a tly_test_ key holding sandbox:write.

The organisation decides the prefix, never the mint request: a sandbox mints tly_test_ and a real tenant mints tly_live_. A tly_test_ key resolves against a sandbox and against nothing else, so a test credential pointed at a real organisation is refused rather than answered. That is the whole guarantee, and it is why the prefix is worth reading in a config file.

The Try it console on this site runs against whichever organisation your key belongs to, and says so before it sends anything. Point it at a sandbox key and it is safe by construction.

Migrating from a tly_pim_ key ​

Existing PIM keys keep working, unchanged, and resolve to the pim:read scope alone. They gain nothing on the day the new door opens: a tly_pim_ key reaches the two Product data operations it always reached and none of the catalogue reads. To widen it, mint a tly_live_ key with the reach you want.

Tightly API, version 2026-11.