Agent skill

Aimfox Webhooks Skill

Receive and authenticate Aimfox webhooks. Use when setting up an Aimfox webhook endpoint, checking the Authentication Header, debugging 401s, or handling LinkedIn automation events like new_connection, accepted, new_reply, message and account_logged_out.

Install this skill

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


Aimfox (LinkedIn automation) sends a JSON webhook to your endpoint when something happens in a workspace: a connection is made or accepted, a lead replies, an account is logged out, a campaign starts or ends. Workspace webhooks are in beta. Webhooks are not signed. The only authentication is the optional Authentication Header you add to the webhook (Basic, or a custom header such as a Bearer token), which Aimfox sends with every request.

When to Use This Skill

  • How do I receive Aimfox webhooks?
  • How do I verify the Aimfox webhook Authentication Header?
  • Does Aimfox sign webhook payloads? (No: use a shared credential header)
  • How do I handle new_connection, accepted, new_reply or account_logged_out events?
  • Why does my Aimfox webhook endpoint return 401?

Verification (core)

const crypto = require('crypto');

// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
  const ha = crypto.createHash('sha256').update(String(a)).digest();
  const hb = crypto.createHash('sha256').update(String(b)).digest();
  return crypto.timingSafeEqual(ha, hb);
}

function verifyAimfoxRequest(headers, secret = process.env.AIMFOX_WEBHOOK_SECRET) {
  if (!secret) return false;                                    // never accept when unconfigured
  const name = (process.env.AIMFOX_WEBHOOK_HEADER || 'Authorization').toLowerCase();
  const received = headers[name];                               // Node lowercases header names
  return Boolean(received) && safeEqual(received, secret);      // whole value, no prefix parsing
}
  • The header name is whatever you typed into Authentication Header in Aimfox. Set AIMFOX_WEBHOOK_HEADER to that name (default Authorization) and AIMFOX_WEBHOOK_SECRET to the exact, whole header value, including any Bearer or Basic prefix. Do not strip or parse the prefix.
  • Look the header up case-insensitively. Return 401 when it is missing or differs.
  • A static shared credential authenticates the sender, not the body. There is no signature and no signed timestamp, so nothing stops a captured request being replayed. Use HTTPS and rotate the credential if it leaks.
  • Read the body raw (express.raw({ type: '*/*' })) and authenticate before parsing it. Return 400 for a body that isn't a JSON object, 200 once handled.

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

Event Types

Every payload has id, event_type, workspace and an event object whose fields depend on the type. Aimfox retries failed deliveries up to 6 times with exponential backoff, so handle duplicates by id.

event_typeSent When
account_logged_inA workspace LinkedIn account logs in and is authenticated
account_logged_outA workspace account is logged out (manually or session expiry)
new_connectionA new LinkedIn connection is detected on a workspace account
viewA campaign visits a LinkedIn profile
connectA campaign sends a connection request
acceptedA campaign connection request is accepted
inmailA campaign sends an InMail
messageA campaign sends a message
new_replyA reply arrives to any LinkedIn message on a workspace account
replyA lead sends their first reply to a campaign message
inmail_replyA lead replies to a campaign InMail
lead_label_addedA label is applied to a lead
campaign_createdA campaign is created
campaign_startedA campaign starts for the first time
campaign_endedA campaign runs to completion
inbox_eventAny inbox activity on a workspace account
campaign_replyA lead replies to any campaign message

Environment Variables

AIMFOX_WEBHOOK_HEADER=Authorization            # the header name you set in Aimfox
AIMFOX_WEBHOOK_SECRET="Bearer your-secret"     # the exact whole header value

Local Development

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

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

Use the printed URL as the webhook URL in Aimfox.

Resources


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 5, 2026

View on GitHub →