E-FACTSDocs
Receipt ingest

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

ParameterDescription
providerLowercase slug matching ^[a-z0-9_-]{1,32}$. Supported: square, clover, stripe, generic. Unknown slugs are rejected in production with 404 unknown_provider.

Headers

HeaderRequiredDescription
Content-TypeYesapplication/json
Signature headerYesDepends 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

StatusWhenBody
202Stored and queued for processing{ "success": true, "eventId": "<uuid>" }
400Missing body or invalid provider slug{ "success": false, "error": "raw_body_required" } or "invalid_provider"
401Signature missing or invalid{ "success": false, "eventId": "<uuid>", "error": "invalid_signature" }
404Unknown provider (production){ "success": false, "error": "unknown_provider" }
413Body larger than 1 MiB{ "success": false, "error": "payload_too_large" }
500Unexpected 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.