Agent skill

Axilio Webhooks Skill

Receive and verify Axilio webhooks (real-phone automation), signed with the Standard Webhooks scheme. Use when setting up an Axilio webhook destination, getting the whsec_ signing secret, debugging webhook-id / webhook-timestamp / webhook-signature verification, or handling events like run.completed, run.failed, session.ended, balance.low, invoice.payment_failed, file.ready and phone.offline.

Install this skill

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


Axilio automates real phones: workflow runs, live sessions, files, billing and phone rentals. Its webhooks tell your server when a run finishes, a session ends, a payment fails or a rented phone goes offline, instead of polling. Every delivery is signed with the Standard Webhooks scheme (webhook-id, webhook-timestamp, webhook-signature).

When to Use This Skill

  • How do I receive Axilio webhooks?
  • How do I verify an Axilio webhook signature?
  • Where do I find my Axilio whsec_ signing secret?
  • How do I handle run.completed, run.failed or session.ended events?
  • Why is my Axilio webhook-signature verification failing?
  • How do I dedupe Axilio webhook retries?

Verification (core)

HeaderValue
webhook-idUnique per event, the same as the body's id, stable across retries
webhook-timestampUnix seconds of this delivery attempt
webhook-signaturev1,<base64 HMAC-SHA256>, possibly several space-separated entries

Signed content is {webhook-id}.{webhook-timestamp}.{raw body}. The key is the base64-decoded part of the destination's secret after whsec_. Each destination has its own secret, shown once when you register it. Axilio has no SDK of its own; the examples use standardwebhooks (npm, PyPI), which compares in constant time and tries every v1 entry.

const { Webhook } = require('standardwebhooks');

// rawBody: exact request body string, NOT re-serialised JSON
function verifyAxilioWebhook(rawBody, headers, secret = process.env.AXILIO_WEBHOOK_SECRET) {
  return new Webhook(secret).verify(rawBody, {
    'webhook-id': headers['webhook-id'],
    'webhook-timestamp': headers['webhook-timestamp'],
    'webhook-signature': headers['webhook-signature'],
  }); // returns the parsed event; throws → respond 400
}

Python: from standardwebhooks.webhooks import Webhook, then Webhook(secret).verify(await request.body(), {...same three headers...}). The examples also read the svix-id, svix-timestamp and svix-signature headers when webhook-id is absent, as Hookdeck does.

Hookdeck retries and the 5-minute window. The library always rejects a webhook-timestamp more than 5 minutes old. Hookdeck retries and replays resend the original timestamp, so the library rejects late retries after 5 minutes. A dependency-free verifier where the window can be dropped or widened is in references/verification.md.

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

Event Envelope

{ "id": "run.completed:0199a3c2-58c1-7f3a-9f2e-2f9a6a2f6a11", "type": "run.completed",
  "occurred_at": "2026-09-24T12:00:45Z", "test": false,
  "data": { "id": "...", "workflow_id": "...", "status": "completed", "success": true } }

id is <type>:<resource id>[:<time>] and equals webhook-id. test is true only for dashboard tests. data uses API field names and carries ids, statuses, reasons and timestamps, never your content (variables, files, recordings, download links). Fetch those through the API.

Common Event Types

The docs list 28 events (the list may not be exhaustive).

AreaEvents
Runsrun.started, run.completed, run.failed, run.cancelled
Sessionssession.started, session.ended, session.recording_ready
Billingbalance.low, balance.depleted, balance.recovered, invoice.paid, invoice.payment_failed, subscription.updated, subscription.canceled, auto_recharge.succeeded, auto_recharge.failed
Filesfile.ready, file.capture_failed, file.delivered, file.delivery_failed
Phones (rented only)phone.maintenance_started, phone.maintenance_ended, phone.offline, phone.online, phone.rental.activated, phone.rental.past_due, phone.rental.canceled
Platformwebhook.test

Full payloads: references/overview.md and Axilio's event reference.

Delivery Semantics

  • At least once. Return any 2xx quickly, then process asynchronously. Any other response, or none, is retried after 30 s, 2 min, 5 min, 15 min and 1 h.
  • Duplicates are possible. Skip an id you have already handled.
  • Order is not guaranteed. run.completed can arrive before run.started. Compare occurred_at or fetch the resource.
  • Failed deliveries can be replayed from the dashboard's delivery log.

Environment Variables

AXILIO_WEBHOOK_SECRET=whsec_xxxxx   # the destination's full signing secret; handlers fail closed (500) without it

Copy it when you register the destination under Settings > Webhooks. It is shown once.

Local Development

# Express / Next.js (use 8000 for FastAPI). No install or account required.
npx hookdeck-cli listen 3000 axilio --path /webhooks/axilio

Register the printed URL under Settings > Webhooks > Register Destination, then use Test on the destination to send an example event.

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 11, 2026

View on GitHub →