Saltar al contenido
Docs
Español
Esc
navigateopen⌘Jpreview
En esta página

Errors and idempotency

RFC 7807 problem details, Idempotency-Key, ETag optimistic concurrency, and rate limits for the Fuxam API v4.

When something goes wrong — or when you intentionally retry — the Fuxam API v4 follows a small set of strict rules: RFC 7807 problem documents for errors, Idempotency-Key for safe creates, ETag / If-Match for safe updates and deletes, and RateLimit-* / Retry-After when you hit the shared budget. Learning these once makes every endpoint safer to automate.

Why it matters

Integrations fail in the real world: networks drop mid-POST, two jobs update the same user, a sync loop exceeds 1000 requests per minute. Without idempotency and concurrency controls you risk duplicate records or silent overwrites. Without a stable error code you cannot branch reliably in client code. This chapter is the contract for those failure modes.

How it fits

Read this alongside the Overview summary, Authentication for 401 cases, and Recipes for copy-ready patterns.

Key concepts

Concept Meaning
Problem details RFC 7807 JSON body with Content-Type: application/problem+json.
ApiErrorCode Closed set of stable top-level code values (for example resource_not_found).
Zod issue code Per-field validation codes inside errors[] (for example invalid_string) with RFC-6901 pointers.
Idempotency-Key Client-generated key that makes a create POST replay-safe within the idempotency window.
ETag / If-Match Optimistic concurrency: write only if the resource version still matches.
Rate limit 1000 requests/min per integration + institution (sliding window).

Error model (RFC 7807)

All errors use application/problem+json. The type field is the URI https://fuxam.app/errors/{code}.

There are two distinct code namespaces:

Namespace Where Purpose
Top-level code Root of the problem document Closed ApiErrorCode union — machine-readable and stable across versions of your client
errors[].code Optional array entries Zod issue codes (for example invalid_string) with an RFC-6901 pointer (for example /email)

Example:

{
  "type": "https://fuxam.app/errors/resource_not_found",
  "title": "Not Found",
  "status": 404,
  "code": "resource_not_found"
}

No 403 — privacy by uniformity

v4 emits no 403. Cross-tenant access returns the same 404 resource_not_found as a truly missing resource. Existence of another institution’s ids is never leaked. Client code should treat 404 as “not available to you,” not only “never existed.”

Validation failures return HTTP 422.

Common status codes

Status Typical meaning
200 Successful read or update
201 Resource created
204 Resource deleted (no body)
400 Bad request (for example invalid_cursor)
401 Authentication failed
404 Missing resource or cross-tenant denial (resource_not_found)
409 Conflict, or in-flight idempotency lock
412 ETag mismatch (precondition_failed)
422 Validation failure
428 Required If-Match missing (precondition_required)
429 Rate limit exceeded — honor Retry-After

Idempotency for creates

POST requests that create resources accept an Idempotency-Key header. Submitting the same key twice within the idempotency window returns the cached response (including the original 201) without creating a duplicate. Conflicting in-flight keys may return 409.

POST /api/v4/users HTTP/1.1
Host: fuxam.app
Authorization: Basic <base64(clientId:clientSecret)>
Content-Type: application/json
Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Accept: application/json
IDEMPOTENCY_KEY="$(uuidgen)"

curl -sS \
  -u "${CLIENT_ID}:${CLIENT_SECRET}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ${IDEMPOTENCY_KEY}" \
  -d @create-body.json \
  "https://fuxam.app/api/v4/users"

Use the JSON body documented on the create operation’s endpoint reference — the important part of this pattern is reusing the same Idempotency-Key on retries.

Optimistic concurrency (ETag)

Single-resource GETs emit a strong ETag. Send it back as If-Match on PATCH, PUT, or DELETE:

Outcome Status / code Meaning
Version still current Success (200 / 204) Write applied
Resource changed since read 412 precondition_failed Re-GET, merge, retry with new ETag
If-Match required but missing 428 precondition_required Include If-Match
GET /api/v4/users/usr_123 HTTP/1.1
Host: fuxam.app
Authorization: Basic <base64(clientId:clientSecret)>
Accept: application/json

Response includes:

ETag: "abc123version"

Then:

PATCH /api/v4/users/usr_123 HTTP/1.1
Host: fuxam.app
Authorization: Basic <base64(clientId:clientSecret)>
Content-Type: application/json
If-Match: "abc123version"
Accept: application/json
ETAG='"abc123version"'

curl -sS \
  -u "${CLIENT_ID}:${CLIENT_SECRET}" \
  -X PATCH \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "If-Match: ${ETAG}" \
  -d @patch-body.json \
  "https://fuxam.app/api/v4/users/usr_123"

Put the fields you intend to change in patch-body.json per the endpoint reference; always pair the write with the ETag you just read.

Rate limits

Each integration shares a 1000 requests/min budget (sliding window, keyed by integration + institution). Responses include RateLimit-* headers. When you exceed the budget:

  • Status: 429
  • Header: Retry-After (honor it before retrying)

Retry and backoff guidance

Treat retries as a layered policy:

  1. 429 — sleep for Retry-After seconds (or the suggested delay your client parses), then retry the same request.
  2. Transient network failures on safe reads — retry with exponential backoff and jitter.
  3. Transient failures on creates — retry with the same Idempotency-Key.
  4. 412 on writes — do not blind-retry the same body with the old ETag; re-read, reconcile, send a new If-Match.
  5. 422 / 401 / most 400s — fix the request; retries without change will keep failing.

Best practices

  • Branch on top-level code, not only on human-readable title text.
  • Surface meta.requestId from successful calls and any identifiers returned in problem documents when logging failures.
  • Always send Idempotency-Key from automated create jobs.
  • Always send If-Match on single-resource writes that require it.
  • Map 404 to a single “unavailable” path in UI or sync logic — do not special-case a fictional 403.

Common pitfalls

Pitfall Consequence Fix
Retrying POST without Idempotency-Key Duplicate resources Generate and reuse one key per logical create
Ignoring 412 Overwrite races or endless retries Re-GET and merge
Treating 404 as “delete from local cache” always May hide permission/tenant issues Confirm with a known-good id first
Busy-looping on 429 Longer lockout, wasted compute Honor Retry-After

FAQ

What is the difference between the two code fields?

Top-level code is the stable API error classification. Entries in errors[].code describe individual validation issues (Zod) and include a pointer to the failing field.

Will I ever see HTTP 403?

Not from v4’s cross-tenant denial path — that is intentionally 404. Design clients accordingly.

How long is the idempotency window?

The overview documents that replays within the idempotency window return the cached response. Treat the key as valid for reasonable retry periods; do not rely on keys forever — use a new key for a new logical create.

¿Te ha resultado útil esta página?