Crypto Invoicing API: Invoice Paid Webhooks and Integration Patterns
Integrate crypto invoicing with your backend using REST APIs and invoice.paid webhooks — HMAC verification, idempotency, retry logic, and fulfillment automation patterns.
Do this next
Create a crypto invoice
- Quote the invoice in fiat.
- Client pays USDC, USDT, or EURC.
- Funds settle to a wallet you already control.
TL;DR
The crypto invoicing API lets you create fiat-quoted invoices programmatically; invoice.paid webhooks fire after on-chain confirmation with tx hash, amount, network, and invoice metadata. Verify HMAC signatures, process idempotently, and use webhooks to trigger fulfillment — license keys, API access, ERP journals — without polling block explorers. Full reference at /developers; test in demo. Product: Settlematic Collect.

Manual invoice workflows break at scale. SaaS platforms issuing hundreds of usage invoices, marketplaces settling seller payouts, and engineering orgs closing milestone billing all need the same primitive: when this invoice pays on-chain, tell my backend reliably.
Webhooks are that primitive. This guide covers API invoice creation, webhook payload structure, security verification, integration patterns, and failure handling — the material your backend team needs before production.
API + webhook architecture
Polling GET /invoices/:id works for prototypes. Webhooks work for production — lower latency, less load, event-driven architecture.
Related: crypto payment webhooks reconciliation for broader gateway patterns; this article focuses on invoicing specifically.
Creating invoices via API
Typical request shape (illustrative — confirm against /developers):
Response includes:
| Field | Use |
|---|---|
| id | Store for webhook matching |
| payment_url | Redirect customer or embed link |
| status | open → paid → expired |
| deposit_address | Per-network addresses if exposed |
| amount_display | Fiat-formatted string for emails |
Store id and metadata before returning payment URL to client. Webhook will echo these fields.
Idempotency
Send Idempotency-Key header on create to prevent duplicate invoices when your job retries:
Same key within 24 hours returns original invoice — critical for queue workers that at-least-once deliver.
Invoice paid webhook payload
When on-chain detection confirms payment:
Fields your fulfillment logic needs:
- invoice_id — primary key for status update
- transaction_hash — audit trail, explorer link
- metadata — your correlation IDs
- amount_crypto vs amount_fiat — verify expected payment
- network — route to correct explorer/parser
HMAC signature verification
Never process unverified webhooks. Settlematic signs payloads with shared secret:
Implementation checklist:
- Read raw body before JSON parse (signature is over raw bytes)
- Extract signature from header (e.g., X-Settlematic-Signature)
- Constant-time comparison
- Reject if timestamp skew > 5 minutes (replay protection if timestamp included)
Rotate webhook secrets periodically; support dual secrets during rotation window.
Idempotent webhook processing
Webhooks deliver at least once. Your handler must tolerate duplicates:
Use invoice_id + transaction_hash as natural idempotency key if event IDs unavailable.
Return 2xx quickly — heavy work belongs in async queue. Timeouts cause retries that amplify duplicate risk if idempotency weak.
Integration patterns
Pattern 1: SaaS license unlock
Map metadata.internal_user_id to user record. Set plan=pro, valid_until=+1 year.
Pattern 2: ERP journal entry
Include tx hash in memo field. Finance reconciles blockchain receipt to GL.
Pattern 3: Marketplace seller notification
Non-custodial: seller already received funds on-chain; webhook triggers operational workflow only.
Pattern 4: Remote team milestone
See crypto invoicing for remote dev teams.
Pattern 5: Recurring billing cycle
Pair with recurring crypto invoices.
Polling fallback
Implement GET /invoices/:id poll every 60s as backup when:
- Webhook endpoint down during maintenance
- Firewall misconfiguration discovered post-deploy
- Local dev without tunnel (use ngrok / Cloudflare tunnel for webhook dev)
Webhook primary; poll safety net. Do not poll block explorers directly — invoice API abstracts chain variance.
Error handling and retries
| Your response | Platform behavior |
|---|---|
| 2xx | Success; no retry |
| 4xx (except 429) | Usually no retry — fix handler |
| 5xx / timeout | Retries with exponential backoff |
| 429 | Retry after delay |
Log failed webhook deliveries to alerting. Dashboard replay button (if available) or manual poll after fix.
Testing in sandbox
Before mainnet:
- Create demo / sandbox API keys
- Create test invoice via API
- Pay with testnet USDC
- Capture webhook with request bin or tunnel
- Verify signature validation code
- Confirm idempotency on replayed delivery
Test crypto payments sandbox covers environment setup.
Security hardening
| Risk | Mitigation |
|---|---|
| Forged webhook | HMAC verification |
| Replay attack | Timestamp tolerance + idempotency store |
| Secret leak | Rotate secret; audit logs |
| SSRF from webhook handler | Do not fetch user-controlled URLs on webhook receipt |
| Invoice amount tampering | Verify amount in webhook matches your order record |
Crypto payment security best practices apply to integration layer.
Observability
Log structured fields:
- invoice_id, event, transaction_hash, network
- Processing duration
- Idempotency skip vs fresh process
Metrics: webhook lag (paid_at → processed_at), failure rate, duplicate rate.
Alert if lag exceeds SLA — customer waiting for license key while payment confirmed on-chain erodes trust.
API authentication
Production API keys via Bearer token or signed requests — see /developers. Separate keys per environment (sandbox vs production). Never commit keys; use secret manager.
Scoped permissions: invoice create/read vs admin settlement config.
Partial and failed payment events
Additional events (names illustrative):
| Event | Action |
|---|---|
| invoice.partially_paid | Notify collections; hold fulfillment |
| invoice.expired | Cancel pending order |
| invoice.created | Analytics only |
Subscribe only to events you handle — reduces noise.
Non-custodial implication for webhooks
Webhook confirms detection of payment to your bound address — not custody release. Unlike custodial processors, there is no "payout completed" event because funds never entered platform custody. Your fulfillment obligation may start at detection; funds are already yours on-chain.
Understand custodial vs non-custodial to set internal policy correctly.
Comparison: build vs buy chain monitoring
| Approach | Cost | Complexity |
|---|---|---|
| DIY RPC + indexer + reorg handling | Engineering months | High |
| Settlematic API + webhooks | Subscription | Low |
DIY makes sense at hyperscale with dedicated blockchain team. Everyone else ships product faster with invoicing API.
Webhook delivery guarantees and SLAs
Production webhook expectations:
| Metric | Reasonable SLA |
|---|---|
| Delivery after detection | <30 seconds p95 |
| Retry window | 24–72 hours exponential backoff |
| Max retries | 10–20 attempts |
| Dead letter | Dashboard alert after exhaustion |
Implement monitoring on your endpoint — 5xx rates during deploys cause retry storms. Use blue-green deploy or queue webhooks during maintenance.
Rate limits and batch invoice creation
API rate limits protect platform stability:
| Operation | Typical limit strategy |
|---|---|
| POST /invoices | 100/min per API key |
| GET /invoices/:id | 1000/min |
| Webhook deliveries | Platform-controlled |
Batch billing run at month-end: queue invoice creation with backoff; use idempotency keys per row. Burst creation without keys duplicates invoices when worker retries.
Versioning and backward compatibility
Webhook payload fields may expand — design consumers to ignore unknown fields (forward compatibility). Pin API version in URL path (/v1/) and subscribe to changelog for breaking changes.
Never branch fulfillment logic on undocumented fields — stick to documented invoice_id, transaction_hash, metadata.
Local development workflow
Keep sandbox webhook secret separate from production — common leak vector when copying .env files.
Production deployment checklist
Before switching webhook URL to production:
- [ ] HMAC verification unit tests pass
- [ ] Idempotency store (DB/Redis) deployed
- [ ] Endpoint HTTPS with valid cert
- [ ] 2xx response within 5 seconds
- [ ] Alerting on 5xx rate >1%
- [ ] Runbook for replay after outage documented
- [ ] Sandbox end-to-end test logged this week
Ship fulfillment integration only after checklist complete — test sandbox catches 90% of issues.
Mapping webhook events to your database schema
Recommended tables:
| Table | Key columns |
|---|---|
| invoices | id, external_invoice_id, status, amount_cents |
| payments | invoice_id, tx_hash, network, paid_at |
| webhook_events | event_id, processed_at, payload_hash |
Unique index on tx_hash prevents duplicate payment rows when webhook retries.
GraphQL vs REST for invoice status
REST GET /invoices/:id suffices for polling fallback. GraphQL optional if your stack already uses it — no functional advantage for webhook-primary architecture. Choose REST for simpler cache semantics and curl-debuggable integrations during incident response.
Multi-environment webhook routing
Use separate webhook URLs per environment:
| Environment | URL | Secret |
|---|---|---|
| Development | ngrok tunnel | dev_secret |
| Staging | staging.app/webhooks | staging_secret |
| Production | app/webhooks | prod_secret |
Never point staging webhook at production fulfillment DB — classic source of test invoices unlocking real licenses. Environment isolation is non-negotiable for payment automation.
Event ordering and out-of-sequence delivery
Webhook consumers must handle events arriving out of chronological order. A client may pay invoice inv_002 before inv_001 if AP processes invoices in reverse due date order. Your handler should update each invoice independently — never assume invoice.paid events sort by created_at in the delivery stream.
Edge cases to code for:
- invoice.expired followed by invoice.paid — Client paid during grace period after expiry job ran. Policy: accept payment if on-chain confirmation occurred before hard cutoff; otherwise manual review.
- invoice.partially_paid then invoice.paid — Fulfillment should wait for final event unless business rules allow partial delivery.
- Duplicate invoice.paid with identical tx_hash — Idempotency store returns 200 without re-fulfilling.
State machine recommendation:
Persist state transitions in your database with from_status, to_status, and event_id for audit. Debugging "customer says paid but no access" almost always traces to missing transition handling, not webhook failure.
Load testing webhook endpoints before launch
Payment automation fails embarrassingly under load when month-end billing creates 500 invoices in an hour and treasury pays in a burst. Before production:
- Synthetic webhook replay — Replay captured sandbox payloads 100× concurrent against staging endpoint; verify idempotency and p95 latency under 2 seconds.
- Queue depth monitoring — If fulfillment runs async, alert when queue depth exceeds 50 for more than 5 minutes.
- Database connection pool — Webhook handler opening new DB connection per request exhausts pools under burst; use connection pooling.
- Circuit breaker on downstream — If license API is down, return 503 to trigger platform retry rather than 200 with silent failure.
| Load scenario | Target p95 handler time | Max acceptable error rate |
|---|---|---|
| 10 concurrent webhooks | <500ms | 0% |
| 100 concurrent webhooks | <2s | <0.1% |
| Sustained 50/min for 1 hour | <1s | <0.01% |
Document load test results in runbook — enterprise clients ask about payment burst handling during security reviews.
Webhook vs polling: operational comparison
| Dimension | Webhook (invoice.paid) | Polling (GET /invoices/:id) |
|---|---|---|
| Latency | Seconds after confirmation | Depends on poll interval |
| Server load | Event-driven, minimal | Linear with open invoice count |
| Reliability | Requires endpoint uptime + retries | Works through brief outages |
| Complexity | HMAC verify, idempotency | Simpler handler |
| Best use | Production fulfillment | Backup reconciliation job |
Run both: webhooks for real-time fulfillment, hourly poll for invoices stuck in open status more than 24 hours past expected payment — catches missed webhooks from deploy blips without polling block explorers.
Contract testing and schema validation
Webhook payloads evolve — new fields appear without breaking old consumers if you follow forward-compatible parsing. In CI:
- Store golden JSON fixtures from sandbox invoice.paid events per network (Solana signature format vs EVM tx hash).
- Validate your parser against fixtures on every deploy.
- Alert if production webhook payload schema drifts from fixture (optional JSON schema validation in staging proxy).
Fields your fulfillment must never ignore: invoice_id, transaction_hash, amount_crypto, network, metadata. Fields safe to ignore until needed: payer_address, confirmations, fee_paid.
Version API client libraries when /developers documents breaking changes — pin settlematic-sdk version in production requirements.
Webhook dead-letter queues and manual replay procedures
When webhook delivery exhausts platform retry budgets, events land in a dead-letter state requiring operator intervention. Production teams should configure dashboard alerts or API polling for failed deliveries — do not discover dead-letter backlog only when customers complain about missing licenses three days after payment. Runbook steps: identify failed invoice.paid events in the webhook delivery log, verify payment confirmed on-chain via explorer, confirm your endpoint returned 5xx during the incident window, fix the handler bug or complete deploy, then trigger manual replay from the dashboard or request platform support replay. Document each manual replay in your incident tracker with invoice_id, original failure timestamp, and root cause category.
Dead-letter events differ from idempotent duplicates. Duplicates are healthy retries where the handler already processed successfully; dead-letter events never received a successful 2xx acknowledgment. Your nightly reconciliation job should cross-reference open invoices with on-chain payments regardless of webhook health — dead-letter recovery is backup, not primary detection. Enterprise deployments processing more than five hundred payments monthly should dedicate on-call rotation familiar with replay procedures and maintain a staging endpoint that replays production payloads with PII redacted for regression testing after handler fixes.
Maintain dead-letter dashboard widget visible to engineering and finance — payment confirmed on-chain but fulfillment pending more than one hour triggers automatic escalation regardless of whether root cause is webhook DLQ or handler bug.
Staging parity for webhook integration testing
Staging webhook environments should mirror production handler code paths, not simplified mocks that skip signature verification or database writes. Parity checklist: same HMAC verification code with environment-specific secrets, same idempotency store schema, same async queue infrastructure, same database transaction boundaries. Engineers testing against simplified handlers routinely ship bugs where staging passes but production fails under real payload shapes or connection pool exhaustion.
Synthetic load tests belong in staging before production URL swap — replay one hundred concurrent sandbox invoice.paid payloads and measure handler p95 latency. Staging should also receive webhooks from actual testnet USDC payments, not only replayed JSON fixtures, because real delivery exercises TLS, DNS, and firewall allowlists that local curl tests skip. Document staging and production webhook URLs in the team wiki; quarterly verify both endpoints respond to health checks and that ngrok tunnels for local development are not accidentally registered as production destinations after engineer laptop sleep cycles break tunnel URLs.
Rotate staging secrets on the same calendar cadence as production — teams that never rotate staging secrets leak them into production .env copies during rushed deploys. Include webhook handler version in deployment tags so incident response can correlate failed deliveries with specific release.
Contract-test staging endpoint against production handler Docker image — environment parity means same container artifact, different secrets and database URLs, not forked handler code paths maintained separately.
Record staging load-test results in release checklist — no production webhook URL change without documented staging p95 under two seconds at expected burst volume.
Include webhook delivery success rate in weekly engineering metrics review alongside API error rates and deployment frequency.
Production crypto invoicing integrates at the webhook boundary: create invoices via API, verify signed invoice.paid events, fulfill idempotently. Settlematic Collect provides REST APIs and webhooks without custodial settlement delay. Read /developers, test in demo, and compare pricing before shipping automation.
Frequently asked questions
- How fast do invoice.paid webhooks arrive?
- Typically seconds after configured confirmation threshold on chain — often faster than card settlement notification to merchant for cross-border wires.
- Can I register multiple webhook URLs?
- Platform-dependent — often one primary + backup endpoint or routing via your event bus from single endpoint.
- What if my server misses a webhook during deploy?
- Retries cover short outages. Run poll reconciliation job hourly for open invoices near expiry.
- Do webhooks include payer wallet address?
- Often yes — useful for sanctions screening and CRM. Confirm field in API docs.
- Can I create invoices in USD via API and settle USDC?
- Yes — standard pattern from invoice USD get paid crypto.
- Is webhook delivery guaranteed exactly once?
- No — at-least-once delivery is industry standard. Idempotent handlers required.
- How do webhooks relate to accounting exports?
- Webhooks trigger real-time ops; exports batch for finance month-end. Use both — accountants want CSV/JSON exports.