Guides
Webhooks
A twin environment can notify your endpoint whenever a call mutatesits state — a customer created, a message posted, an opportunity closed — with a signed event you verify the way you verify Stripe’s. Events are dispatched from the environment’s call ledger, never from sandboxed tool code; reads and failed calls never fire.
Register, verify, replay
API
$ curl -sS -X POST "$BLOBFISH_BASE_URL/api/v1/twin/environments/twe_<id>/webhooks" \
-H "X-API-Key: $BLOBFISH_API_KEY" -H "Content-Type: application/json" \
-d '{"url":"https://hooks.example.com/blobfish","events":["stripe.*","slack.chat.postMessage"]}'
# → 201 {"webhook_id":"whk_…","secret":"whsec_…","events":[…],"include_payload":false,"policy":{…}} (secret shown once)
# every mutating call now delivers:
# POST <url> Blobfish-Signature: t=<unix>,v1=<hex HMAC-SHA256(secret, t + "." + body)>
# Blobfish-Event: stripe.PostCustomers Blobfish-Delivery: whd_…
# body {"schema_version":"blobfish.twin-webhook-event.v1","id":"evt_…","type":"stripe.PostCustomers","payload_mode":"digest",
# "data":{"env_id":"twe_…","service":"stripe","operation":"PostCustomers","tool":"stripe_post_customers","protocol":"rest",
# "session":"sess_…","args_sha256":"…","change_sha256":"…","ts":"…"}}
$ curl -sS "$BLOBFISH_BASE_URL/api/v1/twin/environments/twe_<id>/webhooks/whk_<id>/deliveries" -H "X-API-Key: $BLOBFISH_API_KEY"
$ curl -sS -X POST "$BLOBFISH_BASE_URL/api/v1/twin/environments/twe_<id>/webhooks/whk_<id>/deliveries/whd_<id>/replay" -H "X-API-Key: $BLOBFISH_API_KEY"
$ curl -sS -X DELETE "$BLOBFISH_BASE_URL/api/v1/twin/environments/twe_<id>/webhooks/whk_<id>" -H "X-API-Key: $BLOBFISH_API_KEY"
$ blobfish twin-runs webhooks add twe_<id> --url https://hooks.example.com/blobfish # CLI shapeVerify (python)
import hmac, hashlib
def verify(secret: str, signature_header: str, body: bytes) -> bool:
parts = dict(p.split("=", 1) for p in signature_header.split(","))
expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"])Policy on this deployment
| Rule | Value |
|---|---|
| Payload | digest by default — event name, operation, tool, session, request id, and sha256 of the arguments and of the change set; include_payload: true adds the change set, capped at 32 KB. |
| Endpoint | https, publicly resolvable host (the same guard as custom model endpoints); never loopback or private ranges. |
| Limits | 10 registrations per environment; 500 deliveries per environment per hour; a bounded in-process queue. Past a limit the registration is refused (webhook_limit) or the delivery is counted, never silently dropped. |
| Retries | 3 attempts (1 s, 5 s, 25 s apart), 5 s per request; then dead. The last 100 deliveries stay readable and replayable. |
| Signature | Blobfish-Signature: t=<unix>,v1=<HMAC-SHA256(secret, t.body)>; a five-minute tolerance is the verifier’s default. Secrets derive from a server-side pepper and the shown-once value; the server keeps a hash. |
Vendor-shaped signatures (Stripe-Signature, GitHub’s X-Hub-Signature-256) are not emulated yet: a twin webhook is a Blobfish event about a vendor operation, not the vendor’s own webhook. Cluster-plane (tenv_) environments do not serve webhooks or environment tokens.