Merchant webhooks
Manage a merchant's webhook endpoints from the Dashboard — create configs, inspect deliveries, and rotate signing secrets.
These endpoints back the Dashboard's webhooks screen. A webhook config is a URL Tokeflow delivers signed events to, plus the event filter and signing secret that govern it. From here you register endpoints, watch deliveries and their retries, and rotate the whsec_… secret.
The signing secret is shown only when created or rotated — store it immediately. Every delivery is signed; the receiving endpoint must verify the signature before acting. See Verifying signatures.
These endpoints power the Tokeflow Dashboard UI. They are session-authenticated (JWT bearer) — the token comes from signing in to the Dashboard, not from an sk_/pk_ API key. Requests also carry the x-merchant-id header identifying the merchant in context, and the signed-in user must be a member with access to it.
Endpoints
GET/api/v1/merchant/webhooks
Auth: Dashboard session (JWT bearer). Merchant membership required.
List webhook configs.
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
page | number | No | Page number (1-indexed). Default 1, min 1. |
limit | number | No | Number of items per page. Default 20, min 1, max 100. |
Example request
curl -G https://api.tokeflow.com/api/v1/merchant/webhooks \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9" \
--data-urlencode "page=1" \
--data-urlencode "limit=20"POST/api/v1/merchant/webhooks
Auth: Dashboard session (JWT bearer). Merchant membership required.
Create webhook config.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | The URL where webhook events will be sent. |
description | string | No | Optional description for this webhook configuration. |
event_filter | array | No | List of event types to subscribe to. If not provided, all events will be sent. |
Example request
curl -X POST https://api.tokeflow.com/api/v1/merchant/webhooks \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.example.com/webhooks/payments",
"description": "Production payment notifications",
"event_filter": [
"transaction.authorized",
"transaction.failed",
"transaction.refunded"
]
}'Example response — 201
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"merchant_id": "550e8400-e29b-41d4-a716-446655440001",
"url": "https://api.example.com/webhooks/payments",
"description": "Production payment notifications",
"event_filter": [
"transaction.authorized",
"transaction.failed",
"transaction.refunded"
],
"is_active": true,
"created_at": "2024-01-15T10:30:00.000Z",
"updated_at": "2024-01-15T10:30:00.000Z",
"last_triggered_at": "2024-01-15T10:30:00.000Z",
"secret": "whsec_abc123def456..."
}GET/api/v1/merchant/webhooks/deliveries
Auth: Dashboard session (JWT bearer). Merchant membership required.
List all webhook deliveries.
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
page | number | No | Page number (1-indexed). Default 1, min 1. |
limit | number | No | Number of items per page. Default 20, min 1, max 100. |
status | string | No | Filter by delivery status |
from | string | No | From timestamp (ISO 8601 format) - filter deliveries after this time |
to | string | No | To timestamp (ISO 8601 format) - filter deliveries before this time |
Example request
curl -G https://api.tokeflow.com/api/v1/merchant/webhooks/deliveries \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9" \
--data-urlencode "page=1" \
--data-urlencode "limit=20" \
--data-urlencode "status=failed" \
--data-urlencode "from=2024-01-01T00:00:00Z"DELETE/api/v1/merchant/webhooks/:id
Auth: Dashboard session (JWT bearer). Merchant membership required.
Delete webhook config.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Example request
curl -X DELETE https://api.tokeflow.com/api/v1/merchant/webhooks/obj_1a2b3c4d \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9"GET/api/v1/merchant/webhooks/:id
Auth: Dashboard session (JWT bearer). Merchant membership required.
Get webhook config by ID.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Example request
curl https://api.tokeflow.com/api/v1/merchant/webhooks/obj_1a2b3c4d \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9"Example response — 200
{
"id": "obj_1a2b3c4d",
"merchant_id": "mrc_8a3f12d9",
"merchant": {
"id": "obj_1a2b3c4d",
"organization_id": "org_4b21e7c3",
"organization": {
"id": "obj_1a2b3c4d",
"name": "Acme Store",
"slug": "acme-store",
"status": "active",
"business_registration_number": "string",
"mcc": "string",
"country_code": "BR",
"external_reference": "string",
"phone_number": "+5511987654321",
"address_line1": "string",
"address_line2": "string",
"city": "string",
"state": "string",
"postal_code": "string",
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z",
"memberships": [
{
"id": "obj_1a2b3c4d",
"organization_id": "org_4b21e7c3",
"user_id": "string",
"role": "owner",
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z",
"organization": {},
"user": {}
}
],
"merchants": [
{}
],
"branding": {
"id": "obj_1a2b3c4d",
"organization_id": "org_4b21e7c3",
"organization": {},
"custom_url": "https://example.com/webhooks/tokeflow",
"site_title": "string",
"favicon": "string",
"logo_light_mode": "string",
"logo_dark_mode": "string",
"collapsible_logo_light_mode": "string",
"collapsible_logo_dark_mode": "string",
"theme_primary_color": "#1c1c1c",
"theme_secondary_color": "#1c1c1c",
"show_name_with_logo": true,
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z"
}
},
"name": "Acme Store",
"slug": "acme-store",
"external_reference": "string",
"business_registration_number": "string",
"mcc": "string",
"phone_number": "+5511987654321",
"address_line1": "string",
"address_line2": "string",
"city": "string",
"state": "string",
"postal_code": "string",
"country_code": "BR",
"website": "string",
"status": "active",
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z",
"webhook_configs": [
{}
],
"memberships": [
{
"id": "obj_1a2b3c4d",
"merchant_id": "mrc_8a3f12d9",
"user_id": "string",
"role": "owner",
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z",
"merchant": {},
"user": {
"id": "obj_1a2b3c4d",
"organization_id": "org_4b21e7c3",
"email": "jane@example.com",
"name": "Acme Store",
"auth_provider": "string",
"auth_subject_id": "string",
"password_hash": "string",
"invitation_token": "string",
"invitation_expires_at": "2026-01-15T12:30:00.000Z",
"is_active": true,
"last_login_at": "2026-01-15T12:30:00.000Z",
"must_change_password": true,
"refresh_token_hash": "string",
"failed_login_attempts": 1,
"locked_until": "2026-01-15T12:30:00.000Z",
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z",
"organization": {},
"memberships": [
{}
]
}
}
],
"customers": [
{
"id": "obj_1a2b3c4d",
"merchant_id": "mrc_8a3f12d9",
"merchant": {},
"email": "jane@example.com",
"name": "Acme Store",
"phone": "+5511987654321",
"document_type": "12345678909",
"document_number": "12345678909",
"metadata": {},
"billing_address": {},
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z",
"deleted_at": "2026-01-15T12:30:00.000Z"
}
]
},
"url": "https://example.com/webhooks/tokeflow",
"secret": {},
"description": "Human-readable description",
"event_filter": [
"transaction.created"
],
"is_active": true,
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z",
"deleted_at": "2026-01-15T12:30:00.000Z"
}PATCH/api/v1/merchant/webhooks/:id
Auth: Dashboard session (JWT bearer). Merchant membership required.
Update webhook config.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
url | string | No | The URL where webhook events will be sent. |
description | string | No | Optional description for this webhook configuration. |
event_filter | array | No | List of event types to subscribe to. |
is_active | boolean | No | Whether this webhook configuration is active. |
Example request
curl -X PATCH https://api.tokeflow.com/api/v1/merchant/webhooks/obj_1a2b3c4d \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.example.com/webhooks/payments",
"description": "Production payment notifications",
"event_filter": [
"transaction.authorized",
"transaction.failed",
"transaction.refunded"
],
"is_active": true
}'Example response — 200
{
"id": "obj_1a2b3c4d",
"merchant_id": "mrc_8a3f12d9",
"merchant": {
"id": "obj_1a2b3c4d",
"organization_id": "org_4b21e7c3",
"organization": {
"id": "obj_1a2b3c4d",
"name": "Acme Store",
"slug": "acme-store",
"status": "active",
"business_registration_number": "string",
"mcc": "string",
"country_code": "BR",
"external_reference": "string",
"phone_number": "+5511987654321",
"address_line1": "string",
"address_line2": "string",
"city": "string",
"state": "string",
"postal_code": "string",
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z",
"memberships": [
{
"id": "obj_1a2b3c4d",
"organization_id": "org_4b21e7c3",
"user_id": "string",
"role": "owner",
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z",
"organization": {},
"user": {}
}
],
"merchants": [
{}
],
"branding": {
"id": "obj_1a2b3c4d",
"organization_id": "org_4b21e7c3",
"organization": {},
"custom_url": "https://example.com/webhooks/tokeflow",
"site_title": "string",
"favicon": "string",
"logo_light_mode": "string",
"logo_dark_mode": "string",
"collapsible_logo_light_mode": "string",
"collapsible_logo_dark_mode": "string",
"theme_primary_color": "#1c1c1c",
"theme_secondary_color": "#1c1c1c",
"show_name_with_logo": true,
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z"
}
},
"name": "Acme Store",
"slug": "acme-store",
"external_reference": "string",
"business_registration_number": "string",
"mcc": "string",
"phone_number": "+5511987654321",
"address_line1": "string",
"address_line2": "string",
"city": "string",
"state": "string",
"postal_code": "string",
"country_code": "BR",
"website": "string",
"status": "active",
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z",
"webhook_configs": [
{}
],
"memberships": [
{
"id": "obj_1a2b3c4d",
"merchant_id": "mrc_8a3f12d9",
"user_id": "string",
"role": "owner",
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z",
"merchant": {},
"user": {
"id": "obj_1a2b3c4d",
"organization_id": "org_4b21e7c3",
"email": "jane@example.com",
"name": "Acme Store",
"auth_provider": "string",
"auth_subject_id": "string",
"password_hash": "string",
"invitation_token": "string",
"invitation_expires_at": "2026-01-15T12:30:00.000Z",
"is_active": true,
"last_login_at": "2026-01-15T12:30:00.000Z",
"must_change_password": true,
"refresh_token_hash": "string",
"failed_login_attempts": 1,
"locked_until": "2026-01-15T12:30:00.000Z",
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z",
"organization": {},
"memberships": [
{}
]
}
}
],
"customers": [
{
"id": "obj_1a2b3c4d",
"merchant_id": "mrc_8a3f12d9",
"merchant": {},
"email": "jane@example.com",
"name": "Acme Store",
"phone": "+5511987654321",
"document_type": "12345678909",
"document_number": "12345678909",
"metadata": {},
"billing_address": {},
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z",
"deleted_at": "2026-01-15T12:30:00.000Z"
}
]
},
"url": "https://example.com/webhooks/tokeflow",
"secret": {},
"description": "Human-readable description",
"event_filter": [
"transaction.created"
],
"is_active": true,
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z",
"deleted_at": "2026-01-15T12:30:00.000Z"
}GET/api/v1/merchant/webhooks/:id/deliveries
Auth: Dashboard session (JWT bearer). Merchant membership required.
List deliveries for webhook.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
page | number | No | Page number (1-indexed). Default 1, min 1. |
limit | number | No | Number of items per page. Default 20, min 1, max 100. |
status | string | No | Filter by delivery status |
from | string | No | From timestamp (ISO 8601 format) - filter deliveries after this time |
to | string | No | To timestamp (ISO 8601 format) - filter deliveries before this time |
Example request
curl -G https://api.tokeflow.com/api/v1/merchant/webhooks/obj_1a2b3c4d/deliveries \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9" \
--data-urlencode "page=1" \
--data-urlencode "limit=20" \
--data-urlencode "status=failed" \
--data-urlencode "from=2024-01-01T00:00:00Z"POST/api/v1/merchant/webhooks/:id/deliveries/:deliveryId/resend
Auth: Dashboard session (JWT bearer). Merchant membership required.
Resend a specific webhook delivery.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
deliveryId | string | Yes | — |
Example request
curl -X POST https://api.tokeflow.com/api/v1/merchant/webhooks/obj_1a2b3c4d/deliveries/123/resend \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9"POST/api/v1/merchant/webhooks/:id/rotate-secret
Auth: Dashboard session (JWT bearer). Merchant membership required.
Rotate webhook signing secret.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Example request
curl -X POST https://api.tokeflow.com/api/v1/merchant/webhooks/obj_1a2b3c4d/rotate-secret \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9"Example response — 201
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"merchant_id": "550e8400-e29b-41d4-a716-446655440001",
"url": "https://api.example.com/webhooks/payments",
"description": "Production payment notifications",
"event_filter": [
"transaction.authorized",
"transaction.failed",
"transaction.refunded"
],
"is_active": true,
"created_at": "2024-01-15T10:30:00.000Z",
"updated_at": "2024-01-15T10:30:00.000Z",
"last_triggered_at": "2024-01-15T10:30:00.000Z",
"secret": "whsec_abc123def456..."
}POST/api/v1/merchant/webhooks/:id/test
Auth: Dashboard session (JWT bearer). Merchant membership required.
Send a test event to a webhook endpoint.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Example request
curl -X POST https://api.tokeflow.com/api/v1/merchant/webhooks/obj_1a2b3c4d/test \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9"Errors
Errors use the standard envelope. The most common cases here:
| HTTP | Meaning |
|---|---|
400 | Invalid parameters or a state that does not allow this operation. |
401 | Missing, expired, or invalid Dashboard session token. |
403 | Authenticated, but the signed-in user lacks access to this entity. |
404 | The record does not exist or is not visible to this entity. |