Meretrix Dev / docs

Guides

Webhooks

Webhooks push events to your server as they happen, so you do not need to poll the API. Each delivery is signed so you can check it came from us.

Registering an endpoint

curl -X POST https://api.meretrixdev.com/v2/webhooks \
  -H "Authorization: Bearer $MERETRIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/meretrix",
    "events": ["deployment.succeeded", "deployment.failed"]
  }'

The response contains a signing_secret. It is shown once, so store it with your other secrets.

Event payload

{
  "id": "evt_41d9a7",
  "type": "deployment.succeeded",
  "created_at": "2026-09-28T09:14:02Z",
  "data": {
    "deployment_id": "dpl_7c20be",
    "project_id": "prj_8f31c2",
    "ref": "main"
  }
}

Verifying the signature

Each request carries a Meretrix-Signature header of the form t=<unix time>,v1=<hex digest>. The digest is an HMAC-SHA256 of <t>.<raw body> using your signing secret.

import hmac, hashlib, time

def verify(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    t, sig = parts["t"], parts["v1"]
    if abs(time.time() - int(t)) > tolerance:
        return False
    signed = t.encode() + b"." + raw_body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig)

Compute the digest over the raw body exactly as received. Parsing and re-serialising the JSON changes the bytes and breaks the check.

Delivery and retries