Agent skill

Expo (EAS) Webhooks Skill

Receive and verify Expo EAS webhooks for EAS Build and EAS Submit. Use when setting up an Expo webhook endpoint, verifying the expo-signature header (HMAC-SHA1 hex of the raw body, prefixed sha1=), telling build payloads from submit payloads, or handling finished, errored, and canceled statuses.

Install this skill

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


When to Use This Skill

  • How do I receive Expo EAS Build or EAS Submit webhooks?
  • How do I verify the Expo expo-signature webhook header?
  • Why does my Expo webhook signature verification fail?
  • How do I tell a build payload from a submit payload when there is no event type field?
  • How do I handle finished, errored, and canceled build and submission statuses?

Verification (core)

EAS signs the raw request body with HMAC-SHA1, keyed with the secret you passed to eas webhook:create --secret (at least 16 characters). The expo-signature header is sha1=<hex digest>. There is no timestamp and no message ID.

const crypto = require('crypto');

function verifyExpoSignature(rawBody, signatureHeader, secret) {
  if (!signatureHeader || !secret) return false;
  const expected = 'sha1=' + crypto.createHmac('sha1', secret).update(rawBody).digest('hex');
  try {
    return crypto.timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected));
  } catch {
    return false; // length mismatch
  }
}

In Express, read the body with express.raw({ type: '*/*' }) so verification works whatever Content-Type EAS sends. Parse JSON only after the signature passes.

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

Webhook Types and Statuses

EAS has no event-type field in the payload. Whether a webhook is BUILD or SUBMIT is set when you run eas webhook:create --event BUILD|SUBMIT, so tell payloads apart by their fields:

WebhookIdentifying fieldsNotes
BUILDbuildDetailsPageUrl, artifacts, metadataartifacts.buildUrl is present for successful builds; error for failed ones
SUBMITsubmissionDetailsPageUrl, submissionInfoarchiveUrl, turtleBuildId; submissionInfo.error for failed submissions

Both carry id, accountName, projectName, platform (ios or android), and status:

statusMeaning
finishedBuild or submission succeeded
erroredFailed; see error (build) or submissionInfo.error (submit)
canceledCanceled; cancelingUserId is set

For full payloads, see references/overview.md.

Important Headers

HeaderDescription
expo-signaturesha1= followed by the hex HMAC-SHA1 of the raw body
Content-TypeJSON; do not rely on it for body parsing

Environment Variables

EXPO_WEBHOOK_SECRET=your_webhook_secret   # the value passed to eas webhook:create --secret

Local Development

# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 expo --path /webhooks/expo

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 4, 2026

View on GitHub →