Agent skill
Remote Webhooks Skill
Receive and verify Remote (remote.com) webhooks. Use when setting up a Remote webhook callback, debugging X-Remote-Signature verification, or handling employment.onboarding.completed, employment.onboarding_task.completed, timeoff.requested, payslip.released and contract.termination_date_reached events.
Install this skill
npx skills add hookdeck/webhook-skills --skill remote-webhooks
Remote (global employment, contractor management and payroll) sends API webhooks to the callback URL you register with POST /v1/webhook-callbacks. Each delivery carries two headers, X-Remote-Timestamp (Unix milliseconds) and X-Remote-Signature (a bare lowercase hex HMAC-SHA256 digest), and a JSON body whose event_type field names the event.
When to Use This Skill
- How do I receive Remote webhooks?
- How do I verify the
X-Remote-Signatureheader from Remote? - Why does my Remote webhook signature verification fail?
- Where do I get the Remote webhook signing key? (the
signing_keyreturned when you create the callback) - How do I handle
employment.onboarding.completed,timeoff.requestedorpayslip.releasedevents?
Verification (core)
const crypto = require('crypto');
// signature = hex HMAC-SHA256(secret, raw_body + ":" + X-Remote-Timestamp)
function verifyRemote(rawBody, headers, secret = process.env.REMOTE_WEBHOOK_SECRET) {
const sig = headers['x-remote-signature'];
const ts = headers['x-remote-timestamp'];
if (!secret || typeof sig !== 'string' || typeof ts !== 'string' || !ts) return false;
if (!/^[0-9a-fA-F]{64}$/.test(sig)) return false; // validate before decoding
const message = Buffer.concat([rawBody, Buffer.from(':'), Buffer.from(ts, 'latin1')]);
const expected = crypto.createHmac('sha256', Buffer.from(secret, 'utf8')).update(message).digest();
const received = Buffer.from(sig.toLowerCase(), 'hex');
return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}
- The key is the callback's
signing_key, used as plain UTF-8 text (not hex- or base64-decoded). - Sign the raw body bytes, then a literal
:, then the timestamp header exactly as sent. Never parse and re-serialise the body first. - Return
401for a missing, malformed or wrong signature. Remote treats any 4xx or 5xx as delivered and does not retry it (only transport failures are retried), so there is no retry to wait for. - No timestamp window by default. Hookdeck retries and replays resend the original timestamp, so a fixed window would reject legitimate deliveries. If you want one, make it opt-in with
REMOTE_(unset orWEBHOOK_ TOLERANCE_ SECONDS 0= off) and compare against milliseconds, as the examples do. - Express: read the body with
express.raw({ type: '*/*' }). Next.js:await request.arrayBuffer(). FastAPI:await request.body().
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Event Types
Dispatch on the body's event_type. Remote documents 112 event types; these are the ones the examples handle.
| Event | Sent When |
|---|---|
employment.onboarding.completed | An employee's onboarding is fully completed |
employment.onboarding_ | An employee completes an onboarding task |
timeoff.requested | An employee submits a time off request |
timeoff.approved | A time off request is approved |
payslip.released | A payslip becomes available |
contract.termination_ | A contract reaches its termination date |
See references/overview.md for more, and log unknown types rather than failing.
Environment Variables
REMOTE_WEBHOOK_SECRET=your_signing_key_here # signing_key from POST /v1/webhook-callbacks
# REMOTE_WEBHOOK_TOLERANCE_SECONDS=300 # optional, off by default
Local Development
npx hookdeck-cli listen 3000 remote --path /webhooks/remote
No account required: the CLI creates a guest account on first run and gives you a tunnel and a web UI. Register the printed URL as your callback URL.
Resources
- references/overview.md - What Remote webhooks are, delivery behaviour, events
- references/setup.md - Register a callback and get the signing key
- references/verification.md - Signature details, the published test vector, gotchas
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests