Gareth Wilson Gareth Wilson

Guide to Fiber AI Webhooks: Features and Best Practices

Published


Fiber AI is a B2B data API for finding and enriching people and companies. Many of its operations run in the background, including batch enrichments, lookups, searches, audience builds and Tracker monitoring, so webhooks are how your system learns that a job has finished, whether it succeeded, and where to find the results.

This guide covers how Fiber AI webhooks are configured and signed, the events and their payloads, the retry schedule, and the practices that keep a Fiber AI receiver reliable.

What are Fiber AI webhooks?

Fiber AI delivers webhooks through Svix. You add an HTTPS endpoint, pick the event types it should receive, and Fiber AI posts a JSON body to it when one of those events happens. Svix handles the delivery itself: retries with backoff, signatures, a self-service portal with delivery logs, and one-click replay.

Every delivery carries svix-id, svix-timestamp and svix-signature headers. The signature is an HMAC-SHA256 of the ID, the timestamp and the raw body, keyed with the endpoint's whsec_... signing secret. This is the same construction as Standard Webhooks, with svix- header names.

Fiber AI webhook features

FeatureDetails
Delivery platformSvix
ConfigurationPortal via fiber.ai/app/webhooks, or the webhook endpoints API
EndpointsHTTPS only, subscribed to at least one event type
PayloadVaries by event; most completion events carry success, an identifier and error
Signature headerssvix-id, svix-timestamp, svix-signature
Schemev1,<base64 HMAC-SHA256>, space-separated entries
Signed content{svix-id}.{svix-timestamp}.{raw body}
Secretwhsec_..., per endpoint, shown only at create and rotate
RotationThe old secret keeps verifying for a short grace period
Timeout15 seconds; 3xx counts as a failure
RetriesImmediately, then 5s, 5min, 30min, 2h, 5h, 10h, 10h
OrderingNot guaranteed
Source IPsA published set, available on request

Common events

Fiber AI's catalogue has 26 event types. The docs group 25 of them, and the endpoint API also accepts batch_contact_enrich_v1.completed.

AreaEventsFires when
Enrichmentreveal.completed, batch_live_enrich.completed, batch_contact_enrich.completed, mosaic.completed, audience.enrichment_completed, audience.prospect_export_completed, audience.company_export_completedA contact reveal, enrichment, file repair or audience export finishes
Lookupgithub_to_linkedin.completed, github_lookup.completed, domain_lookup.completed, social_media_lookup.completedA lookup job finishes
Searchsaved_search.run_upcoming, saved_search.run_completed, combined_search.completed, google_maps_search.completed, local_business_search.completed, audience.build_completedA saved search is about a day away or has run, or an async search or audience build finishes
Monitoringtracker.signal_detected, tracker.list_run_upcoming, tracker.list_run_completed, job.changed, job_changes.profiles_addedA Tracker rule fires, a tracker list refresh is due or done, or tracked people change jobs
Otherdepth_chart.completed, sales_nav_scrape.completed, sales_nav_lite_scrape.completedA depth chart or Sales Navigator scrape finishes

The tracker list run events are turned on per organisation. Subscribing an endpoint isn't enough; ask Fiber AI to enable them.

Here is Fiber AI's reveal.completed example:

{
  "task_id": "abc123",
  "success": true,
  "emails": [{ "emailAddress": "jane@company.com", "type": "work" }],
  "phone_numbers": [{ "phoneNumber": "+1-555-0100", "type": "mobile" }],
  "linkedin_url": "https://www.linkedin.com/in/jane-doe"
}
  • success says whether the job worked, and error is filled in when it didn't.
  • The identifier depends on the operation: task_id, run_id, search_id or audience_id.
  • job.changed is different. It carries a profiles array with no success or error, and each profile includes the listId it was tracked on, so one person can arrive once per list.
  • batch_live_enrich.completed reports counts (completed, failed, not_found, malformed) instead of error.
  • The heads-up events (saved_search.run_upcoming, tracker.list_run_upcoming, tracker.list_run_completed) only ever send success: true.
  • Tracker signals use a richer envelope with the matched entity, the rule, a stable eventId and occurredAt.

The documented example has no field naming the event type, and Fiber AI documents no event header. Call listWebhookEventTypes for each type's examplePayload, or send yourself test events, before deciding how to route.

Setting up Fiber AI webhooks

  1. Go to fiber.ai/app/webhooks and click Set up webhooks. Always enter the portal from there, because portal links are short-lived.
  2. Click Add Endpoint and enter your HTTPS URL.
  3. Select the event types to receive and save. Delivery starts immediately.
  4. Copy the endpoint's signing secret (whsec_...) and store it server-side, for example as FIBER_AI_WEBHOOK_SECRET.

