Authentication
Keys and scopes
Every request carries an API key. A key belongs to exactly one business, carries a set of scopes, and can be rotated or revoked at any time from the Nexa portal.
Getting a key
Keys are created in the Nexa portal, at Settings → API keys. Two things to know before you ask your customer for one:
- Only a business owner can create a key. Not an administrator, not a manager with every permission — the owner. A key acts with the authority of the whole company, and that is not something the owner should be able to delegate by handing out a permission.
- The API is an Enterprise feature. If the business is on Standard or Essential, the page is locked and every request answers
402. That is a conversation with Nexa, not a bug in your code.
A key looks like nxa_live_xxxxxxxx…. The secret is shown once, at creation, and is never recoverable — it is stored only as a hash, so not even Nexa support can read it back. Lose it and you rotate.
Making a request
Send the key as a bearer token. There is no other accepted form: a key in a query string is rejected with a 400, because a URL ends up in our access logs, your access logs and every proxy between.
curl https://nexa.com.tn/api/v1/assets \
-H "Authorization: Bearer $NEXA_API_KEY"const nexa = (path, init = {}) =>
fetch(`https://nexa.com.tn/api/v1${path}`, {
...init,
headers: {
...init.headers,
Authorization: `Bearer ${process.env.NEXA_API_KEY}`,
},
});
const response = await nexa("/assets");import os
import requests
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['NEXA_API_KEY']}"
response = session.get("https://nexa.com.tn/api/v1/assets")Scopes
A scope is a resource:action pair. A key holds a set of them, chosen when it is created, and every endpoint names the one it needs. A request with a valid key and the wrong scope gets a 403 naming the scope it wanted, so the fix is never a guess.
Grant the narrowest set that works. If your integration only reads, it should hold only read scopes — then a leaked key cannot cancel anyone’s work.
Assets
Equipment, machines and installed items, and their status history.
assets:readassets:writeassets:deleteBuildings
Sites and buildings in the facility register.
buildings:readbuildings:writebuildings:deleteFloors
Floors within a building.
floors:readfloors:writefloors:deleteRooms
Rooms and zones within a floor.
rooms:readrooms:writerooms:deleteOperations
Work orders: their details, assignment, scheduling and cancellation.
operations:readoperations:writeoperations:canceloperations:rescheduleTechnicians
The technician directory and who is available to assign.
technicians:readSpare parts
Spare-part inventory, stock levels and movements.
spare-parts:readWebhooks
Registered webhook endpoints and their delivery history.
webhooks:readActions read as you would expect: read is read, write is create and update, delete removes. Operations additionally have cancel and reschedule, split out because calling off a visit a technician is already driving to is a different kind of authority from editing a description.
Rotating a key
Rotating issues a new secret for the same key — the id, name and scopes are unchanged, so nothing configured around it has to move.
The old secret keeps working for 24 hours. That window exists because a real integration runs on more than one server and cannot be redeployed atomically; without it, every rotation would be an outage, which in practice means nobody would ever rotate.
While the old secret is still being presented, our responses carry a Nexa-Key-Rotation-Deadline header with the moment it stops working. Watch for it in your own logs — it is how you find the one server that missed the rollout, before it breaks.
A rotation that does not break anything
- Rotate the key in the portal and copy the new secret.
- Deploy it everywhere. Both secrets are accepted, so the order does not matter.
- Watch for
Nexa-Key-Rotation-Deadlinein your responses. When it stops appearing, everything is on the new secret.
Revoking a key
Revocation is immediate — there is no cache, so the next request with that key fails. The key stays listed in the portal, marked revoked, because the record of what a credential was allowed to do and when that ended is worth keeping.
Keys are also revoked automatically when the person who created them is deactivated, since a key acts as its creator.
Keeping keys safe
- Never in client-side code. The API refuses browser requests, but the real problem is that the key is already public by then. Proxy through your backend.
- Never in a repository. Environment variables or a secret manager. A key committed to git is a key to rotate, even in a private repository.
- Never in a URL. We reject
?api_key=outright rather than letting it work quietly. - Rotate when someone leaves who had access to where the key is stored.
- Set an expiry when you know the integration is temporary. A key for a one-off migration should not outlive it.
401 vs 402 vs 403
Three failures that look similar and mean very different things. Reading them correctly is the difference between fixing your code and calling the customer.
| Status | Means | What to do |
|---|---|---|
| 401 | The key is missing, malformed, unknown, disabled, revoked or expired. | Check the header and the key. The response is deliberately identical for every one of those cases — telling an unauthenticated caller which would confirm that a key exists. |
| 402 | The key is good, but the business’s plan does not include the API. | Nothing in your code will fix this. Surface it to whoever owns the Nexa relationship. |
| 403 | The key is good and lacks the scope for this endpoint. | The response names the scope. Have the owner add it in the portal, or stop calling that endpoint. |
See Errors for the full response shape and the rest of the status codes.