Skip to main content
Version: 1.0.0

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

Security Scheme Type:

http

HTTP Authorization Scheme:

bearer

Bearer format:

sk_…