Skip to Content
DevelopersWebhooks

Webhooks

Where the API is you pulling, webhooks are Alma pushing — point one at your endpoint and Alma calls it when a workflow finishes.

The Webhooks section

Creating a webhook

Click New webhook and provide:

  • Endpoint URL — must be https://
  • Events — which to subscribe to
EventFires when
workflow.completedA workflow execution finished successfully
workflow.failedA workflow execution ended in a failed or partial state

The webhook’s name is derived from the URL’s host automatically.

Get your signing secret immediately after creating a webhook — it’s shown only once, and the create dialog doesn’t show it. Open the webhook and click Rotate secret, which does reveal it. Without this step there’s no way to get a signing secret from the Console.

Payload

workflow.completed and workflow.failed deliver:

{ "workflow_id": "...", "execution": { "id": "...", "status": "completed", "trigger_type": "schedule", "response_content": "...", "completion_summary": "...", "error": null, "started_at": "...", "completed_at": "..." } }

Headers

Content-Type: application/json User-Agent: Alma-Webhooks/1 X-Alma-Event: workflow.completed X-Alma-Delivery-Id: <delivery id> X-Alma-Timestamp: <ms epoch> X-Alma-Signature: sha256=<hex HMAC-SHA256>

Verifying the signature

The signature covers {timestamp}.{body}, not the body alone — signing the body alone will never match. This also lets you reject replays: check that X-Alma-Timestamp is recent before trusting the payload.

import { createHmac, timingSafeEqual } from "node:crypto"; function verify(rawBody, headers, secret) { const timestamp = headers["x-alma-timestamp"]; const signature = headers["x-alma-signature"]; if (Math.abs(Date.now() - Number(timestamp)) > 5 * 60 * 1000) return false; const expected = "sha256=" + createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex"); const a = Buffer.from(signature); const b = Buffer.from(expected); return a.length === b.length && timingSafeEqual(a, b); }

Secrets are prefixed whsec_.

Delivery

Alma retries failed deliveries 5 times with exponential backoff. Any non-2xx response counts as a failure. Timeout is 10 seconds.

Redirects aren’t followed — a 3xx counts as a failed attempt, since following one could bounce the delivery somewhere it shouldn’t go. Return a 2xx quickly and do your work afterward.

Auto-disable

15 consecutive failures disables an endpoint automatically, with the reason shown on its detail page. Fix the endpoint, then click Enable. The counter resets on any success.

Monitoring

A webhook's detail, with recent deliveries

The detail page has Send test, Rotate secret, and Disable/Enable, plus a Recent deliveries table with event, status, code, duration, and any error text.

URL restrictions

Endpoints must be https:// and publicly addressable. Alma rejects localhost, .local, .internal, and private or link-local IP ranges, both at creation and on every send.

Testing locally? Use a tunnel (ngrok, Cloudflare Tunnel) and register the public URL.

Limits

A workspace can have 20 endpoints.

Coming soon. The Console only lets you select the two workflow events — source events like source.github work if subscribed via the API, but there’s no UI for them. Filters aren’t editable. Name and URL can’t be changed after creation. There’s no delete, only Disable — which is awkward at the 20-endpoint cap, since the error message suggests deleting one. Email help@alma.team if you hit it.

Last updated on