These docs describe the target Fibric platform contract and proposed developer experience. Public Fibric CLI, API endpoints, and SDK packages are not yet available. All present-tense API, CLI, SDK, tenancy, security, retention, retry, and pricing language below is normative target language, not a statement that the surface is callable or commercially available today. Commands, package names, URLs, responses, limits, and timelines are reference examples unless a page explicitly identifies a deployed BearScope path.
Webhooks API
This page specifies a proposed webhook contract. No public v1.0 webhook-management service, delivery latency, retry schedule, or retention commitment exists today. The endpoints, signatures, and timelines below are reference examples.
Shared conventions, including authentication, pagination, the Idempotency-Key header, and the error envelope, are defined in the API overview. Error codes are catalogued in Errors.
Subscription event types
An endpoint subscribes to one or more delivery types. Each delivery wraps the full current API object named in the table, so a consumer needs no follow-up fetch for the common cases.
| Type | Fires when | data contains |
|---|---|---|
plan.proposed | An operator proposes an execution plan. | The plan object, verdicts included. |
plan.disposed | A plan finishes executing, is vetoed, or expires. | The plan object in its final state. |
action.disposed | The executor disposes one action: allowed, alerted, blocked, or deduplicated. | The action object. |
action.needs_approval | An action's verdict is ALERT and a human must approve. | The action object, plus its plan_id for the approval call. |
connector.status_changed | A connector transitions between connected, degraded, error, and pending_auth. | The connector object. |
receipt.written | Any receipt is appended, refusals included. | The receipt object. High volume; subscribe deliberately. |
Types follow the API's noun.verb convention, and new types are additive within the major version; ignore types you do not recognize.
The webhook endpoint object
Unique identifier, prefixed whk_.
Always webhook_endpoint.
The HTTPS destination. Plain HTTP is rejected at creation.
Subscribed delivery types from the table above.
active, disabled (by you), or suspended (by Fibric, after sustained delivery failure; see retries).
Signing secret, prefixed whsec_. Returned in full at creation and on rotation only; reads return a hint. Used to verify Fibric-Signature.
RFC 3339 timestamp of creation.
Timestamp of the most recent delivery attempt, successful or not.
Create an endpoint
webhooks:writeThe HTTPS destination.
One or more delivery types.
curl -X POST https://api.fibric.io/v1/webhook_endpoints \
-H "Authorization: Bearer $FIBRIC_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: whk-create-ops-consumer" \
-d '{
"url": "https://ops.example.com/fibric/webhook",
"types": ["plan.proposed", "action.needs_approval", "connector.status_changed"]
}'
{
"id": "whk_6b3e0d",
"object": "webhook_endpoint",
"url": "https://ops.example.com/fibric/webhook",
"types": ["plan.proposed", "action.needs_approval", "connector.status_changed"],
"status": "active",
"secret": "whsec_5f2e8a1c9b0d7e4f6a3b",
"created_at": "2026-07-02T15:30:00Z",
"last_delivery_at": null
}
Error cases:
| Status | Code | When |
|---|---|---|
400 | invalid_parameter | url is not HTTPS, or a type is unknown. |
409 | state_conflict | An active endpoint with this url already exists. |
List endpoints
webhooks:readReturns the tenant's endpoints, newest first, cursor-paginated with the standard limit and cursor parameters. The secret reads as a hint.
Update an endpoint
webhooks:writeThe target update operation changes endpoint configuration and can rotate a signing secret with a managed overlap window. No public endpoint or standard overlap duration exists today.
curl -X PATCH https://api.fibric.io/v1/webhook_endpoints/whk_6b3e0d \
-H "Authorization: Bearer $FIBRIC_KEY" \
-H "Content-Type: application/json" \
-d '{"rotate_secret": true}'
Delete an endpoint
webhooks:writeDeletes the endpoint. Pending retries for it are dropped; delivery records remain readable for their retention window.
The delivery object
Every attempt to reach your endpoint is recorded as a delivery. The same shape is what arrives in the HTTP POST body.
Unique identifier, prefixed dlv_. Stable across retries of the same delivery: use it as your deduplication key.
Always webhook_delivery.
The endpoint this delivery targets.
The delivery type, for example plan.proposed.
The full API object the type names, at the moment the delivery was created.
The envelope correlation id threading this delivery to its event, plan, and receipts.
1-based attempt counter for this delivery.
pending, succeeded, or failed (retries exhausted). Read over the API only; the POST body carries the delivery mid-flight.
RFC 3339 timestamp the delivery was created.
POST /fibric/webhook HTTP/1.1
Content-Type: application/json
Fibric-Signature: t=1782064081,v1=6e8a2c91f0b34d7e5a1c9f82d4b60e3a7c5f9b1d8e2a4c6f0b3d5e7a9c1f4b68
{
"id": "dlv_9c4f2e",
"object": "webhook_delivery",
"endpoint_id": "whk_6b3e0d",
"type": "action.needs_approval",
"correlation_id": "co_51d2e8",
"attempt": 1,
"created_at": "2026-07-02T15:02:10Z",
"data": {
"id": "act_91d1",
"object": "action",
"plan_id": "pl_7c1a",
"tool": "order.notify",
"entity_key": "order:SO-10884",
"disposition": "ALERT"
}
}
List deliveries
webhooks:readThe proposed endpoint returns cursor-paginated delivery records and illustrates redelivery. Retention is deployment-specific; no public 30-day commitment or callable replay endpoint exists.
Retries and backoff
The table below is a reference retry profile, not a current service commitment. Managed deployments define response deadlines, attempts, backoff, retention, and suspension behavior in writing:
| Attempt | Delay after previous | Elapsed (approx.) |
|---|---|---|
| 1 | immediate | 0 |
| 2 | 30 seconds | 30 s |
| 3 | 2 minutes | 2.5 m |
| 4 | 10 minutes | 13 m |
| 5 | 1 hour | 1.2 h |
| 6 | 6 hours | 7 h |
| Final | Deployment-defined | Deployment-defined |
Jitter, failure state, notification, suspension, and redelivery are target behaviors. They are not a public SLO or retention promise.
Retries and redelivery mean your consumer can see the same dlv_ id twice; deduplicate on it. Ordering across deliveries is not guaranteed, especially under retry. Treat each delivery as a pointer to current state: the data snapshot is convenient, but for decisions, re-read the object by id.
Verifying Fibric-Signature
Every delivery carries a Fibric-Signature header: a Unix timestamp and one or more HMAC-SHA256 signatures, hex-encoded.
Fibric-Signature: t=1782064081,v1=6e8a2c91f0b34d7e5a1c9f82d4b60e3a7c5f9b1d8e2a4c6f0b3d5e7a9c1f4b68
The signed message is the timestamp, a period, and the raw request body: {t}.{body}. To verify: compute HMAC-SHA256 over that message with your whsec_ secret, compare in constant time against each v1 value (two are present during a secret rotation), and reject if the timestamp is older than your tolerance, five minutes is conventional, to defeat replay.
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyFibricSignature(header: string, rawBody: string, secret: string): boolean {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; // 5-minute tolerance
const expected = createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest("hex");
const given = Buffer.from(String(parts.v1 ?? ""), "hex");
const want = Buffer.from(expected, "hex");
return given.length === want.length && timingSafeEqual(given, want);
}
Verify against the raw body bytes, before any JSON parsing or re-serialization; a re-stringified body will not match. Respond 2xx quickly and do the work asynchronously: the 10-second window includes your processing time.
Errors
| Status | Code | When |
|---|---|---|
400 | invalid_parameter | A non-HTTPS url, an unknown delivery type, or an invalid status transition. |
403 | insufficient_scope | The key lacks the webhooks:read or webhooks:write scope the route requires. |
404 | not_found | No endpoint or delivery with this id exists for the authenticated tenant. |
409 | state_conflict | Creating a duplicate url, or redelivering a delivery that is still pending. |