Agent skill

Argyle Webhooks Skill

Receive and verify Argyle webhooks. Use when setting up an Argyle webhook subscription handler, debugging X-Argyle-Signature (HMAC-SHA512) verification failures, or handling payroll and income verification events such as accounts.connected, accounts.failed and gigs.fully_synced.

Install this skill

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


Argyle is a payroll and income verification API. A webhook subscription reports account, data-resource and verification changes (accounts, gigs, identities, items and more) for every user of your API key. When the subscription has a secret, each delivery carries an X-Argyle-Signature header: the hex HMAC-SHA512 of the raw request body.

When to Use This Skill

  • How do I receive Argyle webhooks?
  • How do I verify the Argyle webhook signature (X-Argyle-Signature, HMAC-SHA512)?
  • Why does my Argyle webhook signature verification fail or return 401?
  • How do I handle accounts.connected, accounts.failed or gigs.fully_synced events?

Setup Note

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

Argyle does not generate the secret: you choose it when you create the subscription (Console or POST /v2/webhooks). Argyle's docs don't name the HTTP method. Hookdeck's Argyle source pre-selects POST without managing the method, so any other method the sender uses must also be allowed on the source.

Verification (core)

const crypto = require('crypto');

// rawBody: Buffer of the exact bytes received (express.raw({ type: '*/*' })), never parsed first.
function verifyArgyleSignature(rawBody, signature, secret = process.env.ARGYLE_WEBHOOK_SECRET) {
  if (!secret) return false;
  if (typeof signature !== 'string' || !/^[0-9a-fA-F]{128}$/.test(signature)) return false;
  const expected = crypto.createHmac('sha512', secret).update(rawBody).digest('hex');
  const a = Buffer.from(signature.toLowerCase());
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
  • Header: X-Argyle-Signature, the whole value is one bare hex digest (128 characters, no prefix, no list).
  • Signed content: the raw body bytes only. No timestamp or id is signed, so there is no replay window to check. Make handlers idempotent instead (deliveries can repeat).
  • The HMAC key is the secret's UTF-8 bytes, used as-is (not hex- or base64-decoded). Hex is case-insensitive: validate length and alphabet, lowercase, then compare in constant time.
  • Missing or empty header, malformed value or mismatch: 401. Verify before parsing, for every method.
  • Accept GET, POST, PUT, PATCH and DELETE on the route; a request with no body is verified over zero bytes and, once verified, acknowledged with 200.
  • Answer 200 within 10 seconds. Argyle retries once after 30 s, only for 429, 500, 502, 503 or 504.

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

Common Event Types

Dispatch on the body's top-level event field (<resource>.<action>). No header names the type.

EventSent When
accounts.addedA new account is added for a user
accounts.connectedAn account connects successfully
accounts.failedAn account connection fails
accounts.removedAn account is removed
accounts.updatedAn account changes
gigs.added / gigs.updated / gigs.removedGig data changes
gigs.partially_synced / gigs.fully_syncedGig data sync progresses or completes
identities.added / identities.updatedIdentity data changes
items.updated / items.removedA supported employer or platform changes

Argyle documents 46 event types in all; the examples handle these 14 and log the rest. See references/overview.md.

Environment Variables

ARGYLE_WEBHOOK_SECRET=your-subscription-secret   # the `secret` set on the subscription

Local Development

Argyle can't reach localhost. Tunnel with the Hookdeck CLI (no account required):

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

Use the printed URL as the subscription url.

Resources


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 11, 2026

View on GitHub →