Webhooks
Webhooks are account-level. One URL receives git events from every repository you own. Amendable stores Git. Your endpoint runs CI, CAD export, notifications, or whatever your product does next.
Events
Section titled “Events”| Event | When |
|---|---|
push | A branch or tag tip moved, including the first commit on a new ref |
create | A branch or tag was created (before is 40 zeros) |
delete | A branch or tag was deleted (after is 40 zeros) |
ping | Synthetic. Use Ping or POST /v1/webhooks/{id}/ping |
repository | Reserved. Do not rely on it yet |
Creating a branch emits create and push.
Create
Section titled “Create”The signing secret is generated by Amendable. You cannot set it. It is returned once on create.
amendable webhook create https://ci.example.com/hooks/amendable \ --events push,create,delete,pingSave the printed secret.
amendable webhook ping 22222222-2222-2222-2222-222222222222amendable webhook deliveries 22222222-2222-2222-2222-222222222222 --include-payload --jsoncurl -sS -X POST https://api.amendable.io/v1/webhooks \ -H "access-token: $AMENDABLE_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "url": "https://ci.example.com/hooks/amendable", "events": ["push", "create", "delete", "ping"], "active": true }'Needs API_WEBHOOKS or ALL. Max 20 webhooks per user. URL must be HTTPS. Private, loopback, and Amendable’s own hosts are rejected.
Settings → Webhooks. Create, ping, and inspect deliveries there.
Headers
Section titled “Headers”Every delivery includes:
| Header | Value |
|---|---|
Content-Type | application/json |
X-Amendable-Event | push, create, delete, or ping |
X-Amendable-Event-Id | Event UUID |
X-Amendable-Delivery | Delivery UUID |
X-Amendable-Signature | Hex HMAC-SHA256 of the raw body using the webhook secret |
Verify the signature
Section titled “Verify the signature”The examples in this repo are tested.
import hashlibimport hmac
def valid(secret: str, body: bytes, signature: str) -> bool: expected = hmac.new(secret.encode("utf-8"), body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, signature)Run the checked-in verifier:
python examples/webhook/verify.py \ --secret "$AMENDABLE_WEBHOOK_SECRET" \ --signature "$HTTP_X_AMENDABLE_SIGNATURE" \ --body-file payload.jsonimport crypto from "node:crypto";
export function valid(secret, body, signature) { const expected = crypto.createHmac("sha256", secret).update(body).digest("hex"); return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));}mac := hmac.New(sha256.New, []byte(secret))mac.Write(body)expected := hex.EncodeToString(mac.Sum(nil))hmac.Equal([]byte(expected), []byte(signature))A stdlib receiver is in examples/webhook/receiver.py. It returns 204 after a valid signature.
Payload (push)
Section titled “Payload (push)”Enough to clone and check out after:
{ "id": "event-uuid", "type": "push", "ref": "refs/heads/main", "ref_name": "main", "ref_type": "branch", "before": "0000000000000000000000000000000000000000", "after": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "created": true, "deleted": false, "commits": [], "head_commit": { "id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }, "tag": null, "repository": { "id": "repo-uuid", "name": "my-repo", "full_name": "demo/my-repo", "default_branch": "main", "html_url": "https://amendable.io/r/demo/my-repo", "clone_url": "https://amendable.io/r/demo/my-repo.git", "owner": { "id": "user-uuid", "username": "demo" } }}Full field list: Webhook payloads.
Delivery behavior
Section titled “Delivery behavior”- Timeout: 30 seconds
- Your endpoint should return 2xx quickly and do work asynchronously
- Failed deliveries are visible in the UI and
GET /v1/webhooks/{id}/deliveries
Ping even if ping is not in events. The ping endpoint force-delivers to that webhook only.