Skip to main content
The LumisReach v1 API follows a consistent shape across every endpoint. The conventions on this page apply everywhere, so you can write your client code once and reuse the patterns.

Errors

Every error response uses the same envelope:

Error codes

When a request fails validation, the fields map contains a per-field explanation so you can surface the right error next to the right input.

Idempotency

Every POST, PUT, PATCH, and DELETE endpoint supports the Idempotency-Key header. Set it to a unique value (a UUID is recommended) on your first request, and retry the same request with the same header to safely replay.

Semantics

  • First request with a key. We run the handler and persist the response under the key.
  • Repeat with the same key and same body. We replay the saved response verbatim, including status code. The replay carries an Idempotency-Replay: true response header so you can tell.
  • Repeat with the same key but a different body. We return 409 idempotency_conflict with the original key’s request id in fields.conflicts_with.

TTL

Idempotency records live for 24 hours after the first successful response. After the window, reusing the key is treated as a fresh request.

Best practices

  • Use a new key for each new logical operation. Reusing keys across different calls drops traffic.
  • The key is scoped to the API key it was submitted under. Rotating keys resets the idempotency window.
  • Idempotency only protects the LumisReach side. Don’t rely on it for cross-process transactions (for example, a call placed plus a Stripe charge).

When to set one

  • Retrying a request after a network timeout — critical.
  • Background jobs that may re-run after a worker crash — critical.
  • Interactive UIs with a double-click-vulnerable button — nice to have.
Client SDKs generate idempotency keys automatically on mutations. Set yours manually only if you need to correlate retries across a longer window than the SDK’s in-memory dedupe.

Pagination

Every list endpoint returns the same envelope:
Pass ?cursor=<meta.cursor> on the next request to walk to the next page. meta.cursor is omitted on the last page. Cursors are opaque to clients — treat the value as a bag of bytes, do not parse it.

Example

Limits

  • limit default is 20, max 100.
  • Cursors are valid for 24 hours from issuance. Requesting an expired cursor returns 400 validation_error with fields.cursor.
  • Ordering is by creation time, newest first for every list endpoint. Use ?order=asc where supported to reverse.