Skip to main content

Webhooks

Receive task.settled callbacks when a task completes settlement (consensus label + on-chain id available).

Registration — POST /v1/webhooks

curl -sS -X POST https://api.asgrefinery.io/v1/webhooks \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"callback_url": "https://example.com/hooks/asg",
"secret": "whsec_...",
"events": ["task.settled"]
}'

events defaults to ["task.settled"] if omitted.

Success (201):

{
"webhook_id": "whk_...",
"callback_url": "https://example.com/hooks/asg",
"events": ["task.settled"],
"active": true
}

Python

import os
from asg_gateway import ASGGatewayClient

client = ASGGatewayClient(os.environ["API_KEY"])
wh = client.register_webhook(
"https://example.com/hooks/asg",
secret="whsec_...",
events=["task.settled"],
)
print(wh["webhook_id"])
warning

For tasks that set callback_url, register a webhook whose callback_url matches so the dispatcher can select the correct signing secret.

Payload (task.settled)

{
"event": "task.settled",
"task_id": "tsk_...",
"consensus_label": "cat",
"confidence": 1.0,
"bsv_txid": "...",
"settled_at": "2026-04-08T21:05:00Z",
"timestamp": "2026-04-08T21:05:01Z"
}

Signature verification

Header: X-ASG-Signature: sha256=<hex> where <hex> is HMAC-SHA256 of the raw JSON body using your webhook secret.

Python

import hmac
import hashlib

def verify_signature(payload: bytes, secret: str, signature: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(), payload, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)

Go

func verifySignature(payload []byte, secret, signature string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(payload)
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signature))
}

Retry policy

  • Up to WEBHOOK_RETRY_MAX attempts (default 3).
  • Backoff delays: 1s, 4s, 16s between attempts.
  • After exhaustion, the task record may be marked with webhook_fail in storage — no further automatic attempts.

Local testing

  • Run a tunnel (ngrok, Cloudflare Tunnel, etc.) to expose https://... to your local HTTP server.
  • Point callback_url at the public URL.
  • When Gateway runs in Docker, you may use http://host.docker.internal:<port> from the container’s perspective (platform-dependent).

List — GET /v1/webhooks

curl -sS -H "Authorization: Bearer $API_KEY" \
https://api.asgrefinery.io/v1/webhooks

Secrets are not returned.

for w in client.list_webhooks():
print(w["webhook_id"], w["callback_url"], w["active"])

Delete — DELETE /v1/webhooks/{id}

Deactivates the webhook (soft delete).

curl -sS -X DELETE -H "Authorization: Bearer $API_KEY" \
https://api.asgrefinery.io/v1/webhooks/whk_xxx
client.delete_webhook("whk_xxx")

404:

{
"error": "webhook not found"
}