The two credentials
A portal session token
This is what the portal API takes: an Auth0 bearer token in theAuthorization header, belonging to a person signed in to the KINTRA CMS, with the tenant named by the {tenantId} segment of the path. Reach for it when you need something the gateway surface does not carry, which for most integrations means issuing the key below, or when you are building against a portal session a person already has.
A tenant API key
This is what the gateway API takes: a key id in a header, and a signature computed with that key’s secret. Your backend holds both, nobody is signed in anywhere, and the key resolves the tenant, so there is no tenant in the path. This is the credential your servers integrate with, and the rest of this page is about it.Getting a key
The path is self-serve from end to end.1
KINTRA invites your team
The invitation goes out through KINTRA’s admin surface.
2
Your team gets access to the KINTRA CMS
Seats in the CMS are what your own people sign in with.
3
You create an API key for your tenant
Do it in the CMS. The same operation is on the portal API as Create an API key.
4
Your backend calls the gateway API with that key
Every request it sends is signed, which is what the rest of this page covers.
key_id as X-Tenant-Key and sign with secret. They are two different values from the same response, and swapping them is a common first failure.
Nothing forces you down to a single key while you rotate. Create the replacement, cut your backend over to it, then revoke the old one, and your integration keeps working throughout. A revoke takes effect immediately and cannot be undone. Listing your keys shows which are active, when each was last used, and when each expires.
What a signed request carries
Every gateway request carries three headers.
All three names are read case-insensitively, so whatever casing your HTTP client picks is fine.
Writes carry a fourth header,
X-Idempotency-Key, which is required rather than optional on the routes that take it. The request conventions page lists those routes and says what a missing, replayed or reused key returns.
The signing string
X-Tenant-Signature is the lowercase hex digest of an HMAC-SHA256, keyed with the key’s secret, over four parts concatenated in this order with nothing between them:
timestampis the same value you put inX-Tenant-Timestamp, in Unix seconds.METHODis the HTTP verb in uppercase. The server uppercases before it hashes, so a lowercase verb in your signing string produces a different digest from the one it computes.urlis the path and query exactly as sent, starting at the/api/v1prefix:/api/v1/gateway/tiers?tier_name=gold, not/gateway/tiers. Every query parameter is part of it, in the order you send them.bodyis the request body for POST, PUT and PATCH, and the empty string for every other method.
Sign the bytes you send
Serialize the body once, sign that exact string, and send that exact string. Both helpers below do that, and so does every sample in the reference.Timestamps
X-Tenant-Timestamp is Unix seconds, not milliseconds. It is checked before the signature, so a request whose timestamp is not current is rejected whatever it was signed with, and rejected without the secret being touched. Stamp each request as you send it and keep the sending machine’s clock synchronised.
Sign a request
Both helpers make the same call,GET /gateway/customers, which is the cheapest way to find out whether a new key works. Export the base URL for this environment first:
YOUR_TENANT_KEY_ID and YOUR_SIGNING_SECRET with the two values from the create response and run either one. A 200 means the credential and the signing string are both right.
node:crypto and fetch are both in Node 18 and later.
To turn either into a write, three things change and nothing else: METHOD becomes the verb you are calling, BODY becomes the compact JSON you are sending, and the request grows a Content-Type: application/json header and an X-Idempotency-Key. The signing line stays exactly as it is, because BODY is already one of its parts. The quickstart runs that version.
The same recipe on every operation page
Every gateway operation in the reference carries this pair of helpers filled in for that operation: its method, its path, its example body, its headers, with the signing lines unchanged from the ones above. They come from one template each, so a helper copied from an operation page and a helper copied from here build the signature the same way. The interactive playground on those pages is the exception. It cannot compute an HMAC, so pressing Send with only your key returns 401 unless you paste in a timestamp and a signature you built yourself.When a signature is rejected
Every failure of this check comes back the same way, 401 withAUTH_INVALID_CREDENTIALS:
message names which check failed, so read it first. Then work through the causes in the order they are checked:
- The timestamp is not current. Send the moment you are sending, in seconds, and check the clock on the machine sending it.
- The body signed is not the body sent. See Sign the bytes you send above.
- The url signed is not the url sent. The query string is part of it, and so is the
/api/v1prefix. - The verb in the signing string is not uppercase.
- The key id and the secret come from different keys, or the key has been revoked or has expired.

