SmartEPI External API
REST API for integrating SmartEPI organizational and transactional data with external systems (ERP / BI / internal tools).
Authentication. Send your API key as a bearer token:
Authorization: Bearer sk_…. Keys are minted in the web console
(Settings ▸ API Integration). A key is tenant-scoped — it only ever reads and
writes your own organization's data — and grants full access to every
endpoint in this API.
Versioning. The contract is versioned in the path (/v1). Within a
version, changes are additive only.
Envelope. Every response wraps its payload: a single record is
{ "data": <entity> }; a list is { "data": [<entity>, …], "cursor": <string|null> }.
Pagination. List endpoints return an opaque cursor; pass it back as the
cursor query parameter to fetch the next page. A null cursor means the
last page. Treat the cursor as opaque.
Errors. Application errors (400/403/404/405/409/422/500) use
RFC 7807 application/problem+json with a type of the form
urn:smartepi:error:<token>, and carry an X-Request-Id header. (A 403
means a key was sent but rejected — e.g. it is invalid/unknown/revoked/expired,
its IP allow-list blocked the source IP, or it is newly created and not yet
activated.) Authentication/authorization failures are gateway-level: they are
rejected by the API gateway before the application runs, so they return a plain
body ({ "message": "Unauthorized" } for a missing key → 401; the raw AWS
authorizer-deny JSON with a CAPITAL Message —
\{ "Message": "User is not authorized to access this resource with an explicit deny in an identity-based policy" \} — for any rejected key or IP block → 403;
{ "message": "Too Many Requests" } for a rate limit → 429) — not RFC 7807,
and without an X-Request-Id. Branch on the status code, never the body text.
Only a sent-but-rejected key returns 403; a missing Authorization header
returns 401.
Writes & concurrency. Organization entities (sectors, cost centers, job
roles, groups, employees, products) support create / update / delete.
Updates and deletes use optimistic concurrency via the
ETag/If-Match headers: every read and write response returns the
record's current version as a strong ETag (e.g. ETag: "4"); send that value
back as If-Match on a subsequent update/delete. A stale value returns 409;
a missing one returns 400. (The body field _version is also accepted on
write, but the version is never returned in any response body — only via
ETag.) A successful create returns 201 with a Location header pointing at
the new resource. Any foreign key you set must exist in your organization or
the write is rejected with 422. A delete soft-deletes the record and returns
200 with the deleted entity as { "data": <entity> }; the record then
disappears from lists and GET-by-id returns 404. A delete is rejected with
409 (resource_in_use) if the entity is still referenced by other records
(e.g. an employee's sectorId, or a product in a group rule) — re-point or
remove the dependents first. Deleting a product also removes its sizes +
certificates. Transactions are read-only — devices are their sole origin.
Access. Every API key grants full access to all endpoints in this API. There are no per-entity scopes — a key can read and write every organization entity and read every transaction, within your own organization only.
Rate limits. Throttling is per organization (all your keys share one
bucket). Exceeding it returns 429. A 429 is enforced by the API gateway,
so its body is gateway-shaped JSON ({"message":"..."}), not an RFC 7807
problem and without an X-Request-Id header. Defaults are generous;
contact your provider if you need more.
Record id. Every entity carries an id. (Note: a small number of legacy
records may omit id; current records always include it.)
Webhooks. Register an endpoint in the console (Settings ▸ API
Integration) to receive real-time, HMAC-signed events
(withdrawal.created, return.created, exchange.created,
training_record.created). Each delivery is a POST whose
body matches the WebhookEvent schema. Verify the
X-SmartEPI-Signature: t=<unix>,v1=<hmac-sha256-hex> header by computing
HMAC-SHA256(secret, "<t>.<rawBody>") and comparing in constant time;
reject deliveries whose t is outside a ±300s window (replay protection).
Each delivery also carries X-SmartEPI-Event-Type (the event type, e.g.
withdrawal.created) and X-SmartEPI-Event-Id (a stable idempotency key).
When subscribing an endpoint you may register for specific event types or
* (all types); * is only a subscription selector and never appears as a
delivered event's type. Endpoints auto-disable after 100 consecutive
failures.
Authentication
- HTTP: Bearer Auth
Security Scheme Type: | http |
|---|---|
HTTP Authorization Scheme: | bearer |
Bearer format: | sk_… |