PurrOSDocs

Errors & limits

RFC 9457 Problem Details with stable error codes, idempotency keys for safe retries, and rate limits.

Errors

Errors use RFC 9457 Problem Details with a stable code:

{
  "type": "https://purros.dev/errors/validation",
  "title": "Validation failed",
  "status": 422,
  "code": "validation_error",
  "requestId": "req_01J8Z…",
  "errors": [{ "path": "lines[0].quantity", "message": "Must be a decimal string" }]
}

Branch on code, not on title or status alone:

StatuscodeMeaning
400bad_requestMalformed JSON or parameters
401unauthorizedMissing, invalid, expired or revoked key, or wrong sign-in details
401session_expiredThe session ended; sign in again
401mfa_requiredFinish signing in at POST /auth/sign-in/mfa
403forbiddenThe key lacks the scope, or the person lacks the permission
403out_of_reachThe person's permission doesn't reach this location or employee
403mfa_enrollment_requiredThe company requires two-factor authentication; set it up first
404not_foundNo such record
404feature_disabledThe feature is switched off
409conflictDuplicate, or an idempotency key reused with a different body
409version_mismatchIf-Match didn't match the current version
409period_lockedThe business day or pay period is locked
413payload_too_largeAn upload is larger than STORAGE_MAX_UPLOAD_MB (Attachments)
422validation_errorField errors in errors[]
422insufficient_stockNot enough stock to reserve or ship
429rate_limitedSlow down, and see Retry-After
5xxinternal_errorRetry with backoff, and quote requestId if reporting it

Every response includes an X-Request-Id header. The same ID appears in the server's logs, so quote it when reporting a problem.

What to retry

Idempotency

Send an Idempotency-Key header on any POST. If a request with the same key is repeated within 24 hours, you get the original response instead of a duplicate. The same key with a different body returns 409 conflict.

POST /api/v1/cash/deposits HTTP/1.1
Authorization: Bearer pk_live_…
Idempotency-Key: 5f0c7e2a-8f59-4a4e-9a0e-6b1d3c1f0e21
Content-Type: application/json

Ingestion endpoints are also idempotent by (source, externalId), so re-sending the same record updates it rather than duplicating it. See Data ingestion.

Rate limits

Key typeDefault
Interactive / personal keys600 requests per minute
Keys sending data feeds3,000 requests per minute

Every response has RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. When over the limit you get 429 with Retry-After. Use batch endpoints for volume: one batch of 1,000 records counts as one request.

Server operators change the defaults with API_RATE_LIMIT_PER_MIN, API_INGEST_RATE_LIMIT_PER_MIN and API_MAX_BATCH_SIZE (Configuration).

On this page