# Partner webhooks

> Receive every store's events at one signed endpoint, with the store's externalRef on each event.

Source: https://docs.nexafone.so/v1/partners/webhooks/

A partner endpoint receives the events of **all** of the partner’s stores, so the platform needs one receiver, not one
per store. Manage endpoints with the Partner API (`partner.webhooks:manage`):

```bash
curl -X POST "https://api.nexafone.so/api/v1/partner/webhook-endpoints" \
  -H "Authorization: Bearer $NEXAFON_PARTNER_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://platform.example/hooks/nexafon", "events": ["order.created", "order.paid", "message.received"]}'
```

Each event carries the store it comes from:

```json
{
  "id": "7c6b5a49-3827-4165-9483-a2b1c0d9e8f7",
  "type": "order.paid",
  "createdAt": "2026-09-21T14:13:18Z",
  "workspaceId": "bbbbbbbb-0000-0000-0000-000000000000",
  "partner": { "storeExternalRef": "shop-1" },
  "data": { "order": { "number": 1042, "status": "paid", "totalCents": 2800 } }
}
```

Deliveries are signed, retried and logged exactly like a workspace’s: see [signatures](/v1/webhooks/signatures/),
[retries](/v1/webhooks/retries/) and the [event catalog](/v1/webhooks/events/). The helpers’ `verifyWebhook` handles
both kinds.

```ts title="partner.ts"
// One endpoint receives every store's events; partner.storeExternalRef names the store.
export function onDelivery(headers: Record<string, string>, rawBody: Buffer) {
  const event = verifyWebhook(headers, rawBody, process.env.NEXAFON_PARTNER_WEBHOOK_SECRET ?? '')
  if (event.type === 'order.paid') {
    console.log('paid order in', event.partner?.storeExternalRef, event.id)
  }
  return event
}
```

Stores can still have their own endpoints too; an event goes to both.
