Search documentation
Fuzzy search across every docs page and section
PlatformWebhooks
Fuzzy search across every docs page and section
PlatformWebhooks
Platform
Signed event deliveries: event types, FC-Signature verification in Node, Python, and Go, and the retry schedule.
Webhooks turn polling into events: FeelConnect POSTs signed JSON to your endpoint when something happens — new transactions, item errors, usage warnings. Configure endpoints per application in Dashboard → Webhooks; each endpoint gets a signing secret (whsec_…, shown once) and a subscribed event list (* for all).
Every delivery is POST with a JSON body and three headers:
| Header | Contents |
|---|---|
FC-Webhook-Id | Unique delivery id — dedupe on it. |
FC-Timestamp | Unix time in seconds at signing. |
FC-Signature | v1= + lowercase hex of HMAC-SHA256(secret, timestamp + "." + rawBody). |
{"id": "evt_4tW7xQkV9nB2mZ5cRj8f","type": "transactions.sync_updates_available","created": 1753689601,"environment": "test","data": {"item_id": "item_7dJ3nWqZ5xC9vK1mTf4g"}}
| Type | Fires when | data |
|---|---|---|
link.session_finished | A Link session ends (success or exit). | link_session_id, status |
item.connected | A new item connects successfully. | item_id, institution_id |
item.error | An item enters login_required or error. | item_id, error.code, error.message |
item.removed | An item is removed via API or dashboard. | item_id |
transactions.initial_update | First transaction history for a new item is ready. | item_id, new_transactions |
transactions.sync_updates_available | New changes are waiting — call /transactions/sync. | item_id |
recurring.updated | Recurring streams changed for an item. | item_id |
holdings.updated | Investment holdings changed for an item. | item_id |
usage.limit_approaching | Your app hits 80% of a monthly quota. | metric, used, limit |
Verify before parsing, using the raw request body — re-serialized JSON will not match. Reject deliveries whose timestamp is more than 300 seconds old (replay protection), and compare signatures in constant time. Every SDK ships a helper:
import { verifyWebhook } from "@feelconnect/node";// Next.js route handler — read the RAW bodyexport async function POST(req: Request) {const rawBody = await req.text();let event;try {event = verifyWebhook({rawBody,signatureHeader: req.headers.get("FC-Signature"),timestampHeader: req.headers.get("FC-Timestamp"),secret: process.env.FEELCONNECT_WEBHOOK_SECRET!, // whsec_…});} catch {return new Response("invalid signature", { status: 400 });}if (event.type === "transactions.sync_updates_available") {await enqueueSync(event.data.item_id as string);}return new Response("ok"); // 2xx fast; do real work async}
A delivery succeeds on any 2xx within 10 seconds. Anything else — timeouts, 4xx, 5xx — is retried with backoff, up to 5 attempts total:
| Attempt | Delay after previous failure |
|---|---|
| 1 | immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
id or FC-Webhook-Id header.created timestamp when ordering matters.In the test environment, fire any event type on demand with POST /v1/sandbox/fire_webhook. For local development, point your endpoint at a tunnel (for example cloudflared or ngrok) and use the dashboard's delivery log to replay failures.