Webhooks
Receive Phylax events at your own endpoint and verify they came from us.
Webhooks push events to your endpoint as they happen, so you do not have to poll for a verdict that may not have changed. Every delivery is signed, and verifying that signature is the part that matters.
Register an endpoint
const hook = await phylax.webhooks.create({ url: 'https://example.com/hooks/phylax', events: ['verdict.changed', 'attestation.created'],})
console.log(hook.data.secret)curl -s -X POST https://api.phyi.dev/v1/webhooks \ -H "Authorization: Bearer $PHYLAX_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/hooks/phylax", "events": ["verdict.changed", "attestation.created"] }'The signing secret is returned exactly once, when the webhook is created, and is stored hashed afterwards. There is no endpoint that will tell you what it was. Put it straight into your secret store; if you lose it, rotate the webhook rather than trying to recover it.
Verify every delivery
Each request carries two headers:
| Header | Contents |
|---|---|
X-Phylax-Signature | HMAC over the timestamp and the raw body |
X-Phylax-Timestamp | When we sent it, in seconds |
The SDK does the comparison for you, in constant time, and rejects deliveries older than five minutes so a captured request cannot be replayed later.
import { verifySignature } from '@phyi/sdk'
app.post('/hooks/phylax', express.raw({ type: 'application/json' }), (req, res) => { const check = verifySignature({ rawBody: req.body, signature: req.get('X-Phylax-Signature'), timestamp: req.get('X-Phylax-Timestamp'), secret: process.env.PHYLAX_WEBHOOK_SECRET, })
if (!check.valid) return res.status(401).send(check.reason)
res.status(202).end() handle(JSON.parse(req.body.toString('utf8')))})Three details decide whether this is actually secure:
- Sign the raw body. Parse it after verifying, never before. A re-serialised body is a different byte string and the signature will not match, so people tend to “fix” that by skipping the check.
- Compare in constant time.
verifySignaturedoes; a===on the hex digest leaks timing. - Answer quickly. Return
202and do the work afterwards. A handler that scans a repository before replying will hit the delivery timeout and be retried.
Manage endpoints
await phylax.webhooks.list()await phylax.webhooks.get(id)await phylax.webhooks.update(id, { events: ['verdict.changed'] })await phylax.webhooks.delete(id)Set active: false to pause an endpoint without losing its configuration, which
is easier than deleting and re-registering during an incident.
Deliveries you should expect
Endpoints go down and networks drop packets, so design for repeats:
- Deliveries can arrive more than once. Key your handler on the event id and ignore one you have already processed.
- They can arrive out of order. Trust the timestamp in the payload rather than arrival order.
- A verdict can change. That is the point of
verdict.changed. An artifact that wasALLOWlast week can beBLOCKtoday, without you changing anything.
Related
- REST API for the endpoint reference
- SDKs for
verifySignatureand the webhook resource - How scoring works for what a verdict change means