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:

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

ScopeGrants
billing:readCharges, line items, export history, cost centers (read)
resources:readResource directory (read)
users:readGroups and memberships (read)
cost-centers:writeCreate / inactivate cost centers
groups:writeAdd / 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