Apply a lifecycle action to every webhook subscription matching a filter
Apply PAUSE, RESUME, or DELETE to every webhook subscription matching the provided filter in a single synchronous request. Mirrors /v1/admin/tenants/bulk-action in body shape, safety semantics (expected_count gate, idempotency_key, 500-row cap), and response envelope. Supports admin-dashboard workflows such as "pause every subscription for tenant acme" or "delete every subscription with url containing staging".
FILTER SEMANTICS: - Filter block mirrors the query params of
listWebhookSubscriptions (tenant_id, status, event_type,
search). AND combination, ILIKE match on search. Operator
previews the affected set via GET /v1/admin/webhooks with the
same filter before invoking bulk-action.
ACTION SEMANTICS: - PAUSE: status ACTIVE → PAUSED. Idempotent when already PAUSED
or DISABLED (row goes into skipped[]).
- RESUME: status PAUSED → ACTIVE. Idempotent when already
ACTIVE. DISABLED subscriptions cannot be resumed (row goes
intofailed[]with INVALID_TRANSITION). - DELETE: removes the subscription record permanently. Not
reversible. Rows missing (already deleted) go into
skipped[].
SAFETY: - expected_count gate: server counts before mutation; mismatch
→ 409 COUNT_MISMATCH with no writes. See bulkActionTenants
for full rationale.
- idempotency_key: 15-min replay window; repeat submits return
the original response without re-applying. - HARD LIMIT: 500 matching rows. Larger filters → HTTP 400
LIMIT_EXCEEDED. - Overall HTTP 200 even when some rows fail; response envelope
reports success/failure counts.
EVENTS (document revision 0.1.25.33): - Server MUST emit one Event per successfully-mutated row,
typed by action: PAUSE → webhook.paused,
RESUME → webhook.resumed, DELETE → webhook.deleted.
Skipped rows (ALREADY_IN_TARGET_STATE, MISSING) and failed
rows MUST NOT produce an Event — emission is bound to actual
state transition, matching the single-op
createWebhookSubscription / updateWebhookSubscription /
deleteWebhookSubscription contract.
- Each emitted Event carries
correlation_id = webhook_bulk_action:<action>:<request_id>
(action lowercased, e.g.
webhook_bulk_action:pause:req_abc). Operators retrieve the
full bulk fan-out via
GET /v1/admin/events?correlation_id=…; the shared request_id
also ties every emitted row to the invocation's single
AuditLogEntry. - Event payload conforms to EventDataWebhookLifecycle. -
webhook.disabledis NOT produced by bulkActionWebhooks —
that EventType is reserved for the auto-health-driven
transition described in
WebhookSubscription.FAILURE HANDLING. Operator PAUSE actions
always emitwebhook.pausedregardless of prior status.
AUDIT LOG: - One AuditLogEntry per bulk-action invocation (not per row).
Per-row Events (above) are the operator-visible audit trail;
the AuditLogEntry is the compliance-facing invocation record.
AUTHORIZATION: - AdminKeyAuth only.
Authorizations
Administrative API key with full system access. Also accepted as an alternative to ApiKeyAuth on an explicit per-operation allowlist — the authoritative list is the union of operations whose security: block declares AdminKeyAuth (consult per-operation security blocks rather than this prose, which has historically drifted as the dual-auth surface expanded). When using AdminKeyAuth on list or fund endpoints, a tenant scoping parameter (typically tenant or tenant_id) is required for scoping (400 if missing) — the per-operation description specifies which. Lookup-style endpoints that uniquely identify a resource by non-tenant key (e.g. GET /v1/admin/budgets/lookup, where the (scope, unit) pair is unique) do NOT require a tenant parameter. Allowlisting is per-operation (exact method:path matching — no prefix matching, no wildcards) so new endpoints do not accidentally inherit admin-accessible status.
Request Body
Responses
Bulk action applied (may include per-row failures)