NewWacht Bench is live — AI-assisted development for Wacht
Rust

Webhooks

Manage webhook apps, event catalogs, endpoints, deliveries, replay, and analytics from the Rust SDK.

Webhooks are managed through one flat surface, client.webhooks() — there are no sub-groups. The model is layered: an app owns a signing secret and an event catalog, the catalog defines which events exist, endpoints subscribe to events and receive deliveries, and deliveries can be inspected, replayed, and measured. Each method is a builder; call .send().await? to execute. Most list builders take .limit() and .offset(); the request bodies for catalog and endpoint mutations live under wacht::api::webhooks.

Apps

An app is the top-level container. create_webhook_app takes a name and returns a WebhookApp carrying its app_slug (the identifier every other call uses) and signing_secret. list_webhook_apps returns Vec<WebhookApp>; get_webhook_app fetches one by slug; update_webhook_app and delete_webhook_app manage it. rotate_webhook_secret issues a new signing secret and returns the updated app — the old secret stops verifying immediately, so deployments still using it will reject deliveries until they pick up the new value.

// Returns the created WebhookApp, including app_slug and signing_secret.
// Capture the secret now; it is what you verify delivery signatures with.
let app = client
    .webhooks()
    .create_webhook_app("billing-events")
    .description("Billing lifecycle events")
    .failure_notification_emails(vec!["oncall@example.com".into()])
    .send()
    .await?;
println!("slug={} secret={}", app.app_slug, app.signing_secret);

The signing secret is how receivers confirm a delivery came from Wacht. Store it server-side and verify each incoming request against it; never expose it to a browser.

Event catalogs

A catalog is the schema layer — the set of events an app can emit, each with a name, description, JSON schema, and example payload. create_webhook_event_catalog takes a full CreateWebhookEventCatalogRequest. To add events to an existing catalog, use append_webhook_event_catalog_events, which merges new WebhookEventDefinition entries by slug and returns the updated WebhookEventCatalog. Retire an event with archive_webhook_event_in_catalog — archiving keeps the definition but stops it being offered for new subscriptions. Read catalogs with list_webhook_event_catalogs and get_webhook_event_catalog; get_webhook_catalog and get_webhook_events read the catalog resolved for a specific app.

use wacht::api::webhooks::{AppendWebhookEventCatalogEventsRequest, WebhookEventDefinition};

// Adds an event definition to an existing catalog. Returns the full catalog.
let catalog = client
    .webhooks()
    .append_webhook_event_catalog_events(
        "core-events",
        AppendWebhookEventCatalogEventsRequest {
            events: vec![WebhookEventDefinition {
                name: "user.created".into(),
                description: "Emitted when a user is created.".into(),
                group: Some("users".into()),
                schema: Some(serde_json::json!({ "type": "object" })),
                example_payload: Some(serde_json::json!({ "user_id": "user_123" })),
                is_archived: Some(false),
            }],
        },
    )
    .send()
    .await?;

Endpoints

An endpoint is a URL that receives deliveries for the events it subscribes to. create_webhook_endpoint takes the app slug and URL; chain .add_event(name, filter_rules) (or .subscriptions(...)) to subscribe, and .max_retries(), .timeout_seconds(), .headers(), and .rate_limit_config() to tune delivery. It returns the stored WebhookEndpoint. List with list_webhook_endpoints (pass .include_inactive(true) to see disabled ones), or get_webhook_endpoints_with_subscriptions to get each endpoint paired with its subscribed event names. update_webhook_endpoint is a PATCH; delete_webhook_endpoint removes it.

// Subscribes to one event with no filter. Returns the created WebhookEndpoint.
let endpoint = client
    .webhooks()
    .create_webhook_endpoint("billing-events", "https://api.example.com/hooks")
    .description("Primary receiver")
    .add_event("user.created", serde_json::json!({}))
    .max_retries(5)
    .timeout_seconds(10)
    .send()
    .await?;

Endpoints auto-disable after repeated delivery failures — WebhookEndpoint exposes failure_count, auto_disabled, and auto_disabled_at. A disabled endpoint receives nothing until you bring it back. reactivate_webhook_endpoint takes the endpoint id (not the app slug), clears the disabled state, and returns a ReactivateEndpointResponse. Before reactivating, fix whatever was failing, then confirm with test_webhook_endpoint: it sends a synthetic event to the endpoint and returns a TestWebhookEndpointResponse with success, status_code, response_time_ms, and any error — without recording a real delivery.

// Send a test event. success=false with a status_code/error tells you why.
let result = client
    .webhooks()
    .test_webhook_endpoint("billing-events", &endpoint.id, "user.created")
    .send()
    .await?;
if !result.success {
    eprintln!("test failed: {:?}", result.error);
}

// Re-enable an auto-disabled endpoint by id once the cause is resolved.
client
    .webhooks()
    .reactivate_webhook_endpoint(&endpoint.id)
    .send()
    .await?;

Triggering events

