Skip to main content

Authentication

Every operation except the marketplace catalogue needs a credential in the Authorization header. There are three kinds.

API keyPersonal access tokenOAuth access token
HeaderBearer zk_live_… / Bearer zk_test_…Bearer zpat_…Bearer <JWT>
Acts asyour organisationthe person who made itthe person who signed in to your app
Made inDeveloper Studio (live and test); the console's Team → New API key (live)POST /v1/api-tokensyour app, through auth.zaitrack.app
Lifetime1 to 365 days (default 365)1 to 365 daysshort-lived; refreshed with a refresh token
Use it forserver-to-server integrationsscripts you run as yourselfapps people sign in to

Whatever the kind, a credential never carries a scope wider than its creator's.

API keys​

An API key belongs to the organisation, not to a person. It looks like zk_live_ (or zk_test_), 32 base32 characters, _ and a 16-character checksum; the gateway rejects a malformed key without a lookup.

  • Scopes are chosen when the key is made, no wider than yours.
  • IP allowlist and expiry are optional; a request from another address gets 403 and an expired key 401.
  • Rotate with POST /v1/api-keys/{id}:rotate: the new secret is shown once and keeps the old key's scopes. The old key keeps working for 30 days, or until it expires if that is sooner, so you can switch without downtime.
  • Revoke with POST /v1/api-keys/{id}:revoke. A revoked key stops working within 30 seconds (the gateway's caches).

Keys are created in Developer Studio, live or test (a test key on one of your sandboxes), or as live keys in the console under Team → New API key. The API has no call to create one.

Personal access tokens​

A personal access token (zpat_…) acts as you, with at most your scopes, and every use is audited. Make one with POST /v1/api-tokens while signed in through OAuth: the service refuses API keys and other non-interactive credentials there.

curl -X POST "https://api.zaitrack.app/v1/api-tokens" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"label": "Fuel import script", "scopes": ["fuel:read", "fuel:write"], "ttlDays": 30}'

The token is in the response once. List yours with GET /v1/api-tokens; revoke one with POST /v1/api-tokens/{id}:revoke.

OAuth apps​

Register an app in Developer Studio (OAuth apps → Register an app) for software people sign in to.

  • Issuer: https://auth.zaitrack.app/ (Ory Hydra). Read the endpoints from https://auth.zaitrack.app/.well-known/openid-configuration.
  • Grant: authorization code, with refresh tokens. There is no client-credentials grant for apps: a server with no person behind it uses an API key.
  • Public apps (single-page, mobile, desktop) have no secret and use PKCE (send code_challenge_method=S256).
  • Confidential apps (a server-side web app) get a client secret, shown once, sent with HTTP Basic (client_secret_basic). Rotate shows a new one.
  • Redirect URIs are https, or http://localhost for development.
  • Scopes are no wider than what your organisation's roles grant and what you, registering the app, hold.
  • Who signs in: only members of your organisation. There is no consent screen yet.
  • Delete removes the app and revokes its refresh tokens at once; an access token already issued lasts until it expires (15 minutes).

Scopes​

A scope is <family>:<action>, for example vehicles:read. A <family>:* scope covers every action of the family: alerts:* covers alerts:read and alerts:write. A request without the operation's scope gets 403 with code auth.token.scope_missing and the missing scopes in detail.

The API reference lists each scope and how many operations need it; every operation's page names its own.

Step-up MFA​

Sending a device command and approving one (POST /v1/commands, POST /v1/commands/{id}:approve) need a recent WebAuthn step-up. Only an OAuth access token from a session that passed it qualifies; an API key never does. Without it the gateway answers 403 with code step_up_required and a WWW-Authenticate: Step-Up … header.