Get started
Authentication
API tokens to start with, API keys for production servers, the scopes both hold, and the pattern for an app that acts as a person.
Every call carries Authorization: Bearer <token>. There are two ways to get that
token:
| API token | API key | |
|---|---|---|
| What you keep | the token itself | a client id and a secret |
| Lives | 7, 30, 90 or 365 days | each exchanged token lives 15 minutes |
| Made | in one click here, or in the console | in the console |
| For | trying the API, scripts, cron jobs, CI | servers that keep a secret |
Both hold only the scopes they were given, both are revoked in the console, and both count against the same account.
API tokens: start here
Signed in, Create a test token above makes one for your account in this browser: every read scope, valid seven days. It is selected in the reference's playground at once, so every page's Send and every code sample carries it. Copy it from the bar for your terminal:
export INORBIT_TOKEN="eyJ…"
curl -H "Authorization: Bearer $INORBIT_TOKEN" https://api.inorbit.hr/v1/meFor a token with a name, chosen scopes or a longer life, use API tokens and keys in
the console (https://console.inorbit.hr/keys/). There you can also write what the
token is for in plain words ("read the radar from a weekly cron job") and the platform's
own model proposes a name, the scopes with a reason for each, and a lifetime. You
check the proposal and create the token yourself; the model never creates one.
- A token is shown once, when it is made. The platform keeps its name, scopes, expiry and last use, never the token; lose it and make another.
- The test tokens this site makes live in this browser's storage only, and Forget removes one from here without revoking it.
- An account holds up to twenty active tokens.
- Revoking a token in the console refuses it within seconds, on every route.
API keys: for production servers
An API key is a client id and a client secret. You do not send the key itself on a call; you exchange it for an access token and send the token. The exchange is the OAuth 2.0 client credentials grant at the identity provider:
curl -u "$KEY_ID:$KEY_SECRET" https://auth.inorbit.hr/oauth2/token \
-d grant_type=client_credentials \
-d audience=iohr-api \
-d scope="identity:read account:read"{
"access_token": "eyJ…",
"token_type": "bearer",
"expires_in": 899,
"scope": "identity:read account:read"
}Then, on every call:
Authorization: Bearer eyJ…The token is a signed JWT with audience iohr-api and the scopes it was given. The
gateway verifies the signature and the audience on every request, admits the call only
when the token holds the route's scope, and hands it on with the identity inside; the services behind it never see the secret and never verify a
token themselves.
Where keys come from
Keys are made and revoked in the console at https://console.inorbit.hr/keys/, for your personal account or
for a team you own or administer. The secret is shown once, with this exchange filled in.
Scopes
A token made in the console or here holds the scopes ticked when it was made. A key
holds the scopes ticked when it was made, and a token from a key holds the ones asked
for in the exchange: scope is a space-separated list, any subset of the key's. Asking
for a scope the key does not hold is refused with invalid_scope; a call to a route
whose scope the token lacks is 403 forbidden. Scopes are fixed for life: to change
them, make a new token or key and revoke the old one.
| Scope | Lets a token call |
|---|---|
identity:read | GET /v1/me: the caller as the gateway verified it |
account:read | GET /v1/accounts/me: the account, its plan, the key's name and last use |
usage:read | GET /v1/accounts/orgs/{org_id}/units and /usage, GET /v1/accounts/units/categories: the account's units and the price list |
radar:read | GET /v1/radar/digests, /v1/radar/digests/{id}, /v1/radar/items: the radar's published digests |
events:read | GET /v1/events/types, the event catalogue, and the account's events over MQTT (events/#) |
webhooks:read | GET /v1/webhooks/endpoints and the routes under it: the account's webhook endpoints and their deliveries; GET /v1/webhooks/inboxes and what the test inbox received |
webhooks:write | making, changing, testing, rotating and deleting endpoints, resending a delivery, and making or deleting the test inbox |
domains:read | GET /v1/accounts/orgs/{org_id}/domains and /domains/{domain}: the account's domains, their records and status |
domains:write | adding, checking, confirming and removing the account's domains |
trail:read | GET /v1/trails/recordings and /recordings/{recording_id}: the account's trail recordings (RFC 0055) |
trail:write | uploading trail recordings and deleting them, in the account the token belongs to |
connections:read | GET /v1/connections/kinds, the account's connections, their grants and history of uses, and GET /v1/connections/tools: the actions granted to this token |
connections:write | adding, changing, pausing, testing and deleting the account's connections, and revoking a grant; granting is done by a person in the console |
connections:use | calling a connection action a grant gives this token: POST .../connections/{connection_id}/actions/{action} or POST /v1/connections/tools/{name} |
mcp:read | over MCP, listing and calling the tools that only read |
mcp:write | over MCP, the tools that change something (a connection action also needs connections:use) |
mcp:generate | over MCP, the tools that run a model: a generation or an embedding, counted against the account's budget |
Any token may fetch GET /v1/openapi.json: the document for its plan, narrowed to the
scopes it holds, so it lists exactly the operations that token may call. info.x-iohr-cut
names the plan, the account, the scopes and a hash of the operations and schemas; two
documents with the same hash are the same API. A signed-in person in several accounts adds
?account=<id> for a team's document (its plan); an account they are not in answers
403. iohr sdk generate (Generate an SDK) turns that document into a client with exactly the
operations the credential may call.
Every operation in the Reference names its scope. Each product
opens to keys with scopes of its own as it is published.
Lifetime, rotation, revocation
- An API token lives the days chosen when it was made. A token from a key lives
fifteen minutes; fetch a new one before it expires. A call with an expired token is
401 unauthenticated. - To rotate, make a second key, move your fleet to it, then revoke the first. An account holds up to ten keys at once.
- Revoking a key refuses new tokens at once, and the gateway refuses the tokens already issued from it within seconds. Revoking an API token works the same way.
- Keep the secret out of code and logs. The platform's own logs carry the key id, the
route and the status of a call, never a body and never a secret. Every answer carries
x-request-id; log it on your side to find a call with us (Paging, retries and errors).
The exchange in three languages
const basic = Buffer.from(`${process.env.KEY_ID}:${process.env.KEY_SECRET}`).toString("base64");
const res = await fetch("https://auth.inorbit.hr/oauth2/token", {
method: "POST",
headers: { authorization: `Basic ${basic}`, "content-type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
audience: "iohr-api",
scope: "identity:read account:read",
}),
});
const { access_token } = await res.json();An app that acts as a person
A key represents an account. An app that acts for a signed-in person (a mobile or
desktop client) uses the authorization code grant with PKCE against the same identity
provider, in the system browser, with audience=iohr-api on the authorization request,
and then sends the resulting bearer token exactly as above. Registering such a client
is a request to the operator today.
What the token carries
GET /v1/me shows it: subject (the key id for a key or API token, the person's id
for a person), kind (client for a key or API token, person for a person), scopes, and the account the call
counts against under org: the account a key belongs to, or a person's own.
GET /v1/accounts/me answers the account itself, with its plan and, for a key, the
name of the key or token and when it was last used.