Webhook Endpoints API
Create, list, and manage webhook endpoint configurations programmatically. Requires the release_webhooks feature flag.
Overview#
Webhook endpoints define where Zazu sends outbound event notifications. For payload formats, signing, retries, and event semantics, see the Webhooks guide.
Required scopes#
| Endpoint | Scope |
|---|---|
| List / Get webhook endpoints | webhook_endpoints:read |
| Create / Update / Delete webhook endpoints | webhook_endpoints:write |
| Enable / Disable / Test / Regenerate secret | webhook_endpoints:write |
Webhook endpoint object#
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier (UUIDv7) |
url | string | HTTPS destination URL |
description | string | Optional internal label |
events | array | Event types this endpoint subscribes to |
status | string | active or disabled |
disabled_at | string | ISO 8601 timestamp when disabled, or null |
last_succeeded_at | string | ISO 8601 timestamp of the last successful delivery, or null |
created_at | string | ISO 8601 timestamp |
updated_at | string | ISO 8601 timestamp |
signing_secret is returned only when creating an endpoint or regenerating the secret. Store it immediately — it is never returned by list, get, update, enable, disable, or test responses.List webhook endpoints#
GET /api/webhook_endpoints
Supports cursor pagination (cursor, limit 1–100, default 25).
curl "https://zazu.africa/api/webhook_endpoints" \
-H "Authorization: Bearer sk_live_..."zazu webhook-endpoints listimport { Zazu } from "@getzazu/sdk";
const zazu = new Zazu({ apiKey: process.env.ZAZU_API_KEY });
await zazu.webhookEndpoints.list();require "zazu"
zazu = Zazu.new(api_key: ENV["ZAZU_API_KEY"])
zazu.webhook_endpoints.listfrom zazu_sdk import Zazu
client = Zazu(api_key="sk_live_...")
client.webhook_endpoints.list()import zazu "github.com/getzazu/zazu-go"
client, _ := zazu.New(zazu.WithAPIKey(os.Getenv("ZAZU_API_KEY")))
page, err := client.WebhookEndpoints.List(ctx, zazu.ListParams{})let client = zazu_sdk::Client::builder()
.api_key(std::env::var("ZAZU_API_KEY")?)
.build()?;
let page = client.webhook_endpoints().list(Default::default())?;{:ok, client} = Zazu.new(api_key: System.fetch_env!("ZAZU_API_KEY"))
{:ok, page} = Zazu.WebhookEndpoints.list(client)require "zazu"
client = Zazu::Client.new # reads ZAZU_API_KEY
page = client.webhook_endpoints.listuse Zazu\Client;
$client = new Client(apiKey: getenv('ZAZU_API_KEY'));
$page = $client->webhookEndpoints->list();{
"data": [
{
"id": "019dde72-02ed-7a23-aaca-98c40c48e6b6",
"url": "https://example.com/webhooks/zazu",
"description": "Production",
"events": ["payment_link.paid", "transfer.executed"],
"status": "active",
"disabled_at": null,
"last_succeeded_at": "2026-04-30T11:14:00Z",
"created_at": "2026-04-30T10:30:00Z",
"updated_at": "2026-04-30T10:30:00Z"
}
],
"has_more": false,
"next_cursor": null
}Create webhook endpoint#
POST /api/webhook_endpoints
Required: url (must be HTTPS; private, loopback, and link-local IPs are rejected) and events (must contain supported subscribable events). A signing secret is generated automatically and returned once.
curl -X POST "https://zazu.africa/api/webhook_endpoints" \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/zazu",
"description": "Production",
"events": ["payment_link.paid", "transfer.executed"]
}'zazu webhook-endpoints create --url https://example.com/webhooks/zazu --event payment_link.paid --event transfer.executedimport { Zazu } from "@getzazu/sdk";
const zazu = new Zazu({ apiKey: process.env.ZAZU_API_KEY });
await zazu.webhookEndpoints.create({
url: "https://example.com/webhooks/zazu",
events: ["payment_link.paid", "transfer.executed"],
});require "zazu"
zazu = Zazu.new(api_key: ENV["ZAZU_API_KEY"])
zazu.webhook_endpoints.create(
url: "https://example.com/webhooks/zazu",
events: ["payment_link.paid", "transfer.executed"],
)from zazu_sdk import Zazu
client = Zazu(api_key="sk_live_...")
client.webhook_endpoints.create(
url="https://example.com/webhooks/zazu",
events=["payment_link.paid", "transfer.executed"],
)import zazu "github.com/getzazu/zazu-go"
client, _ := zazu.New(zazu.WithAPIKey(os.Getenv("ZAZU_API_KEY")))
endpoint, err := client.WebhookEndpoints.Create(ctx, zazu.Attributes{
"url": "https://example.com/webhooks/zazu",
"events": []string{"payment_link.paid", "transfer.executed"},
})let client = zazu_sdk::Client::builder()
.api_key(std::env::var("ZAZU_API_KEY")?)
.build()?;
let endpoint = client.webhook_endpoints().create(&serde_json::json!({
"url": "https://example.com/webhooks/zazu",
"events": ["payment_link.paid", "transfer.executed"],
}))?;{:ok, client} = Zazu.new(api_key: System.fetch_env!("ZAZU_API_KEY"))
{:ok, endpoint} =
Zazu.WebhookEndpoints.create(client, %{
"url" => "https://example.com/webhooks/zazu",
"events" => ["payment_link.paid", "transfer.executed"]
})require "zazu"
client = Zazu::Client.new # reads ZAZU_API_KEY
endpoint = client.webhook_endpoints.create(
url: "https://example.com/webhooks/zazu",
events: ["payment_link.paid", "transfer.executed"]
)use Zazu\Client;
$client = new Client(apiKey: getenv('ZAZU_API_KEY'));
$endpoint = $client->webhookEndpoints->create([
'url' => 'https://example.com/webhooks/zazu',
'events' => ['payment_link.paid', 'transfer.executed'],
]);{
"id": "019dde72-02ed-7a23-aaca-98c40c48e6b6",
"url": "https://example.com/webhooks/zazu",
"description": "Production",
"events": ["payment_link.paid", "transfer.executed"],
"status": "active",
"disabled_at": null,
"last_succeeded_at": null,
"created_at": "2026-04-30T10:30:00Z",
"updated_at": "2026-04-30T10:30:00Z",
"signing_secret": "whsec_..."
}signing_secret (a random whsec_ value) is shown only once in this response. Use it to verify webhook signatures — see the Webhooks guide.Get webhook endpoint#
GET /api/webhook_endpoints/:id
Returns the webhook endpoint object without signing_secret.
curl "https://zazu.africa/api/webhook_endpoints/019dde72-02ed-7a23-aaca-98c40c48e6b6" \
-H "Authorization: Bearer sk_live_..."zazu webhook-endpoints get 019dde72-02ed-7a23-aaca-98c40c48e6b6import { Zazu } from "@getzazu/sdk";
const zazu = new Zazu({ apiKey: process.env.ZAZU_API_KEY });
await zazu.webhookEndpoints.retrieve("019dde72-02ed-7a23-aaca-98c40c48e6b6");require "zazu"
zazu = Zazu.new(api_key: ENV["ZAZU_API_KEY"])
zazu.webhook_endpoints.get("019dde72-02ed-7a23-aaca-98c40c48e6b6")from zazu_sdk import Zazu
client = Zazu(api_key="sk_live_...")
client.webhook_endpoints.retrieve("019dde72-02ed-7a23-aaca-98c40c48e6b6")import zazu "github.com/getzazu/zazu-go"
client, _ := zazu.New(zazu.WithAPIKey(os.Getenv("ZAZU_API_KEY")))
endpoint, err := client.WebhookEndpoints.Get(ctx, "019dde72-02ed-7a23-aaca-98c40c48e6b6")let client = zazu_sdk::Client::builder()
.api_key(std::env::var("ZAZU_API_KEY")?)
.build()?;
let endpoint = client.webhook_endpoints().get("019dde72-02ed-7a23-aaca-98c40c48e6b6")?;{:ok, client} = Zazu.new(api_key: System.fetch_env!("ZAZU_API_KEY"))
{:ok, endpoint} = Zazu.WebhookEndpoints.get(client, "019dde72-02ed-7a23-aaca-98c40c48e6b6")require "zazu"
client = Zazu::Client.new # reads ZAZU_API_KEY
endpoint = client.webhook_endpoints.get("019dde72-02ed-7a23-aaca-98c40c48e6b6")use Zazu\Client;
$client = new Client(apiKey: getenv('ZAZU_API_KEY'));
$endpoint = $client->webhookEndpoints->get('019dde72-02ed-7a23-aaca-98c40c48e6b6');Update webhook endpoint#
PATCH /api/webhook_endpoints/:id
Update the destination url, description, or subscribed events. Returns the updated object without signing_secret.
curl -X PATCH "https://zazu.africa/api/webhook_endpoints/019dde72-02ed-7a23-aaca-98c40c48e6b6" \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/zazu-v2",
"description": "Production v2",
"events": ["payment_link.paid"]
}'zazu webhook-endpoints update 019dde72-02ed-7a23-aaca-98c40c48e6b6 --url https://example.com/webhooks/zazu-v2 --event payment_link.paidimport { Zazu } from "@getzazu/sdk";
const zazu = new Zazu({ apiKey: process.env.ZAZU_API_KEY });
await zazu.webhookEndpoints.update("019dde72-02ed-7a23-aaca-98c40c48e6b6", {
url: "https://example.com/webhooks/zazu-v2",
events: ["payment_link.paid"],
});require "zazu"
zazu = Zazu.new(api_key: ENV["ZAZU_API_KEY"])
zazu.webhook_endpoints.update("019dde72-02ed-7a23-aaca-98c40c48e6b6",
url: "https://example.com/webhooks/zazu-v2",
events: ["payment_link.paid"],
)from zazu_sdk import Zazu
client = Zazu(api_key="sk_live_...")
client.webhook_endpoints.update("019dde72-02ed-7a23-aaca-98c40c48e6b6",
url="https://example.com/webhooks/zazu-v2",
events=["payment_link.paid"],
)import zazu "github.com/getzazu/zazu-go"
client, _ := zazu.New(zazu.WithAPIKey(os.Getenv("ZAZU_API_KEY")))
endpoint, err := client.WebhookEndpoints.Update(ctx, "019dde72-02ed-7a23-aaca-98c40c48e6b6", zazu.Attributes{
"url": "https://example.com/webhooks/zazu-v2",
"events": []string{"payment_link.paid"},
})let client = zazu_sdk::Client::builder()
.api_key(std::env::var("ZAZU_API_KEY")?)
.build()?;
let endpoint = client.webhook_endpoints().update("019dde72-02ed-7a23-aaca-98c40c48e6b6", &serde_json::json!({
"url": "https://example.com/webhooks/zazu-v2",
"events": ["payment_link.paid"],
}))?;{:ok, client} = Zazu.new(api_key: System.fetch_env!("ZAZU_API_KEY"))
{:ok, endpoint} =
Zazu.WebhookEndpoints.update(client, "019dde72-02ed-7a23-aaca-98c40c48e6b6", %{
"url" => "https://example.com/webhooks/zazu-v2",
"events" => ["payment_link.paid"]
})require "zazu"
client = Zazu::Client.new # reads ZAZU_API_KEY
endpoint = client.webhook_endpoints.update("019dde72-02ed-7a23-aaca-98c40c48e6b6",
url: "https://example.com/webhooks/zazu-v2",
events: ["payment_link.paid"]
)use Zazu\Client;
$client = new Client(apiKey: getenv('ZAZU_API_KEY'));
$endpoint = $client->webhookEndpoints->update('019dde72-02ed-7a23-aaca-98c40c48e6b6', [
'url' => 'https://example.com/webhooks/zazu-v2',
'events' => ['payment_link.paid'],
]);Disable webhook endpoint#
POST /api/webhook_endpoints/:id/disable
Disables delivery to the endpoint (returns the object with status: "disabled"). Existing delivery logs remain available in the dashboard.
curl -X POST "https://zazu.africa/api/webhook_endpoints/019dde72-02ed-7a23-aaca-98c40c48e6b6/disable" \
-H "Authorization: Bearer sk_live_..."zazu webhook-endpoints disable 019dde72-02ed-7a23-aaca-98c40c48e6b6import { Zazu } from "@getzazu/sdk";
const zazu = new Zazu({ apiKey: process.env.ZAZU_API_KEY });
await zazu.webhookEndpoints.disable("019dde72-02ed-7a23-aaca-98c40c48e6b6");require "zazu"
zazu = Zazu.new(api_key: ENV["ZAZU_API_KEY"])
zazu.webhook_endpoints.disable("019dde72-02ed-7a23-aaca-98c40c48e6b6")from zazu_sdk import Zazu
client = Zazu(api_key="sk_live_...")
client.webhook_endpoints.disable("019dde72-02ed-7a23-aaca-98c40c48e6b6")import zazu "github.com/getzazu/zazu-go"
client, _ := zazu.New(zazu.WithAPIKey(os.Getenv("ZAZU_API_KEY")))
endpoint, err := client.WebhookEndpoints.Disable(ctx, "019dde72-02ed-7a23-aaca-98c40c48e6b6")let client = zazu_sdk::Client::builder()
.api_key(std::env::var("ZAZU_API_KEY")?)
.build()?;
let endpoint = client.webhook_endpoints().disable("019dde72-02ed-7a23-aaca-98c40c48e6b6")?;{:ok, client} = Zazu.new(api_key: System.fetch_env!("ZAZU_API_KEY"))
{:ok, endpoint} = Zazu.WebhookEndpoints.disable(client, "019dde72-02ed-7a23-aaca-98c40c48e6b6")require "zazu"
client = Zazu::Client.new # reads ZAZU_API_KEY
endpoint = client.webhook_endpoints.disable("019dde72-02ed-7a23-aaca-98c40c48e6b6")use Zazu\Client;
$client = new Client(apiKey: getenv('ZAZU_API_KEY'));
$endpoint = $client->webhookEndpoints->disable('019dde72-02ed-7a23-aaca-98c40c48e6b6');Enable webhook endpoint#
POST /api/webhook_endpoints/:id/enable
Re-enables delivery (returns the object with status: "active") and clears any in-progress failure window.
curl -X POST "https://zazu.africa/api/webhook_endpoints/019dde72-02ed-7a23-aaca-98c40c48e6b6/enable" \
-H "Authorization: Bearer sk_live_..."zazu webhook-endpoints enable 019dde72-02ed-7a23-aaca-98c40c48e6b6import { Zazu } from "@getzazu/sdk";
const zazu = new Zazu({ apiKey: process.env.ZAZU_API_KEY });
await zazu.webhookEndpoints.enable("019dde72-02ed-7a23-aaca-98c40c48e6b6");require "zazu"
zazu = Zazu.new(api_key: ENV["ZAZU_API_KEY"])
zazu.webhook_endpoints.enable("019dde72-02ed-7a23-aaca-98c40c48e6b6")from zazu_sdk import Zazu
client = Zazu(api_key="sk_live_...")
client.webhook_endpoints.enable("019dde72-02ed-7a23-aaca-98c40c48e6b6")import zazu "github.com/getzazu/zazu-go"
client, _ := zazu.New(zazu.WithAPIKey(os.Getenv("ZAZU_API_KEY")))
endpoint, err := client.WebhookEndpoints.Enable(ctx, "019dde72-02ed-7a23-aaca-98c40c48e6b6")let client = zazu_sdk::Client::builder()
.api_key(std::env::var("ZAZU_API_KEY")?)
.build()?;
let endpoint = client.webhook_endpoints().enable("019dde72-02ed-7a23-aaca-98c40c48e6b6")?;{:ok, client} = Zazu.new(api_key: System.fetch_env!("ZAZU_API_KEY"))
{:ok, endpoint} = Zazu.WebhookEndpoints.enable(client, "019dde72-02ed-7a23-aaca-98c40c48e6b6")require "zazu"
client = Zazu::Client.new # reads ZAZU_API_KEY
endpoint = client.webhook_endpoints.enable("019dde72-02ed-7a23-aaca-98c40c48e6b6")use Zazu\Client;
$client = new Client(apiKey: getenv('ZAZU_API_KEY'));
$endpoint = $client->webhookEndpoints->enable('019dde72-02ed-7a23-aaca-98c40c48e6b6');Send test event#
POST /api/webhook_endpoints/:id/test
Queues a test.ping delivery to the endpoint. The endpoint must be active. Responds 202 Accepted.
curl -X POST "https://zazu.africa/api/webhook_endpoints/019dde72-02ed-7a23-aaca-98c40c48e6b6/test" \
-H "Authorization: Bearer sk_live_..."zazu webhook-endpoints test 019dde72-02ed-7a23-aaca-98c40c48e6b6import { Zazu } from "@getzazu/sdk";
const zazu = new Zazu({ apiKey: process.env.ZAZU_API_KEY });
await zazu.webhookEndpoints.test("019dde72-02ed-7a23-aaca-98c40c48e6b6");require "zazu"
zazu = Zazu.new(api_key: ENV["ZAZU_API_KEY"])
zazu.webhook_endpoints.test_endpoint("019dde72-02ed-7a23-aaca-98c40c48e6b6")from zazu_sdk import Zazu
client = Zazu(api_key="sk_live_...")
client.webhook_endpoints.test("019dde72-02ed-7a23-aaca-98c40c48e6b6")import zazu "github.com/getzazu/zazu-go"
client, _ := zazu.New(zazu.WithAPIKey(os.Getenv("ZAZU_API_KEY")))
result, err := client.WebhookEndpoints.Test(ctx, "019dde72-02ed-7a23-aaca-98c40c48e6b6")let client = zazu_sdk::Client::builder()
.api_key(std::env::var("ZAZU_API_KEY")?)
.build()?;
let result = client.webhook_endpoints().test("019dde72-02ed-7a23-aaca-98c40c48e6b6")?;{:ok, client} = Zazu.new(api_key: System.fetch_env!("ZAZU_API_KEY"))
{:ok, result} = Zazu.WebhookEndpoints.test(client, "019dde72-02ed-7a23-aaca-98c40c48e6b6")require "zazu"
client = Zazu::Client.new # reads ZAZU_API_KEY
result = client.webhook_endpoints.test("019dde72-02ed-7a23-aaca-98c40c48e6b6")use Zazu\Client;
$client = new Client(apiKey: getenv('ZAZU_API_KEY'));
$result = $client->webhookEndpoints->test('019dde72-02ed-7a23-aaca-98c40c48e6b6');{
"delivery_id": "019dde72-0538-7795-a0b2-48c7097e9827",
"status": "queued"
}Regenerate signing secret#
POST /api/webhook_endpoints/:id/regenerate_secret
Rotates the endpoint signing secret and returns the new value (the object includes signing_secret). Requests sent after rotation are signed with the new secret.
curl -X POST "https://zazu.africa/api/webhook_endpoints/019dde72-02ed-7a23-aaca-98c40c48e6b6/regenerate_secret" \
-H "Authorization: Bearer sk_live_..."zazu webhook-endpoints regenerate-secret 019dde72-02ed-7a23-aaca-98c40c48e6b6import { Zazu } from "@getzazu/sdk";
const zazu = new Zazu({ apiKey: process.env.ZAZU_API_KEY });
await zazu.webhookEndpoints.regenerateSecret("019dde72-02ed-7a23-aaca-98c40c48e6b6");require "zazu"
zazu = Zazu.new(api_key: ENV["ZAZU_API_KEY"])
zazu.webhook_endpoints.regenerate_secret("019dde72-02ed-7a23-aaca-98c40c48e6b6")from zazu_sdk import Zazu
client = Zazu(api_key="sk_live_...")
client.webhook_endpoints.regenerate_secret("019dde72-02ed-7a23-aaca-98c40c48e6b6")import zazu "github.com/getzazu/zazu-go"
client, _ := zazu.New(zazu.WithAPIKey(os.Getenv("ZAZU_API_KEY")))
endpoint, err := client.WebhookEndpoints.RegenerateSecret(ctx, "019dde72-02ed-7a23-aaca-98c40c48e6b6")let client = zazu_sdk::Client::builder()
.api_key(std::env::var("ZAZU_API_KEY")?)
.build()?;
let endpoint = client.webhook_endpoints().regenerate_secret("019dde72-02ed-7a23-aaca-98c40c48e6b6")?;{:ok, client} = Zazu.new(api_key: System.fetch_env!("ZAZU_API_KEY"))
{:ok, endpoint} = Zazu.WebhookEndpoints.regenerate_secret(client, "019dde72-02ed-7a23-aaca-98c40c48e6b6")require "zazu"
client = Zazu::Client.new # reads ZAZU_API_KEY
endpoint = client.webhook_endpoints.regenerate_secret("019dde72-02ed-7a23-aaca-98c40c48e6b6")use Zazu\Client;
$client = new Client(apiKey: getenv('ZAZU_API_KEY'));
$endpoint = $client->webhookEndpoints->regenerateSecret('019dde72-02ed-7a23-aaca-98c40c48e6b6');Delete webhook endpoint#
DELETE /api/webhook_endpoints/:id
Deletes the endpoint and its delivery records. Responds 204 No Content.
curl -X DELETE "https://zazu.africa/api/webhook_endpoints/019dde72-02ed-7a23-aaca-98c40c48e6b6" \
-H "Authorization: Bearer sk_live_..."zazu webhook-endpoints delete 019dde72-02ed-7a23-aaca-98c40c48e6b6import { Zazu } from "@getzazu/sdk";
const zazu = new Zazu({ apiKey: process.env.ZAZU_API_KEY });
await zazu.webhookEndpoints.del("019dde72-02ed-7a23-aaca-98c40c48e6b6");require "zazu"
zazu = Zazu.new(api_key: ENV["ZAZU_API_KEY"])
zazu.webhook_endpoints.delete("019dde72-02ed-7a23-aaca-98c40c48e6b6")from zazu_sdk import Zazu
client = Zazu(api_key="sk_live_...")
client.webhook_endpoints.delete("019dde72-02ed-7a23-aaca-98c40c48e6b6")import zazu "github.com/getzazu/zazu-go"
client, _ := zazu.New(zazu.WithAPIKey(os.Getenv("ZAZU_API_KEY")))
_, err := client.WebhookEndpoints.Delete(ctx, "019dde72-02ed-7a23-aaca-98c40c48e6b6")let client = zazu_sdk::Client::builder()
.api_key(std::env::var("ZAZU_API_KEY")?)
.build()?;
client.webhook_endpoints().delete("019dde72-02ed-7a23-aaca-98c40c48e6b6")?;{:ok, client} = Zazu.new(api_key: System.fetch_env!("ZAZU_API_KEY"))
{:ok, _} = Zazu.WebhookEndpoints.delete(client, "019dde72-02ed-7a23-aaca-98c40c48e6b6")require "zazu"
client = Zazu::Client.new # reads ZAZU_API_KEY
client.webhook_endpoints.delete("019dde72-02ed-7a23-aaca-98c40c48e6b6")use Zazu\Client;
$client = new Client(apiKey: getenv('ZAZU_API_KEY'));
$client->webhookEndpoints->delete('019dde72-02ed-7a23-aaca-98c40c48e6b6');Supported events#
| Event | Trigger |
|---|---|
checkout_session.completed | Fires when a checkout session receives a confirmed payment |
checkout_session.settled | Fires when the provider settles a checkout session payment |
payment_link.paid | Fires when a payment link receives a payment |
payment_link.payment_settled | Fires when the provider settles a payment link payment |
transfer.executed | Fires when an outgoing transfer is executed |
The test.ping event can be sent with the test endpoint, but it is not a subscribable event.
Error responses#
| Scenario | Status | Type | Param |
|---|---|---|---|
| Missing or invalid API key | 401 | authentication_error | — |
| Missing required scope | 403 | insufficient_scope | — |
| Webhooks not enabled | 403 | forbidden_error | — |
| Endpoint not found | 404 | not_found_error | — |
| Non-HTTPS URL | 422 | validation_error | url |
| Private IP URL | 422 | validation_error | url |
| Unknown event name | 422 | validation_error | events |
| Test disabled endpoint | 422 | state_error | — |