Reference
alter webhooks
Register and operate the outbound webhook endpoints that receive approval lifecycle events.
An application can register HTTPS endpoints that Alter calls when a held request is created, decided, expires, executes or fails. These commands manage them. See Outbound webhooks for the event payloads and how to verify a delivery.
alter webhooks event-types --app <app-id-or-name>alter webhooks list --app <app-id-or-name>alter webhooks show --app <app-id-or-name> --endpoint <id>alter webhooks create --app <app-id-or-name> --url <url> --events <types> [--description <text>] [--header-file <path|->] [--header "Name: value"]...alter webhooks update --app <app-id-or-name> --endpoint <id> [--url <url>] [--events <types>] [--description <text>] [--enable | --disable] [--header-file <path|->] [--header "Name: value"]... | [--clear-headers]alter webhooks delete --app <app-id-or-name> --endpoint <id> [--confirm <endpoint-id> | --yes]alter webhooks rotate-secret --app <app-id-or-name> --endpoint <id> [--expire-previous] [--confirm <endpoint-id> | --yes]alter webhooks test --app <app-id-or-name> --endpoint <id>alter webhooks deliveries --app <app-id-or-name> --endpoint <id> [--status <status>] [--limit <n>] [--offset <n>]alter webhooks resend --app <app-id-or-name> --endpoint <id> --delivery <id>alter webhooks recover --app <app-id-or-name> --endpoint <id> --since <iso-8601>Every command takes --app <id> (or the app’s name), or link a workspace / set ALTER_APP_ID.
Scopes: dashboard_app_webhooks:read for event-types, list, show and deliveries (show prints the full URL only to a token whose owner is an organization admin and that holds dashboard_app_webhooks:write or dashboard_app_webhooks:admin as well); dashboard_app_webhooks:write for update, test, resend and recover; dashboard_app_webhooks:admin for delete; and dashboard_app_webhooks:rotate for anything that returns the signing secret or points an endpoint somewhere new — create (which needs write as well), update --url (which needs write as well) and rotate-secret. rotate is literal-only: neither * nor dashboard_app_webhooks:* grants it, so type it at alter auth login --scopes .... A default alter auth login grants read and write but not rotate; when the token lacks it, create and update --url refuse before sending anything, exit 7, and print the full alter auth login --scopes ... command that keeps the default set and adds dashboard_app_webhooks:rotate.
Every command that changes an endpoint — create, update, delete, rotate-secret, test, resend and recover — is also admin-only: the token’s owner must be an organization admin at the time of the call, whatever scopes the token holds (the same rule as keys mint, and as GitHub’s admin:repo_hook). A member’s token gets a 403, exit 7, and the CLI prints webhook changes require an organization admin. The reads stay open to every member.
event-types
Section titled “event-types”The event types an endpoint can subscribe to, the current payload api_version, and a payload_example.
alter webhooks event-types --app <app-id-or-name>List the app’s endpoints: URL, subscribed events, whether each is enabled (and why not — consecutive_failures or continuous_failure when Alter disabled it), the failure counter and the last delivery time. The signing secret is never returned. On stderr the CLI prints how many of the app’s max_endpoints are registered and the auto-disable thresholds (brake: the consecutive failed messages and the days of failure after which Alter disables an endpoint), which the list carries beside its rows. Each url is redacted to its scheme, host and port plus the endpoint’s own id, with no path or query (https://hooks.example.com#ep_1a2b3c4d), and a host label that looks random is masked (https://***.m.pipedream.net#ep_1a2b3c4d). The row carries url_redacted: true. This is because a receiver URL can carry its own secret in the path or the host. The id suffix tells endpoints on the same host apart, and nothing derived from the URL is shown; show prints the full URL to a token that also holds dashboard_app_webhooks:write. The CLI notes this on stderr.
alter webhooks list --app <app-id-or-name>One endpoint’s metadata, never its secret. The url is full (url_redacted: false) only when the token’s owner is an organization admin and the token holds dashboard_app_webhooks:write or dashboard_app_webhooks:admin — the caller that may change the endpoint. Any other token — one with only dashboard_app_webhooks:read, or one owned by a member, whatever its scopes — gets the same redacted url as list (url_redacted: true), and the CLI notes on stderr that the full URL needs an organization admin’s token holding dashboard_app_webhooks:write or dashboard_app_webhooks:admin. has_custom_headers says whether custom request headers are stored; their values are write-only.
alter webhooks show --app <app-id-or-name> --endpoint <endpoint-id>create
Section titled “create”Register an endpoint. The answer carries the endpoint’s signing secret in the signing_secret field — once. Store it where the receiver verifies deliveries; it cannot be retrieved later, only rotated.
| Flag | Required | Description |
|---|---|---|
--url <url> | yes | Absolute https:// URL of the receiver (at most 2048 characters; no embedded credentials, no ?query and no #fragment — a receiver credential or tenant id travels as a --header). Alter resolves and validates the host when it saves the endpoint and again on every delivery; a private, loopback or link-local address is refused. |
--events <types> | yes | Comma-separated, at least one of approval.requested, approval.decided, approval.expired, approval.executed, approval.failed. webhook.test is not subscribable: test delivers it to the endpoint whatever it subscribes to. An endpoint subscribed to nothing is refused locally (exit 2) and by the backend (webhook_event_types_required). |
--description <text> | no | A label (at most 255 characters). |
--header-file <path> | no | Custom request headers read from a file, or from stdin with -: one Name: value per line, blank lines ignored. This keeps header values (often receiver credentials) out of argv and shell history. |
--header "Name: value" | no | A custom request header sent with every delivery — an Authorization header the receiver checks, a tenant id. Repeatable; combined with --header-file, at most 10 in all. The value lands in argv, so the CLI warns and --header-file is preferred. |
A refused header is named by its position (--header #2, --header-file line 3) and, once its name is valid, by that name; its value is never printed. Header names are HTTP tokens (letters, digits and !#$%&'*+.^_`|~-), at most 128 characters, and a name may appear only once in any letter case. Host, Content-Type, Content-Length, Transfer-Encoding, Connection and the webhook-* names are reserved. A value is 1–1024 characters of visible ASCII, spaces and tabs; an empty value is refused (“header value must not be empty”). Headers are stored in Alter’s secret store and never shown again.
alter webhooks create --app <app-id-or-name> \ --url https://bot.example.com/alter/webhooks \ --events approval.requested,approval.decided,approval.expired \ --header-file /run/secrets/receiver-headers \ --output json > endpoint.json # the signing_secret field is in this file, onceAn app may hold at most 10 endpoints: the CLI reads the list first and refuses at the limit with exit 5 before sending anything (the backend’s own refusal is webhook_endpoint_limit, a conflict, the same exit code).
update
Section titled “update”Change the URL, the subscribed events or the description, replace or clear the custom headers, or enable / disable the endpoint. --enable and --disable are mutually exclusive, as are --header / --header-file and --clear-headers; --description "" clears the description. Changing --url re-points the endpoint, so it needs dashboard_app_webhooks:rotate as well as dashboard_app_webhooks:write. The CLI reads the token’s own scopes first and, when rotate is missing, refuses before sending anything; otherwise the backend’s refusal is printed. Either way the command exits 7 and names the missing scope. Re-enabling resets the failure counters: the next failure starts a new run.
alter webhooks update --app <app-id-or-name> --endpoint <endpoint-id> --events approval.decided,approval.failedalter webhooks update --app <app-id-or-name> --endpoint <endpoint-id> --enabledelete
Section titled “delete”Delete an endpoint. Its delivery history is deleted with it and cannot be recovered; pending deliveries to it are dropped; the signing secret and custom headers are removed from the secret store. The endpoint itself can be registered again (with a new secret). On a terminal the CLI states that and asks you to type the endpoint ID back; without one, pass --confirm <endpoint-id> (the same ID as --endpoint) or --yes, or the command exits 8. A --confirm that names another endpoint exits 2.
alter webhooks delete --app <app-id-or-name> --endpoint <endpoint-id> --confirm <endpoint-id>rotate-secret
Section titled “rotate-secret”Mint a new signing secret; the answer carries it in the signing_secret field — once — with previous_secret_valid_until. Deliveries during the following 24 hours carry a signature for each secret, so a receiver that verifies with either keeps working while the new one is rolled out. --expire-previous stops signing with the previous secret at once, with no overlap — for a secret that leaked; receivers still on it fail verification immediately. It is confirmed like delete: type the endpoint ID on a terminal, or pass --confirm <endpoint-id> or --yes. Two rotations racing each other are refused rather than silently overwriting: the loser exits 5 with webhook_rotation_conflict; read the endpoint and rotate again.
alter webhooks rotate-secret --app <app-id-or-name> --endpoint <endpoint-id> --confirm <endpoint-id> --output jsonQueue a webhook.test event to the endpoint — delivered whatever events it subscribes to — and print its message_id and delivery_id. Follow it with deliveries. A disabled endpoint is refused before anything is queued (exit 5, naming disabled_reason and the code webhook_endpoint_disabled, the same code the backend’s own 409 carries): update --enable it first.
alter webhooks test --app <app-id-or-name> --endpoint <endpoint-id>deliveries
Section titled “deliveries”The endpoint’s delivery log, newest first: the event, the status (pending, delivered, exhausted, failed, skipped_disabled), the attempt count, the last HTTP status or typed error (status:503, transport:timeout, blocked:blocked_destination), the next attempt time while pending, and the delivery id resend takes. Response bodies are never stored.
alter webhooks deliveries --app <app-id-or-name> --endpoint <endpoint-id> --status exhausted--status— filter by delivery status.--limit/--offset— pagination (1–200 per page, default 50). When another page exists, the CLI prints the next--offseton stderr.
resend
Section titled “resend”Queue a fresh delivery of one message (a new attempt row; the original keeps its history).
alter webhooks resend --app <app-id-or-name> --endpoint <endpoint-id> --delivery <delivery-id>recover
Section titled “recover”After an outage of the receiver: queue a fresh delivery of every message since --since whose latest delivery to this endpoint did not succeed. --since must be in the past. Bounded to 1,000 messages per call; the answer’s queued counts what was queued and next_since, when present, is the --since to pass on the next call.
alter webhooks recover --app <app-id-or-name> --endpoint <endpoint-id> --since 2026-11-01T00:00:00ZAuto-disable
Section titled “Auto-disable”An endpoint is disabled by Alter when 20 events in a row exhaust every delivery attempt with no success in between, or when every attempt has failed for 5 days: enabled turns false with disabled_reason set, the organization’s administrators are emailed, and an audit row webhook_endpoint.auto_disabled is written. Fix the receiver, update --enable, then recover what was missed.