trigger_webhook_event emits an event into an app and fans it out to the matching subscribed endpoints. It takes the app slug, event name, and a JSON payload, and returns a TriggerWebhookEventResponse with delivery_ids, delivered_count, and filtered_count. filtered_count is the number of subscriptions whose filter rules excluded this payload — a non-zero value there is expected, not an error. Chain .filter_context(...) to supply extra data the endpoint filter rules evaluate against.

let dispatch = client
    .webhooks()
    .trigger_webhook_event(
        "billing-events",
        "user.created",
        serde_json::json!({ "user_id": "user_123" }),
    )
    .send()
    .await?;
println!(
    "delivered {} / filtered {}",
    dispatch.delivered_count, dispatch.filtered_count
);

Deliveries

A delivery is one attempt to send an event to one endpoint. list_webhook_deliveries returns a page of WebhookDelivery rows and accepts .endpoint_id(), .event_name(), .status(), .since(), .until(), .limit(), and .offset() filters. Each row carries status, http_status_code, attempt_number, and max_attempts. get_webhook_delivery_details returns the heavier WebhookDeliveryDetails, which adds the request payload and the endpoint's response_body and response_headers — use it to diagnose a specific failure.

// Only failed deliveries for one endpoint, most recent first.
let failures = client
    .webhooks()
    .list_webhook_deliveries("billing-events")
    .status("failed")
    .limit(50)
    .send()
    .await?;

for d in &failures.data {
    let details = client
        .webhooks()
        .get_webhook_delivery_details("billing-events", &d.delivery_id)
        .send()
        .await?;
    eprintln!("{} -> {:?}", d.delivery_id, details.response_body);
}

A delivery row with attempt_number = 0 and status = "pending" is a marker for a queued delivery, not a real attempt — exclude it when you compute attempt counts or success ratios yourself, since it would otherwise inflate the denominator.

Replay

Replay re-sends past deliveries — useful after fixing a broken endpoint or recovering from an outage. replay_webhook_deliveries runs in one of two modes: .by_ids(vec![...]) for an explicit set of delivery ids, or .by_date_range(start, end) for everything in a window (further narrowed with .status(), .event_name(), .endpoint_id()). Pass .idempotency_key(...) so a retried replay request does not enqueue the work twice. It returns a ReplayWebhookDeliveriesResponse with a task_id, because replay is asynchronous.

let replay = client
    .webhooks()
    .replay_webhook_deliveries("billing-events")
    .by_ids(vec!["del_1".into(), "del_2".into()])
    .idempotency_key("incident-4821-replay")
    .send()
    .await?;

// Replay runs in the background; poll the task to follow progress.
if let Some(task_id) = replay.task_id {
    let status = client
        .webhooks()
        .get_webhook_replay_task_status("billing-events", &task_id)
        .send()
        .await?;
    println!(
        "{} / {} replayed, {} failed",
        status.replayed_count, status.total_count, status.failed_count
    );
}

Track replays with list_webhook_replay_tasks and get_webhook_replay_task_status (which reports total_count, processed, replayed_count, and failed_count), and stop an in-flight one with cancel_webhook_replay_task.

Analytics

Three read-only views measure delivery health. get_webhook_stats returns a compact WebhookStats — total deliveries, success rate, active endpoints, and failed deliveries in the last 24 hours — suited to a dashboard tile. get_webhook_analytics returns the full AnalyticsResult: success rate, response-time percentiles (p50/p95/p99), top events, per-endpoint performance, and failure-reason breakdowns, scoped by optional .start_date(), .end_date(), and .endpoint_id(). get_webhook_timeseries returns the same metrics bucketed over time; it requires an interval (such as "hour" or "day").

let stats = client.webhooks().get_webhook_stats("billing-events").send().await?;
println!("success rate {:.1}%", stats.success_rate * 100.0);

let series = client
    .webhooks()
    .get_webhook_timeseries("billing-events", "day")
    .start_date("2026-06-01")
    .send()
    .await?;
println!("{} buckets", series.data.len());

Method group summary

  • Apps — list_webhook_apps / get_webhook_app / create_webhook_app / update_webhook_app / delete_webhook_app / rotate_webhook_secret
  • Catalogs — list_webhook_event_catalogs / get_webhook_event_catalog / create_webhook_event_catalog / update_webhook_event_catalog / append_webhook_event_catalog_events / archive_webhook_event_in_catalog / get_webhook_catalog / get_webhook_events
  • Endpoints — list_webhook_endpoints / get_webhook_endpoints_with_subscriptions / create_webhook_endpoint / update_webhook_endpoint / delete_webhook_endpoint / reactivate_webhook_endpoint / test_webhook_endpoint
  • Events and deliveries — trigger_webhook_event / list_webhook_deliveries / get_webhook_delivery_details
  • Replay — replay_webhook_deliveries / list_webhook_replay_tasks / get_webhook_replay_task_status / cancel_webhook_replay_task
  • Analytics — get_webhook_stats / get_webhook_analytics / get_webhook_timeseries

On this page