E-FACTSDocs
Quickstart

Quickstart

Send one signed test receipt to E-FACTS and confirm it was accepted. This guide uses the generic adapter, which works with any POS. If you're on Square, Clover or Stripe, read POS integrations instead.

1. Request credentials

During the public preview, email info@efacts.ca with:

  • your business name and POS system
  • the store IDs your POS will send (one per location). Receipts from unmapped stores are held until the store is mapped.
  • your expected monthly receipt volume

You'll receive:

CredentialUsed for
EFACTS_BASE_URLThe ingest host for your environment
EFACTS_WEBHOOK_SECRETSigning each request (HMAC-SHA256)

Keep the secret server-side. Never ship it inside a browser, mobile app or POS client that customers can inspect.

2. Build a receipt

Save this as receipt.json. Money is in integer cents when amounts_in_cents is true.

{
  "store_id": "STORE_001",
  "transaction_id": "TXN_0001",
  "timestamp": "2026-09-26T15:04:05Z",
  "currency": "CAD",
  "amounts_in_cents": true,
  "subtotal": 1400,
  "tax": 175,
  "total": 1575,
  "payment_token": "tok_from_your_processor",
  "items": [
    { "sku": "GEN-001", "description": "Notebook", "quantity": 2, "unit_cents": 500 },
    { "sku": "GEN-002", "description": "Pen set", "quantity": 1, "unit_cents": 400 }
  ]
}

3. Sign and send it

The signature is the lowercase hex HMAC-SHA256 of the exact bytes you send, keyed with your secret.

export EFACTS_BASE_URL="https://…"        # from step 1
export EFACTS_WEBHOOK_SECRET="…"          # from step 1

SIG=$(openssl dgst -sha256 -hmac "$EFACTS_WEBHOOK_SECRET" -hex < receipt.json | awk '{print $NF}')

curl -sS -X POST "$EFACTS_BASE_URL/api/v1/webhooks/ingest/generic" \
  -H "Content-Type: application/json" \
  -H "X-Efacts-Signature: $SIG" \
  --data-binary @receipt.json

Use --data-binary, not -d: -d strips newlines, which changes the bytes and breaks the signature.

4. Check the response

{ "success": true, "eventId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }

202 Accepted means the receipt was stored and queued. Normalization runs asynchronously, so keep the eventId for support requests. A 401 with "error": "invalid_signature" means the signature did not match. See Webhook signatures for the usual causes.

Next steps