Skip to content

Webhooks

Receive signed HTTPS notifications for account grants, sync, account resources, orders, and reauth.

Finatic can POST JSON events to an HTTPS endpoint you control whenever subscribed account grants, sync state, account resources, orders, or reauth requirements change. Delivery is handled by FinaticBackground and is scoped to company account grants.

Configure subscriptions

Create and manage webhook endpoints in the Finatic dashboard (Settings → Webhooks) after you sign in.

For each subscription you provide:

  • URL — Must be HTTPS.
  • Environment — live or sandbox.
  • Event types — One or more event strings from the catalog below.
  • Secret — Used to compute X-Finatic-Signature.

The public event catalog is available at GET /api/v1/webhooks/catalog (no auth required). Subscription management is performed through the dashboard.

Event catalog

The canonical catalog is available at /api/v1/webhooks/catalog.

Account grants

  • account.grant.created
  • account.grant.updated
  • account.grant.revoked

Account sync

  • account.sync.started
  • account.sync.succeeded
  • account.sync.failed

Account resources

  • account.balance.updated
  • account.position.updated
  • account.transaction.created

Orders

  • order.created
  • order.updated
  • order.filled
  • order.cancelled
  • order.rejected

Connections

  • connection.reauth.required

Request shape

Each POST body is a JSON object with:

  • schemaVersion — currently v1.
  • eventId — unique event id.
  • eventType — subscribed event string.
  • createdAt — ISO timestamp.
  • environment — live or sandbox.
  • companyAccountId — company workspace that owns the grant/subscription.
  • accountId — financial account ID when the event is account-scoped.
  • grantId — account grant ID when available.
  • resource — resource pointer with resourceType, resourceId, accountId, and grantId.
  • data — event-specific payload.

Example:

1{ 2"schemaVersion": "v1", 3"eventId": "evt_01HX...", 4"eventType": "order.filled", 5"createdAt": "2026-06-08T20:15:00Z", 6"environment": "live", 7"companyAccountId": "acct_company_123", 8"accountId": "acct_financial_456", 9"grantId": "grant_789", 10"resource": { 11"resourceType": "order", 12"resourceId": "order_abc", 13"accountId": "acct_financial_456", 14"grantId": "grant_789" 15}, 16"data": { 17"status": "FILLED", 18"symbol": "AAPL" 19} 20} 21

Headers

Finatic sends at least:

1Content-Type: application/json 2User-Agent: Finatic-Webhooks/3.0 3Finatic-Event-Id: <eventId> 4 Finatic-Timestamp: <createdAt> 5 X-Finatic-Event: <eventType> 6 X-Finatic-Event-Id: <eventId> 7 X-Finatic-Timestamp: <createdAt> 8 X-Finatic-Delivery: live|test 9 ``` 10 11 `Finatic-Delivery-Id` / `X-Finatic-Delivery-Id` and `Finatic-Trace-Id` / `X-Finatic-Trace-Id` are included 12 when those 13 values are available. When a subscription secret is configured, Finatic also sends both signature headers with 14 the 15 same value: 16 17 ```http 18 Finatic-Signature: sha256=<hex> 19 X-Finatic-Signature: sha256=<hex> 20 ``` 21 22 ## Verify signatures 23 24 The signature is HMAC-SHA256 over the exact UTF-8 JSON body Finatic posts. Read and verify the raw body 25 before parsing or 26 re-serializing it. Acknowledge quickly with any HTTP 2xx and process expensive work asynchronously. 27 28 ```typescript 29 import { createHmac, timingSafeEqual } from 'node:crypto'; 30 31 export async function POST(request: Request) { 32 const rawBody = await request.text(); 33 const signature = request.headers.get('x-finatic-signature') ?? ''; 34 const expected = `sha256=${createHmac( 35 'sha256', 36 process.env.FINATIC_WEBHOOK_SECRET!, 37 ) 38 .update(rawBody, 'utf8') 39 .digest('hex')}`; 40 41 const valid = 42 signature.length === expected.length && 43 timingSafeEqual(Buffer.from(signature), Buffer.from(expected)); 44 45 if (!valid) { 46 return new Response('invalid signature', { status: 401 }); 47 } 48 49 return new Response('ok', { status: 200 }); 50 } 51 ``` 52 53 ```python 54 import hashlib 55 import hmac 56 import os 57 58 def is_valid_finatic_signature(raw_body: bytes, signature_header: str) -> bool: 59 expected = "sha256=" + hmac.new( 60 os.environ["FINATIC_WEBHOOK_SECRET"].encode("utf-8"), 61 raw_body, 62 hashlib.sha256, 63 ).hexdigest() 64 return hmac.compare_digest(signature_header, expected) 65 ``` 66 67 ## Retries and idempotency 68 69 The worker treats any HTTP 2xx response as success. On timeouts, transport errors, or non-2xx responses, 70 Finatic retries 71 with exponential backoff and jitter up to the configured attempt limit. Use `eventId` as your idempotency 72 key and store 73 the latest processed event per resource if your workflow is order-sensitive. 74 75 ## Testing 76 77 - Use sandbox subscriptions while you iterate. 78 - Use dashboard test sends to validate endpoint routing and signature handling. 79 - Confirm your handler routes by `companyAccountId`, `accountId`, and `grantId`. 80 - Do not use provider connection IDs in webhook routing logic. 81 82 ## Next steps 83 84 1. **[Account-First Model](/docs/quick-start/v1-account-first)** — Understand public identity fields. 85 2. **[Account Grants](/docs/quick-start/account-grants)** — Manage grant scopes and disconnect behavior. 86 3. **[API Reference](/docs/api-reference)** — Explore the webhook event catalog.