You can do the same over the API, free of credits:

curl -X POST "https://api.fiber.ai/v1/webhooks/endpoints" \
  -H "Content-Type: application/json" \
  -d '{"apiKey": "'"$FIBER_API_KEY"'",
       "url": "https://example.com/webhooks/fiber",
       "eventTypes": ["reveal.completed", "batch_contact_enrich.completed"]}'

The response's output.signingSecret is only returned here and by rotateWebhookEndpointSecret. Reads never return it.

If you receive Fiber AI webhooks through Hookdeck, use the Hookdeck source URL as the endpoint and paste that endpoint's whsec_... secret into the Fiber AI source. Each endpoint has its own secret, so if you create several endpoints, give each one its own Hookdeck source.

To test, use sendTestWebhookEvent or the portal's Send Example button. Both deliver an example payload of the type you choose, signed like a real one.

Securing Fiber AI webhooks

Join svix-id, svix-timestamp and the raw request body with dots. Take the part of the secret after whsec_, base64-decode it, and use those bytes as the HMAC-SHA256 key. Base64-encode the result and compare it in constant time with each v1, entry in the space-separated svix-signature header. A match on any entry is enough.

Fiber AI's docs, and the Fiber AI skill's examples, use the official svix library. It always rejects an svix-timestamp more than 5 minutes from your clock, and the window can't be turned off. Hookdeck retries and replays resend the original svix-timestamp, so a delivery that reaches your code later than that fails even though its signature is valid. If you receive through Hookdeck, use a manual verifier like the one below, or the one in the skill's references/verification.md, where the window is optional and off by default.

const crypto = require('crypto');
const express = require('express');

function verifyFiberSignature(rawBody, headers, secret, toleranceSeconds = 0) {
  const id = headers['svix-id'];
  const timestamp = headers['svix-timestamp'];
  const signatureHeader = headers['svix-signature'];
  if (!id || !timestamp || !signatureHeader || !secret || !secret.startsWith('whsec_')) return false;
  if (!/^\d+$/.test(timestamp)) return false;

  // Optional replay check, off by default.
  if (toleranceSeconds > 0 && Math.abs(Date.now() / 1000 - Number(timestamp)) > toleranceSeconds) {
    return false;
  }

  const key = Buffer.from(secret.slice('whsec_'.length), 'base64');
  const expected = crypto.createHmac('sha256', key).update(`${id}.${timestamp}.`).update(rawBody).digest();

  return signatureHeader.split(' ').some((entry) => {
    const [version, signature] = entry.split(',');
    if (version !== 'v1' || !signature) return false;
    const received = Buffer.from(signature, 'base64');
    return received.length === expected.length && crypto.timingSafeEqual(received, expected);
  });
}

const app = express();

app.post('/webhooks/fiber', express.raw({ type: '*/*' }), (req, res) => {
  const secret = process.env.FIBER_AI_WEBHOOK_SECRET;
  if (!secret) return res.sendStatus(500); // fail closed

  if (!verifyFiberSignature(req.body, req.headers, secret)) {
    return res.sendStatus(401);
  }

  const event = JSON.parse(req.body.toString('utf8'));
  // Deduplicate on req.headers['svix-id'], then process asynchronously.
  res.sendStatus(200);
});

The same check in Python:

import base64
import binascii
import hashlib
import hmac
import time


def verify_fiber_signature(raw_body: bytes, headers, secret: str, tolerance_seconds: int = 0) -> bool:
    msg_id = headers.get("svix-id")
    timestamp = headers.get("svix-timestamp")
    signature_header = headers.get("svix-signature")
    if not (msg_id and timestamp and signature_header and secret and secret.startswith("whsec_")):
        return False
    if not timestamp.isdigit():
        return False
    # Optional replay check, off by default.
    if tolerance_seconds > 0 and abs(time.time() - int(timestamp)) > tolerance_seconds:
        return False

    try:
        key = base64.b64decode(secret[len("whsec_"):])
    except (binascii.Error, ValueError):
        return False
    signed = f"{msg_id}.{timestamp}.".encode("utf-8") + raw_body
    expected = base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest())

    for entry in signature_header.split(" "):
        version, _, signature = entry.partition(",")
        if version == "v1" and hmac.compare_digest(signature.encode("utf-8"), expected):
            return True
    return False


