FR

Conventions

Common conventions across all v1 endpoints — request IDs, idempotency, cursor pagination, versioning, and transport rules.

Request IDs

Each request gets an ID in ULID format prefixed with req_. This ID is returned in 2 headers (aliased):

  • X-Request-ID: req_01J9X8Z3F4K2M5N7P9Q1R3S5T7
  • Capturia-Request-ID: req_01J9X8Z3F4K2M5N7P9Q1R3S5T7

Provide this ID to support to retrieve the incident in Sentry/logs.

Idempotency

Mutating routes (POST / PUT / DELETE) accept an optional Idempotency-Key header to ensure a retry doesn't execute the operation twice.

Idempotency-Key: a1b2c3d4-1234-4567-89ab-cdef01234567
  • Accepted format: [a-zA-Z0-9_-]{1,64} (typically a UUID v4)

  • Storage TTL: 24h in Vercel KV

  • Scope: per API key (two different keys can reuse the same Idempotency-Key without conflict)

Behaviors

ScenarioOutcome
First execution

Endpoint executed normally, response cached 24h

Replay (same payload)

Original response replayed verbatim, same status, Idempotent-Replayed: true header

Conflict (different payload)

409 idempotency_conflict with details.original_request_id

Concurrent request (lock active)

409 idempotency_conflict with details.in_progress: true

Pagination

All GET collection endpoints use opaque cursor-based pagination:

ParameterTypeDefaultMaxDescription
limitint20100

Page size (max 100, > 100 → 400 invalid_request)

cursorstring

Opaque base64url cursor returned in the previous response's pagination.next_cursor

{
  "data": [...],
  "pagination": {
    "next_cursor": "eyJ2IjoxLCJmIjoiY3JlYXRlZF9hdCIsInMiOiJkZXNjIiwidmFsIjoiMjAyNi0wNS0wNFQxMDowMDowMFoiLCJpZCI6ImN0X2FiYyJ9",
    "has_more": true,
    "limit": 20
  }
}

Error format

All errors follow a unified envelope. See Error catalog for the exhaustive list of codes.

Versioning and deprecation

URL versioning (/v1/, future /v2/). A major version is bumped only for breaking changes — backwards-compatible additions stay in the current version.

  • Minimum 12-month delay between deprecation announcement and removal

  • Headers emitted on deprecated endpoints: Deprecation (RFC 9745) + Sunset (RFC 8594) + Link: <url>; rel="deprecation"

  • Public changelog page: /developers/api/v1/changelog

Dry-run mode

The ?dry_run=true query param is parsed by the route wrapper but support varies per endpoint. Check each endpoint's reference to know whether dry-run is implemented.

Content-Type

Strict application/json (with or without charset=utf-8). Refused: multipart/form-data, application/x-www-form-urlencoded, etc → 415 invalid_content_type.

Body size

Limit: 1 MB per request. Over → 413 payload_too_large. For large batches (>100 contacts), use multiple smaller requests with distinct Idempotency-Key.

Encoding

Strict UTF-8. Any other charset rejected → 400 invalid_encoding.

Compression

Handled automatically by Vercel: Accept-Encoding: gzip, br supported. The Vary: Accept-Encoding header is emitted for intermediate caches.