Webhooks
Webhooks deliver real-time notifications to your server whenever something changes — a job or step (Scheduler) or money movement (Payments). Instead of polling the API, you register an endpoint URL and BayWise pushes events to it. Pick the events you care about across both products when you register.
Register an endpoint
POST /v1/platform/webhooks
{
"url": "https://yourapp.example.com/hooks/baywise",
"events": ["job.created", "job.delivered", "step.completed"]
}The response includes a signingSecret — store it securely. BayWise uses it to sign every delivery so you can verify requests came from us.
{
"id": "wh_...",
"url": "https://yourapp.example.com/hooks/baywise",
"events": ["job.created", "job.delivered", "step.completed"],
"signingSecret": "a3f9...",
"isActive": true
}Available events
Scheduler
| Event | When it fires |
|---|---|
job.created | A new job is created |
job.updated | Any field on a job changes |
job.delivered | A job reaches the Delivered status |
step.created | A new step is added to a job |
step.updated | Any field on a step changes |
step.completed | A step is marked completed |
The exact, current set of subscribable events is always shown in Settings → Developer when you register or edit a webhook — register only the ones you need.
Payload shape
Every webhook delivery sends a POST to your endpoint with a JSON body:
{
"event": "job.delivered",
"occurredAt": "2026-06-15T09:42:00Z",
"data": {
"id": "...",
"status": "delivered",
"vehiclePlate": "ABC 123",
...
}
}Verifying signatures
BayWise signs every delivery with HMAC-SHA256 using your signingSecret. Verify the signature to confirm the delivery is authentic.
The signature is in the X-BayWise-Signature request header:
X-BayWise-Signature: sha256=a1b2c3d4...Verification steps
- Read the raw request body as a UTF-8 string (do not parse it first)
- Compute
HMAC-SHA256(signingSecret, rawBody)and hex-encode the result - Compare it to the value after
sha256=in the header
import hmac, hashlib
def verify_signature(secret: str, raw_body: bytes, signature_header: str) -> bool:
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
received = signature_header.removeprefix("sha256=")
return hmac.compare_digest(expected, received)import { createHmac, timingSafeEqual } from 'node:crypto';
function verifySignature(secret, rawBody, signatureHeader) {
const expected = createHmac('sha256', secret).update(rawBody).digest('hex');
const received = signatureHeader.replace('sha256=', '');
const a = Buffer.from(expected, 'hex');
const b = Buffer.from(received, 'hex');
return a.length === b.length && timingSafeEqual(a, b);
}Always use a constant-time comparison (
hmac.compare_digest/timingSafeEqual) to prevent timing attacks.
Retry behaviour
If your endpoint returns a non-2xx response or times out (10 seconds), BayWise retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 (immediate) | — |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
After 5 failed attempts, the delivery is marked exhausted and no further retries occur. You can monitor delivery health in Settings → Integrations → Webhooks.
Respond quickly
Your endpoint should return 200 within 10 seconds. If processing takes longer, accept the webhook immediately and handle it asynchronously.
Managing registrations
| Action | Description |
|---|---|
| List webhooks | GET /v1/platform/webhooks — see all registered endpoints |
| Update a webhook | PATCH /v1/platform/webhooks/{id} — change the URL, events, or active state |
| Rotate the secret | PATCH /v1/platform/webhooks/{id} with { "rotateSecret": true } — generates a new signing secret |
| Delete a webhook | DELETE /v1/platform/webhooks/{id} — removes the registration and all delivery history |