Receipt ingest
POST /api/v1/webhooks/ingest/{provider}
The single endpoint every POS uses to send transactions to E-FACTS. There are no per-vendor routes: the provider path segment picks the adapter that verifies and normalizes the payload.
Path parameters
| Parameter | Description |
|---|---|
provider | Lowercase slug matching ^[a-z0-9_-]{1,32}$. Supported: square, clover, stripe, generic. Unknown slugs are rejected in production with 404 unknown_provider. |
Headers
| Header | Required | Description |
|---|---|---|
Content-Type | Yes | application/json |
| Signature header | Yes | Depends on the provider. See Webhook signatures. |
Body
The raw JSON your POS emits. For generic, use the receipt payload shape. The maximum body size is 1 MiB.
The signature is always checked against the original request bytes, so don't re-serialize the JSON between signing and sending.
Responses
| Status | When | Body |
|---|---|---|
202 | Stored and queued for processing | { "success": true, "eventId": "<uuid>" } |
400 | Missing body or invalid provider slug | { "success": false, "error": "raw_body_required" } or "invalid_provider" |
401 | Signature missing or invalid | { "success": false, "eventId": "<uuid>", "error": "invalid_signature" } |
404 | Unknown provider (production) | { "success": false, "error": "unknown_provider" } |
413 | Body larger than 1 MiB | { "success": false, "error": "payload_too_large" } |
500 | Unexpected server error | { "success": false, "error": "internal_error" } |
202 is not "receipt delivered". Normalization and wallet delivery happen asynchronously after the response. Keep the eventId: it's the reference support uses to trace an event.
Retries
Retry on network errors and 5xx with exponential backoff. Don't retry 400, 401, 404 or 413 unchanged: fix the request first. Always send the same transaction_id for the same sale so retries can be reconciled.
Privacy
Before anything is stored, values that look like full card numbers are replaced with [REDACTED_PAN], and payment tokens that look like card numbers are rejected. Send your processor's token or a fingerprint, never the card number.