Partner API

Paidbon ingests receipts from POS systems and payment processors. You can either use a native adapter (Stripe, Adyen, Mollie, Square, SumUp, PayPal, Zettle, myPOS) or post a canonical JSON payload to the generic endpoint described below.

Which should I use?

Connect your POS whenever possible. A POS webhook carries the full receipt — line items, quantities, VAT per line, discounts, loyalty and exit barcodes — which is what makes Paidbon feel like a real receipt to your customer.

  • POS / cash register (recommended): Square, SumUp, Zettle by PayPal, Adyen POS, Lightspeed, and any custom POS via the generic HMAC endpoint below.
  • Payment processor (fallback): Stripe, Mollie, Adyen (online), PayPal, myPOS. You'll only get proof of payment — amount, currency, timestamp, card brand — no line items.

Using both is fine. Paidbon deduplicates automatically: official POS receipts win over payment-processor records, which win over bank proofs of payment.

1. Get an endpoint

A merchant signs up at Paidbon, opens Profile → Integrations and creates a Generic / Custom POS endpoint. They receive:

  • A unique webhook URL: /api/public/webhooks/generic/<endpoint-id>
  • A 64-char signing secret (rotatable from the dashboard)

2. Canonical receipt payload

POST a JSON body with the following shape. Only merchant_name and amount are required.

{
  "merchant_name": "Café Centraal",
  "amount": 12.50,
  "currency": "EUR",
  "payment_method": "card",
  "receipt_number": "INV-2025-0001",
  "category": "Food & drink",
  "status": "completed",
  "receipt_date": "2025-01-15T14:32:00Z",
  "customer_email": "customer@example.com",
  "items": [
    { "description": "Espresso", "quantity": 1, "unit_price": 2.50, "total": 2.50, "tax": 0.43 },
    { "description": "Sandwich", "quantity": 1, "unit_price": 10.00, "total": 10.00, "tax": 1.74 }
  ],
  "notes": "Table 5"
}
  • amount — decimal, in the major unit of the currency (e.g. EUR not cents).
  • statuscompleted / pending / refunded.
  • customer_email — if present and the email matches a Paidbon user, the receipt is routed to their personal wallet.
  • items[] — optional line items for itemized receipts.

3. Sign the request

Compute HMAC-SHA256(secret, raw_body) in hex and pass it in the x-paidbon-signature header. Use the exact body bytes you sent — do not re-stringify.

# 1. Compute HMAC-SHA256 of the raw JSON body using your endpoint's signing secret
BODY='{"merchant_name":"Café Centraal","amount":12.50,"currency":"EUR","status":"completed"}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$PAIDBON_SECRET" -hex | awk '{print $2}')

# 2. POST to your unique endpoint URL
curl -X POST "https://paidbon.app/api/public/webhooks/generic/<your-endpoint-id>" \
  -H "content-type: application/json" \
  -H "x-paidbon-signature: $SIG" \
  -d "$BODY"

4. What Paidbon does next

  • Verifies the signature and stores the raw payload for audit.
  • Enriches the merchant (logo + category).
  • Fuzzy-matches the receipt to the customer's bank transaction (±€0.05 / ±15 min) and replaces the auto-generated proof of payment with your official receipt.
  • Pushes a notification to the customer's phone if they accept push.

5. Native provider adapters

If you use one of the supported processors, create a provider-specific endpoint instead of generic — Paidbon will verify the provider's own signature scheme and normalize their payload automatically:

  • Stripe
  • Adyen
  • Mollie
  • Square
  • SumUp
  • PayPal
  • Zettle by PayPal
  • myPOS

6. Responses

  • 201 — receipt accepted and stored.
  • 200 { ignored: true } — event recognized but not a receipt-creating type.
  • 401 — invalid signature.
  • 404 — unknown endpoint or provider.
  • 413 — payload exceeds 512 KB.