Webhooks: setup, events and troubleshooting
Webhooks let Arca notify your server in real time when something happens — a payment succeeds, an invoice fails, a subscription is canceled. This guide covers registering a URL, the events you can receive, and how to handle delivery reliably.
Registering a webhook URL
Send a request to the webhook-URLs endpoint with your API token and tenant header:
| Field | Value |
|---|---|
| Endpoint | POST /webhook-urls |
| Headers | Authorization: Bearer <token> and tenant: <your tenant id> |
| Body | { "url": "https://your-server.com/webhook", "isActive": true } |
- The URL must be a public HTTPS endpoint, 200 characters max, with no spaces.
- You can register multiple URLs; every active URL receives all events.
- Only URLs with
isActive: truereceive deliveries. - Manage them with
GET /webhook-urls,PATCH /webhook-urls/:id, andDELETE /webhook-urls/:id.
The event payload
Each event is delivered as a JSON POST:
| Field | Description |
|---|---|
eventName | Which event fired, e.g. subscriptions.payment_failed |
object | The full record the event is about (transaction, invoice, subscription, etc.) |
tenantId / googleTenantId | Your account identifiers — use these to verify the event (see below) |
Respond with HTTP 200 (any 2xx) to acknowledge receipt.
tenantId (or googleTenantId) matches your account and reject anything that doesn't. Always use an HTTPS endpoint, and a hard-to-guess URL path adds protection.
Event types
The most useful events, by area (this is a subset — the full set also covers products, prices, coupons, credit notes, and partners):
| Area | Events |
|---|---|
| Payments & transactions | transactions.succeeded, transactions.failed, transactions.pending, transactions.refunded, transactions.partial_refund, transactions.voided, transactions.disputed |
| Invoices | invoices.paid, invoices.payment_succeeded, invoices.payment_failed, invoices.finalized, invoices.voided, invoices.refunded, invoices.created_from_subscription |
| Subscriptions | subscriptions.created, subscriptions.updated, subscriptions.canceled, subscriptions.paused, subscriptions.payment_succeeded, subscriptions.payment_failed, subscriptions.payment_collection_paused, subscriptions.payment_collection_resumed |
| Orders | orders.paid, orders.canceled, orders.voided, orders.refunded, orders.payment_failed |
| Payment links | payment_links.created, payment_links.activated, payment_links.deactivated, payment_links.order_created |
| Customers | customers.created, customers.updated, customer_cards.created, customer_cards.updated |
Delivery and retries
- Each event is delivered to every active URL; respond 200 within about 30 seconds.
- If your endpoint returns a 5xx or times out, Arca retries up to 3 times with increasing delays (roughly 5s, 10s, 20s).
- A 4xx response is treated as permanent — it is not retried, and the event is dropped. Only return 4xx if you genuinely want to reject the event.
- Delivery is at-least-once: the same event can arrive more than once, so make your handler idempotent (de-duplicate on the object's id plus
eventName). Acknowledge quickly and do heavy work asynchronously.
Common problems
Webhook never arrives
-
Cause
The URL is inactive, was registered under the wrong tenant, or your endpoint isn't reachable from the public internet.
SolutionCall GET /webhook-urls to confirm the URL exists, is active, and is under the right tenant; make sure the endpoint is a public HTTPS address.
Events still go to an old URL after it changed
-
Cause
There's no automatic re-sync — a registered URL stays until you change it.
SolutionPATCH /webhook-urls/:id with the new URL, or delete the old one and register the new one. Re-registering the same URL returns a 409 (already exists).
Receiving duplicate deliveries
-
Cause
Delivery is at-least-once, so retries or a slow-but-successful response can cause the same event to arrive twice.
SolutionMake the handler idempotent — de-duplicate on the object id plus eventName — and acknowledge quickly (well under 30 seconds).
Your server rejects events (tenant mismatch)
-
Cause
Your verification expects a different tenant id, or you're mixing sandbox and production.
SolutionConfirm the tenant id your handler checks exactly matches the account that registered the URL; accept both tenantId and googleTenantId.
Events stop after your endpoint returned an error
-
Cause
A 4xx response is permanent (no retry); 5xx and timeouts retry up to 3 times, then stop.
SolutionReturn 2xx as soon as you accept an event. Return 5xx only when you actually want a retry; never return 4xx for a temporary problem.
Error reference (registration)
| Code | Meaning | What to do |
|---|---|---|
| 401 Unauthorized | Missing or invalid API token | Send a valid Authorization: Bearer token |
| 403 Forbidden | No permission, or tenant check failed | Use a key with webhook permission and the correct tenant |
| 400 Bad Request | Missing/invalid tenant header or invalid URL | Include the tenant header; use a valid HTTPS URL ≤200 chars |
| 409 Conflict | That URL is already registered | Update the existing one instead of re-adding |
| 404 Not Found | The webhook URL id doesn't exist | Check the id from GET /webhook-urls |
Still having trouble?
Contact Arca support and include:
- The webhook URL and its id
- The event(s) you expected and roughly when
- The HTTP status your endpoint returned, if known