← Docs
Webhooks
Subscribe to SeaLink events — low balance, key budget warnings, model deprecations, status incidents, invoice generation.
Status: Live in v1 (target July 2026)
Event types
| Event | Description |
|---|---|
| balance.low | Account balance dropped below threshold (default $5). |
| balance.exhausted | Account balance reached $0; calls now return 402. |
| key.budget_warning | Per-key monthly spend reached 80% of cap. |
| key.budget_exhausted | Per-key monthly spend reached 100% of cap. |
| key.rotated | API key was rotated; old key was revoked immediately. |
| model.deprecated | An upstream model is being retired; deadline included. |
| incident.opened | A status page incident was opened. |
| incident.resolved | Incident resolved. |
| invoice.issued | Monthly invoice generated; PDF available. |
Signature verification
Each request carries SeaLink-Signature = HMAC-SHA256(timestamp + '.' + payload, secret). Always verify the timestamp to prevent replay.
Node.js
import crypto from "node:crypto";export function verifySeaLinkWebhook(payload: string, // raw request bodysignature: string, // value of SeaLink-Signature headersecret: string, // your endpoint signing secret from /dashboard/webhooks): boolean {// Header format: t=<timestamp>,v1=<hmac-sha256-hex>const parts = Object.fromEntries(signature.split(",").map((kv) => kv.split("=") as [string, string]));const t = parts.t, v1 = parts.v1;// Reject if older than 5 minutes (replay protection)if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;const expected = crypto.createHmac("sha256", secret).update(t + "." + payload).digest("hex");return crypto.timingSafeEqual(Buffer.from(expected, "hex"),Buffer.from(v1, "hex"),);}
Retries & idempotency
- SeaLink retries 5 times with exponential backoff on 5xx or timeout.
- Each request includes a SeaLink-Event-Id (UUID) — use it for idempotent deduplication.
- Return 2xx within 10 seconds; queue heavy processing.