Webhooks
Design a safe Cobru webhook handler and understand the current callback limitations.
Cobru sends a POST request to the callback URL you include when creating a payment. This is currently the main way to react to payment updates from your own backend.
Start here
- Treat the callback as a notification, not a source of truth.
- Persist the raw event immediately.
- Return HTTP
200fast. - Deduplicate by Cobru identifiers or your own payment reference.
- Reconcile before unlocking irreversible value.
Current trust model
Cobru webhooks are not signed today. There is no public HMAC header or JWT verification contract. Treat the webhook as a notification, not as definitive proof.
Typical payload
{
"orderId": "123",
"state": 3,
"payment_method": "Bre-B",
"amount": "50000.00",
"url": "3gofdf6f"
}Payment states
| State | Meaning | Notes |
|---|---|---|
0 | Created / pending | payment object exists |
1 | Processing | user has started or Cobru is awaiting final confirmation |
2 | Unpaid / rejected | outcome depends on payment method |
3 | Paid / approved | the state most teams treat as successful settlement |
4 | Refunded | refund applied |
5 | Expired | appears in older Cobru materials; confirm before depending on it |
What a safe webhook integration must do
Accept the callback, parse JSON, and persist the raw payload immediately.
Return HTTP 200 quickly so Cobru does not depend on your downstream processing time.
Deduplicate by orderId, url, or your own internal payment reference.
Reconcile the payment before shipping goods, unlocking value, or marking irreversible business success.
Recommended handler pattern
- Parse the payload.
- Persist the event immediately.
- Return HTTP
200as fast as possible. - Process reconciliation and side effects asynchronously.
- Protect downstream actions with idempotency on
orderIdor your own reference.
export async function POST(request: Request) {
const payload = await request.json();
await saveWebhookEvent(payload);
return new Response('ok', { status: 200 });
}Recommended persistence model
Store at least:
- raw payload
- received timestamp
- your internal order ID
- Cobru
urlslug - Cobru
orderIdwhen present - processing result
- replay count or deduplication marker
Hardening options available today
| Risk | Workaround today |
|---|---|
| Anyone can hit your callback URL | include your own secret in the callback query string |
| Duplicate deliveries | store orderId and skip already-processed events |
| Callback spoofing | re-check payment details with Cobru before shipping goods or unlocking value |
| Operational blind spots | log every callback and expose an internal replay tool |
What not to do
- do not mark an order as permanently successful before reconciliation
- do not perform heavy downstream work before returning
200 - do not trust source IP alone as your only security control
- do not assume callbacks are delivered exactly once
Local testing
ngrok http 3000Then use the public HTTPS URL as your Cobru callback value in sandbox.
Recommended production controls
| Control | Why it matters |
|---|---|
| callback URL secret or unguessable token | reduces trivial spoofing risk |
| persistent event log | lets you debug support incidents and replay failures |
| idempotent processor | protects against duplicates |
| reconciliation step | prevents false positives when callbacks are spoofed or incomplete |
| alerting on failed processing | reduces silent payment-handling failures |
Read next
/docs/testing/docs/production-readiness/docs/troubleshooting/docs/api/cobrus/create