Authentication
How provider keys are formatted and sent, how the partner header works, which routes each kind of key may call, effective scopes, and replacing a key without downtime.
Every call you make is authenticated with a provider key that Localoy issues to your company. A
call made for a partner also names that partner in the X-Localoy-Partner-Id header.
Key format#
ltp_{environment}_{prefix}_{secret}| Part | Value |
|---|---|
ltp | The fixed tag of Technology Provider keys, so a leaked key is easy to find with a secret scanner. Partner keys start lok. |
environment | sbx for a sandbox key, live for a production key. A key keeps its environment for life. |
prefix | 8 characters from a–z and 2–9, without the look-alikes l, o, 0 and 1. |
secret | 43 characters of URL-safe base64. It may itself contain _. |
A sandbox key is 60 characters long and a production key 61. The first three parts together —
ltp_sbx_k7m2q9xa — are the key's public prefix: GET /ping returns it, so you
can tell keys apart without exposing the secret.
Localoy stores only a SHA-256 hash of each key. A key is shown once, when Localoy issues it, and cannot be recovered afterwards. Localoy can also give a key an expiry date; after it, the key is refused.
Sending the key#
Send the key in either header:
Authorization: Bearer ltp_live_r4t8wz3c_…X-Api-Key: ltp_live_r4t8wz3c_…If a request carries both, the Authorization header is used.
Which routes a key can call#
| Route | Partner key (lok_…) | Provider key (ltp_…) |
|---|---|---|
GET /health | No key needed. | No key needed. |
GET /ping | Yes. | Yes. The partner header is optional. |
| Catalogue, bookables and payments | Yes, for its own partner. | Yes, for the partner named in X-Localoy-Partner-Id. |
/connections… and /webhooks… | 403 provider_key_required | Yes. The partner header is ignored. |
The catalogue, bookables and payment endpoints are the partner-scoped endpoints. The connection and webhook endpoints act for your company, not for a partner.
The partner header#
X-Localoy-Partner-Id: cm1partnerid000000000000001Send it on every partner-scoped call. Its value is the partnerId of a connection
you claimed. Localoy then checks that connection:
| Situation | Status | Code |
|---|---|---|
| The header is missing or empty. | 400 | partner_id_required |
| The partner is not connected to you — never connected, its code not yet claimed, or disconnected. | 404 | connection_not_found |
| The partner has paused you. | 403 | connection_paused |
| The connection's effective scopes lack the scope the endpoint needs. | 403 | open_network_scope_required |
{
"success": false,
"message": "That partner is not connected to you. A partner connects you by giving you a connection code to claim.",
"code": "connection_not_found",
"requestId": "3f0c9f8e-6a1d-4f5e-9b1a-2f1c0d9e7a41"
}connection_not_found answers every partner you are not connected to in the same way, so it never
confirms that an ID belongs to a real partner.
Effective scopes#
Each partner-scoped endpoint needs one scope, as in the
Open Network API: REGISTRATION to create items, UPDATE to change or
delete them, INVENTORY to read items and bookables, and PAYMENT for payment sessions. For you, the
check uses the connection's effective scopes — what the partner granted that Localoy still allows
you. See Effective scopes.
{
"success": false,
"message": "This partner has not granted you the UPDATE scope, which this endpoint requires.",
"code": "open_network_scope_required",
"details": {
"requiredScope": "UPDATE",
"grantedScopes": ["REGISTRATION", "INVENTORY"]
},
"requestId": "3f0c9f8e-6a1d-4f5e-9b1a-2f1c0d9e7a41"
}details.grantedScopes holds the effective scopes. Do not retry: tell the partner which permission
is missing. It can add a scope in its Partner Portal without a new code.
Verifying a key#
GET /ping accepts a provider key and needs no partner header:
{
"success": true,
"data": {
"ok": true,
"principal": "TECHNOLOGY_PROVIDER",
"environment": "PRODUCTION",
"keyPrefix": "ltp_live_r4t8wz3c",
"provider": { "key": "example-pos", "name": "Example POS" }
}
}Send X-Localoy-Partner-Id as well and /ping checks that connection the way a partner-scoped call
does — with the 400, 404 and 403 answers in the table above — though it needs no scope. For an
active connection the response adds the partnerId, the connection's id and status, and your
effective scopes for it: a quick check before a sync.
{
"success": true,
"data": {
"ok": true,
"principal": "TECHNOLOGY_PROVIDER",
"environment": "PRODUCTION",
"keyPrefix": "ltp_live_r4t8wz3c",
"provider": { "key": "example-pos", "name": "Example POS" },
"partnerId": "cm1partnerid000000000000001",
"connection": { "id": "cm7c0nn3ct10n00000000001", "status": "ACTIVE" },
"scopes": ["REGISTRATION", "UPDATE", "INVENTORY"]
}
}When a key is refused#
A missing, malformed, unknown, revoked or expired key, or one with the wrong secret, gets one answer, whatever the reason:
{
"success": false,
"message": "Invalid or missing API key",
"code": "open_network_key_invalid",
"requestId": "3f0c9f8e-6a1d-4f5e-9b1a-2f1c0d9e7a41"
}The response never says why, so it cannot help someone guessing keys. Compare the key's public prefix
with the one Localoy gave you; if it still fails, send the requestId to your partnerships contact.
Errors#
| Code | Status | Meaning |
|---|---|---|
open_network_key_invalid | 401 | The key is missing, malformed, unknown, revoked or expired, or its secret is wrong. |
provider_suspended | 403 | Localoy has suspended your company. Every key is refused until it resumes you. |
provider_key_required | 403 | A partner key called a provider-only route: /connections… or /webhooks…. |
partner_id_required | 400 | A partner-scoped call has no X-Localoy-Partner-Id, or it is empty. |
connection_not_found | 404 | The partner is not connected to you. |
connection_paused | 403 | The partner has paused you. |
open_network_scope_required | 403 | The connection's effective scopes lack the endpoint's scope. |
Every provider error is listed in Errors.
Rotating a key without downtime#
Localoy issues and revokes your keys, so replacing one is a request to your partnerships contact:
- Ask for a second key in the same environment. You can hold up to 5 live keys in each.
- Deploy it everywhere the old key is used.
- Confirm that traffic has moved:
GET /pingreturns the new key'skeyPrefix. - Ask Localoy to revoke the old key. It stops working on its next request.
If a key has leaked, do not wait for a replacement: ask Localoy to revoke it at once.
Calling from a server only#
A provider key acts for every partner who connected you
Keep provider keys in your servers' secret store or environment variables, and send every request from your backend. Never put a key in a browser, a mobile app, a plugin or app you ship to partners, or a public repository. The API sends no CORS headers to arbitrary origins, so a browser page cannot call it.