A customer is who you send to. An endpoint is where their webhooks go. Both are managed through the REST API with a manage-scoped token, in the dashboard under Customers, or by the customers themselves in the portal. All three surfaces operate on the same objects, so an endpoint a customer adds in the portal is immediately visible to your API and your team.
Customers#
Customers are identified by an externalId you choose: your database's account id, an org slug, whatever you already use.
curl -X POST https://api.repost.sh/v1/customers/acme \
-H "Authorization: Bearer $REPOST_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Acme Corp", "metadata": {"plan": "enterprise"}}'Creation is an idempotent upsert. It returns 201 when the customer is new and 200 with the existing customer otherwise, so you can call it from a signup flow without checking first. A customer carries an optional display name and free-form metadata.
| Operation | Route |
|---|---|
| Upsert | POST /v1/customers/{externalId} |
| Get | GET /v1/customers/{externalId} |
| List | GET /v1/customers?limit=20&cursor=... |
| Update | PATCH /v1/customers/{externalId} |
| Delete | DELETE /v1/customers/{externalId} (also removes its endpoints) |
You don't have to pre-create customers. Creating an endpoint for an unknown externalId creates the customer at the same time.
Endpoints#
curl -X POST https://api.repost.sh/v1/customers/acme/endpoints \
-H "Authorization: Bearer $REPOST_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://acme.example.com/webhooks",
"description": "Production receiver",
"subscriptions": ["order.created", "order.refunded"],
"headers": {"x-acme-source": "repost"}
}'The 201 response contains the endpoint and its whsec_... signing secret. The secret is returned only on creation; store it or show it to your customer right away. It remains available later through the secret routes and the portal.
| Property | Meaning |
|---|---|
url | HTTPS destination. Validated for safety, see below. |
subscriptions | Event-type names to deliver, or ["*"] (the default) for all of them. |
description | Optional label, up to 512 characters. |
headers | Custom headers added to every delivery. The webhook-*, content-type, and user-agent headers are reserved and cannot be overridden. |
status | ENABLED, PAUSED, or DISABLED. |
The other routes follow the same pattern: GET /v1/customers/{externalId}/endpoints lists a customer's endpoints, and GET, PATCH, and DELETE /v1/endpoints/{id} operate on one. Updates apply to pending retries as well, so correcting a mistyped URL also redirects the backlog.
Pause and resume#
curl -X POST https://api.repost.sh/v1/endpoints/{id}/pause -H "Authorization: Bearer $REPOST_TOKEN"
curl -X POST https://api.repost.sh/v1/endpoints/{id}/resume -H "Authorization: Bearer $REPOST_TOKEN"Use pause for maintenance windows. Deliveries for a paused endpoint are still created but held, and resume drains the backlog in order. Nothing is lost, and nothing burns retries against a receiver you know is down. Deleting an endpoint behaves differently: its in-flight deliveries fail terminally as endpoint_gone.
URL safety#
Endpoint URLs are validated when set and checked again when a connection is opened. localhost, .local names, and private or reserved IP ranges are rejected with 422, and DNS answers are re-checked at dial time, so an endpoint cannot be pointed at your internal network. To receive webhooks on a development machine, use a public target such as a Repost bucket forwarded to localhost with the CLI.