API reference

Every endpoint on both sides of the integration. Bookmark this page; you will read it more than once.

Endpoints you host (Pattern A)

MethodPathAuthPurpose
GET /oauth/authorize Merchant session Display consent screen, redirect with code.
POST /oauth/token Client credentials Exchange code or refresh token for an access token.
POST /oauth/revoke Client credentials Invalidate a token server-side on disconnect.
GET /v1/merchants/me Bearer access token Merchant profile (name, country, tax number).
GET /v1/locations Bearer access token List the merchant's locations.
GET /v1/orders/{id} Bearer access token Fetch a completed order.
GET /v1/customers/{id} Bearer access token Resolve a POS customer's email/phone (optional but recommended).

Endpoints Paperless hosts

MethodPathAuthPatternPurpose
GET /integrations/{provider}/oauth/callback Public (CSRF via state) A OAuth redirect URI. Receives code and state.
POST /webhooks/{provider} HMAC signature A Receive signed event envelopes.
POST /ingest/{provider}/receipts API key + HMAC signature B Receive fully-formed receipt pushes.
POST /debug/verify-signature HMAC signature A, B Sandbox/staging only. Echoes back signature validity for testing.

Status codes (summary)

CodeMeaningRetry?
200Accepted (idempotent paths) or duplicate.No.
201Created (push receipt only).No.
400Validation error in payload.No — fix and re-send.
401Bad signature / bad token / skew.No — investigate.
403Authenticated but not authorized for this merchant.No.
404Unknown provider, merchant, or order.No.
409Conflict (e.g., same external_id with different totals).No — fix and re-send under a new external_id.
413Payload too large (> 256 KB).No.
422Semantically invalid (e.g., subtotal + tax ≠ total).No.
429Rate limited.Yes — honor Retry-After.
5xxTransient infra issue.Yes — exp. backoff.

Error response shape

All Paperless error responses use a stable envelope:

{
  "error": {
    "code":    "invalid_signature",
    "message": "X-Paperless-Signature did not match the expected value.",
    "request_id": "req_01HZX0K2A7B6C5D4E3F2G1H0J9"
  }
}
FieldNotes
error.codeMachine-readable. Stable across versions.
error.messageHuman-readable. Wording may change.
error.request_idGive this to ops when filing a ticket. Logs are indexed by it.

Common error codes

CodeHTTPWhat it means
invalid_signature401HMAC mismatch.
signature_skew401X-Paperless-Timestamp is outside the ±5-minute window.
unknown_provider404{provider} slug not recognized.
unknown_merchant404merchant_id not recognized.
schema_validation400Field missing / wrong type. error.message names the field.
totals_mismatch422subtotal + tax ≠ total or sum(payments) ≠ total.
currency_mismatch422Mixed currencies in the same order.
pan_in_last4400last4 looks like a PAN. Receipt rejected as a possible leak.
duplicate_external_id409Same external_id, different body. Use a new ID for a new receipt.
rate_limited429Per-merchant or per-provider rate limit hit.

Rate limits

SurfaceDefaultNotes
POST /webhooks/{provider}200 req/s/providerSoft. Bursts to 500/s are accepted.
POST /ingest/{provider}/receipts100 req/s/merchantHard. 429 with Retry-After.
Order fetch from Paperless to your /v1/orders/{id}~30 req/s/merchantYou should accept bursts. Paperless will back off on 429.
Token refresh1 req per merchant per day in steady stateBurst on rotation events.

Request a higher ceiling through your solutions engineer if your peak load exceeds these.

Versioning

Idempotency-Key header

Supported on every POST Paperless hosts. If a request with the same Idempotency-Key arrives within 24 hours of a prior success, Paperless replays the prior response without re-executing.

Time format

Every timestamp is ISO 8601. Always include a timezone. Examples Paperless accepts:

2026-05-15T17:21:19Z
2026-05-15T17:21:19+00:00
2026-05-15T13:21:19-04:00

Paperless rejects naive timestamps without a zone offset.