# In FastAPI: raw_body = await request.body()
# verify_fiber_signature(raw_body, request.headers, os.environ["FIBER_AI_WEBHOOK_SECRET"])

To check your verifier, use the secret whsec_ZmliZXItYWktZ3VpZGUtdGVzdC1zaWduaW5nLWtleSE=, the ID msg_2nQx7bP0uYc3LwKfR8sT1vZ5aE9, the timestamp 1761400000 and this exact body:

{"task_id":"abc123","success":true,"linkedin_url":"https://www.linkedin.com/in/jane-doe"}

The svix-signature value should be:

v1,b8/3JsNDYwrxzxBJuq2ImhIzv8SPtBNv03e8LsNMjoQ=

Make Fiber AI webhooks production-ready. Hookdeck Event Gateway verifies the Svix signature on every Fiber AI delivery, answers well inside the 15-second timeout, and holds each job result until your service has processed it.

Fiber AI webhook limitations and pain points

The body doesn't name the event

The Problem: One handler receives several event types and can't tell which one arrived.

Why It Happens: Fiber AI documents no event-type header, and its documented example body has no type field. Payload shapes also overlap: most completion events carry success, an identifier and error.

Workarounds:

  • Check examplePayload from listWebhookEventTypes, or a test delivery, for each type you subscribe to.
  • Create a separate endpoint for each event type, or each group you handle the same way, with its own URL path, so the route tells you the type.

How Hookdeck Can Help: Give each Fiber AI endpoint its own Hookdeck source and route each one to the right handler. Hookdeck filters can also route on fields such as profiles or task_id when one source receives several types.

The library's fixed 5-minute window

The Problem: Genuine events fail verification when they are retried or replayed late.

Why It Happens: The svix libraries reject any svix-timestamp more than 5 minutes from the current time, and a retry or replay from Hookdeck keeps the original timestamp because it is part of the signed content.

Workarounds:

  • Verify manually and choose your own window, or drop the window and deduplicate on svix-id.
  • Run verification as soon as the request arrives, before it is queued.

How Hookdeck Can Help: Hookdeck verifies the signature when the request arrives and applies no timestamp window, so retrying or replaying an event from Hookdeck doesn't fail verification at the source.

Out-of-order and repeated deliveries

The Problem: A later result overwrites a newer one, or the same job is processed twice.

Why It Happens: Fiber AI doesn't guarantee order, and retries over roughly a day mean the same message can arrive more than once. People tracked on several job-change lists arrive once per list.

Workarounds:

  • Deduplicate on svix-id, which stays the same across retries. Tracker signals also carry a stable eventId.
  • Order Tracker signals by occurredAt, not by arrival.
  • Treat job.changed entries per listId.

How Hookdeck Can Help: Hookdeck's deduplication can drop repeated deliveries on svix-id, and its delivery queue smooths bursts from large batch jobs.

Secrets shown only twice

The Problem: A lost secret means you can't verify deliveries until you rotate it.

Why It Happens: Fiber AI returns the signing secret only when an endpoint is created or its secret is rotated.

Workarounds:

  • Save the secret into your secrets manager in the same step that creates the endpoint.
  • To recover, rotate the secret and deploy the new one during the grace period, while the old one still verifies.

How Hookdeck Can Help: With Hookdeck in front, only the Fiber AI source holds the secret, so a rotation means updating one field.

Best practices

Verify against the raw body

The signature covers the exact bytes. Use express.raw({ type: '*/*' }) in Express, await request.text() in a Next.js route handler and await request.body() in FastAPI, and parse only after verification.

Check success before using results

A completion event can report a failed job. Branch on success, log error, and only fetch or store results for jobs that worked.

Pause endpoints during maintenance

Update an endpoint with disabled: true to stop deliveries without deleting it, and set it back to false to resume. A message that exhausts its retries is marked failed in the portal, where you can retry it manually.

Acknowledge fast and process asynchronously

Verify, store, return 200, and do the work afterwards. Slow handlers hit the 15-second timeout and cause needless retries. See why to process webhooks asynchronously and our guide to webhook idempotency.

Conclusion

Fiber AI webhooks arrive through Svix, signed with the svix-* headers and a per-endpoint whsec_ secret. Save the secret when you create the endpoint, verify against the raw body with a verifier whose window suits your retry path, work out how you will tell event types apart, and deduplicate on svix-id.

Hookdeck Event Gateway sits in front of your receiver, checks every Fiber AI signature, and holds each event until your service has processed it.

Get started with Hookdeck for free and handle Fiber AI webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.