---
title: "Dodo Webhook"
method: POST
path: "/api/billing/webhook"
tags: ["billing"]
---

# Dodo Webhook

`POST /api/billing/webhook`

## Request

- Base URL: `https://api.parlel.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.parlel.com/api/billing/webhook`
- Auth: none declared

Apply one Dodo event. Unauthenticated, signature-verified. See the module docs.

## The status codes, and why each one

``503`` billing is not configured. Retryable, and correctly so: if a deploy lost
its secret, Dodo's retry schedule is the thing that recovers the event once it is
put back.

``401`` the signature did not verify. Never retried usefully, but a 401 is what
the dashboard should show for a misconfigured secret rather than a 400 that reads
as "our payload was malformed".

``400`` the body is not JSON. A retry cannot fix it, and it should be loud.

``200`` everything else, INCLUDING events we did not act on and duplicates. Dodo
retries any non-2xx up to 8 times over ~23 hours, so answering non-2xx to an event
that will never be handled buys eight deliveries of a thing we are going to ignore
eight times. The response body says what happened; the status code says "stop
sending this".

## Not processed asynchronously, deliberately

The vendor's guidance is to acknowledge immediately and process in the background,
which is right when processing is slow. Here it is one INSERT, one UPDATE and a
recompute, inside the 15-second timeout with room to spare — and doing it inline
means the transaction that records the event is the transaction that applies it.
Backgrounding it would trade that guarantee for latency this endpoint does not
need.

## Response `200`

Successful Response

- object

---

[API](https://skmtc.dev/parlel/apis/parlel-api.md) · [All operations](https://skmtc.dev/parlel/apis/parlel-api/llms.txt) · [OpenAPI document](https://skmtc.dev/parlel/apis/parlel-api/revisions/4d41a8f5cfc9?raw)
