Conventions
Rules that hold for every operation of the API reference.
Requests and responses
- JSON in and out (
application/json); timestamps are RFC 3339 in UTC with milliseconds; identifiers are UUID strings, an EID is a 32-digit string, an ICCID a string of up to 20 digits. - A single resource is returned as is; a write returns the resource with the
audit_event_idof its audit event (also in theX-Audit-Event-Idheader). - Send an
X-Request-Idto correlate your logs with ours; it is echoed, or generated when you send none.
Idempotency
Send an Idempotency-Key header (1 to 255 printable ASCII characters, e.g. a UUID) on any POST that creates something. eimHub keeps the key for 24 hours per principal:
| You send | eimHub answers |
|---|---|
| a new key | runs the request and stores the answer |
| the same key and the same body again | the stored answer, with Idempotent-Replayed: true; nothing runs twice |
| the same key while the first request still runs | 409 RESOURCE_CONFLICT — retry later |
| the same key with a different body | 422 IDEMPOTENCY_KEY_REUSED |
Retry a timed-out create with the same key and body: you get the original result, never a duplicate. Operations also take a client_reference of your own, unique in your tenant: creating the same operation again with it returns the existing one.
Pagination
Lists are cursor-paginated: ?limit=50&cursor=… returns
{ "items": [], "next_cursor": "eyJ…", "total": 1234, "total_is_estimate": false }
Pass next_cursor as cursor for the next page until it is null. total is exact up to 10 000 rows, an estimate beyond (total_is_estimate: true).
Filtering, sorting, fields
?filter[status]=ACTIVE, or with an operator:?filter[created_at][gte]=2026-10-01T00:00:00Z. Operators:eq,ne,in,gt,gte,lt,lte,contains,prefix. The filterable fields of each list are in its reference entry.?q=searches the documented keyword fields;?sort=-created_at,eidsorts (-descending).?fields=eid,status,last_seen_atreturns only those fields of each list item.
Concurrency
Single resources carry an ETag. PUT and PATCH require If-Match with it; a resource changed in between answers 412 PRECONDITION_FAILED — read it again and reapply your change.
Errors
Errors are RFC 9457 problem documents (application/problem+json):
{
"type": "about:blank", "title": "Unprocessable Content", "status": 422,
"code": "VALIDATION_FAILED", "detail": "…", "request_id": "…",
"errors": [ { "field": "parameters.iccid", "code": "pattern", "message": "…" } ]
}
Act on code, a stable string; detail is for people. Quote request_id when you contact support.
| HTTP | code |
Meaning |
|---|---|---|
| 400 | MALFORMED_REQUEST |
unparsable body, bad cursor or header |
| 401 | UNAUTHENTICATED |
missing, invalid or expired token: obtain a new one |
| 403 | FORBIDDEN |
your roles do not allow this action here |
| 404 | NOT_FOUND |
no such resource — also for one outside your scope |
| 409 | INVALID_STATE_TRANSITION, RESOURCE_CONFLICT, EUICC_EXISTS, … |
the resource's state or a uniqueness rule forbids it |
| 412 | PRECONDITION_FAILED |
If-Match does not match |
| 422 | VALIDATION_FAILED, EUICC_NOT_ACTIVE, EIM_ASSOCIATION_NOT_ACTIVE, … |
the request is understood but not allowed as sent; errors[] names the fields |
| 429 | RATE_LIMITED, QUOTA_EXCEEDED |
slow down: wait Retry-After seconds |
| 500 | INTERNAL_ERROR |
our failure; safe to retry later with the same Idempotency-Key |
| 503 | DEPENDENCY_UNAVAILABLE |
a service eimHub needs is down; retry later |
Rate limits
Requests are limited per principal: 600 per minute by default, 60 per minute on bulk endpoints. A request over the limit answers 429 with Retry-After (seconds); the X-RateLimit-* headers tell you where you stand. Back off and retry after the indicated time.
Long-running work
Imports, exports and bulk operations answer 202 Accepted with a job. Follow it at GET /api/v1/jobs/{job_id} until it is finished, then read its results.