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-signaturewebhook 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, andcanceledbuild 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:
| Webhook | Identifying fields | Notes |
|---|---|---|
BUILD | buildDetailsPageUrl, artifacts, metadata | artifacts.buildUrl is present for successful builds; error for failed ones |
SUBMIT | submissionDetailsPageUrl, submissionInfo | archiveUrl, turtleBuildId; submissionInfo.error for failed submissions |
Both carry id, accountName, projectName, platform (ios or android), and status:
status | Meaning |
|---|---|
finished | Build or submission succeeded |
errored | Failed; see error (build) or submissionInfo.error (submit) |
canceled | Canceled; cancelingUserId is set |
For full payloads, see references/overview.md.
Important Headers
| Header | Description |
|---|---|
expo-signature | sha1= followed by the hex HMAC-SHA1 of the raw body |
Content-Type | JSON; 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
- references/overview.md - BUILD and SUBMIT payloads, statuses, retries
- references/setup.md - Create, update, list, and delete webhooks with the EAS CLI
- references/verification.md - expo-signature details and debugging