Agent skill

Spacelift Webhooks Skill

Receive and verify Spacelift webhooks. Use when setting up a Spacelift named webhook endpoint, debugging X-Signature-256 signature verification, or handling run state change and internal error messages routed by notification policies.

Install this skill

npx skills add hookdeck/webhook-skills --skill spacelift-webhooks


Spacelift is an infrastructure-as-code orchestration platform. Named webhooks (Integrations -> Webhooks) receive the messages that your notification policies route to them: run state changes and internal errors. Per-stack webhooks still exist for backwards compatibility. Webhooks are available on paid accounts only.

Each delivery is signed with the webhook's secret using "the exact same mechanism as GitHub": an HMAC of the raw request body, sent in the X-Signature-256 header as sha256=<hex>.

Setup note

Set a secret on the Spacelift webhook. Without one, deliveries are unsigned and fail verification.

When to Use This Skill

  • How do I receive Spacelift webhooks?
  • How do I verify the Spacelift X-Signature-256 signature?
  • How do I tell Spacelift run events from internal error events?
  • How do I order run updates with stateVersion?
  • Why does my Spacelift webhook fail signature verification, or get auto-disabled?

Verification (core)

const crypto = require('crypto');

// rawBody: the request body as a Buffer, exactly as received (empty Buffer if none)
function verifySpaceliftSignature(rawBody, header, secret) {
  if (!secret || typeof header !== 'string') return false;
  let sig = header.trim().replace(/^sha256=/i, '');      // accept with or without the prefix
  if (!/^[0-9a-fA-F]{64}$/.test(sig)) return false;      // validate before decoding
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest();
  const received = Buffer.from(sig.toLowerCase(), 'hex');
  return crypto.timingSafeEqual(expected, received);     // both 32 bytes
}
  • Header: X-Signature-256. Value: sha256=<64 hex chars> (lowercase hex HMAC-SHA256). The prefix is GitHub's format by reference; Spacelift's page doesn't print the value format, so accept it with or without sha256=.
  • Signed content is the raw body bytes only: no timestamp, id or other header is signed, so there is no replay window to check. Never parse and re-serialize before verifying.
  • The HMAC key is the secret's UTF-8 bytes, used as-is (not hex- or base64-decoded). The secret is chosen when you create the webhook and cannot be edited: rotate by deleting and recreating the webhook.
  • Spacelift also sends X-Signature (HMAC-SHA1) on most installs; verify only X-Signature-256 (the SHA-1 header is absent on FedRAMP).
  • Missing, empty or malformed header, or a mismatch: return 401.
  • Python: hmac.compare_digest on bytes, never str.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

HTTP Methods

Spacelift's docs don't name the HTTP method. Hookdeck's Spacelift source pre-selects POST without managing the method, so any other method the sender uses must also be allowed on the source. The examples accept GET, POST, PUT, PATCH and DELETE, verify the signature first for every method, and treat a missing body as empty bytes.

Message Types

There is no event-name field or header. Tell the two documented payload shapes apart:

MessageHow to recognise itKey fields
runhas state, stateVersion, run and stackaccount, state (e.g. FINISHED), stateVersion, timestamp, run.id, stack.id
internal_errorhas title and body, no statetitle, body, error, severity, account

Delivery can be out of order: use stateVersion to order updates for the same run and ignore a lower version than one already stored.

Delivery Behaviour

  • Optional Retry on failure: up to 3 retries, only on 5xx responses. Return 401 for a bad signature (not retried) and 5xx only for transient failures.
  • A webhook is auto-disabled after 10 or more consecutive failures and must be re-enabled by hand. Deliveries and their status codes are listed per webhook.

Environment Variables

SPACELIFT_WEBHOOK_SECRET=your-webhook-secret   # the secret you set on the Spacelift webhook

Local Development

npx hookdeck-cli listen 3000 spacelift --path /webhooks/spacelift

No account required. Use the printed URL as the webhook endpoint in Spacelift.

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 13, 2026

View on GitHub →