Getting started
From zero to your first authenticated request in five minutes.
1. Get an API key
Keys are issued inside the OpenIRIS admin application by your organisation's Provider Admin (Administration → API keys). Ask for:
- One key per integration — separate keys for your ERP job and your ELN connector. Rotation and monitoring stay per-system, and revoking one never breaks the other.
- Only the scopes you need (see below). An ERP export job does not need
users:read. - A named integration contact — changelog and deprecation notices go to this person.
The plaintext key is shown exactly once at creation. Store it in your secret manager; OpenIRIS keeps only a hash.
2. Authenticate
Every request carries the key as a bearer token:
curl -H "Authorization: Bearer oi_live_<key-id>_<secret>" \
https://api.openiris.io/v1/ping
GET /v1/ping is the smoke test — it echoes the provider IDs and scopes your
key resolves to:
{
"status": "ok",
"provider_ids": [42],
"scopes": ["billing:read", "resources:read"],
"server_time": "2026-09-01T10:30:00Z"
}
Keys prefixed oi_test_ are for the test environment; oi_live_
for production.
3. Scopes
| Scope | Grants |
|---|---|
billing:read | Charges, line items, export history, cost centers (read) |
resources:read | Resource directory (read) |
users:read | Groups and memberships (read) |
cost-centers:write | Create / inactivate cost centers |
groups:write | Add / remove group members |
A request without the required scope returns 403; without a valid key,
401. Further scopes arrive with the roadmap surfaces.
4. Conventions you'll meet everywhere
Collections: envelope + cursor pagination
GET /v1/resources?limit=50&sort=-created_at
{
"data": [ … ],
"meta": { "total": 335, "cursor_next": "…", "cursor_prev": null },
"links": { "self": "…", "next": "…", "prev": null }
}
Follow links.next until it is null. Cursors are stable under
concurrent inserts (no skipped or duplicated rows) and are forward-only in v1.
Default page size 50, maximum 500.
Filtering and sorting
GET /v1/charges?filter[created_at][gte]=2026-01-01T00:00:00Z&filter[status]=invoiced&sort=-created_at
Operators: eq (default), ne, gt, gte,
lt, lte, in, contains,
starts_with, ends_with — each field supports a documented subset.
Unknown fields or operators return 400 naming the offending parameter, never a
silent empty result.
Errors: RFC 9457 problem details
{
"type": "https://docs.openiris.io/errors/403",
"title": "Forbidden",
"status": 403,
"detail": "scope 'billing:read' is required",
"code": "forbidden",
"trace_id": "0HNO82FT95FQR:00000001"
}
Quote the trace_id in any support request — it points us at the exact
server-side trace. Machine codes: bad_request, unauthorized,
forbidden, not_found, conflict,
unprocessable, rate_limited, internal_error.
Writes: Idempotency-Key
Every POST/PATCH requires an Idempotency-Key header
(any unique string ≤128 chars, e.g. a UUID). Retrying with the same key returns the original
response byte-for-byte (marked Idempotency-Replayed: true) instead of repeating
the action. Reusing a key with a different body returns 422.
Rate limits
600 requests/minute per key by default. Watch RateLimit-Limit /
RateLimit-Remaining on every response; a 429 carries
Retry-After. Member integrations that need more can request a per-key raise —
see the handbook.
Tenancy
Your key sees only data belonging to its resolved providers. An id outside your scope
returns 404 — indistinguishable from a nonexistent record, by design.
5. Next steps
- Posting charges to your ERP → ERP charge sync
- Syncing WBS codes or group members → Directory sync
- Full endpoint details → API reference