REST API reference
Webhooks¶
Receive verified business updates without polling the REST API.
DeliveryAt least once
OrderingNot guaranteed
Retry policyUp to 8 attempts
Implement a receiver¶
- Create a webhook destination. Add a public HTTPS URL in your portal's External API settings and save the signing secret when it is shown.
- Read the raw request body. Keep the unmodified bytes until after signature verification; parsing and re-encoding JSON changes the signed value.
- Verify
Levr-Signature. Reject timestamps older than 300 seconds and compare everyv1digest using a constant-time comparison. - Acknowledge after durable acceptance. Return a 2xx response within 10 seconds, then perform slower work asynchronously. Deduplicate retries by the event
id.
Signature format¶
The header uses t=<unix-timestamp>,v1=<lowercase-hex-digest>. Parse its comma-separated values, read t and every v1, then compute HMAC-SHA256(signing_secret, timestamp + "." + raw_body) as lowercase hexadecimal. Accept the request when any v1 value matches; secret rotation may temporarily send two signatures.
Request headers¶
| Header | Use |
|---|---|
Content-Type |
Always application/json. Verify the signature before parsing the body. |
Levr-Event-ID |
Stable event identifier. Store it to make processing idempotent. |
Levr-Delivery-ID |
Stable identifier for this destination's delivery across retries. |
Levr-Signature |
Timestamp and one or more HMAC-SHA256 digests. |
Levr retries timeouts, connection failures, and non-2xx responses with exponential backoff. Events can arrive more than once or out of order.
business.status_changed¶
A business moved to a different stage in the portal pipeline. Verify Levr-Signature against the unmodified request body before processing.
View example event
{
"id": "evt_2m8q7c9k4x6p3n5d",
"type": "business.status_changed",
"api_version": "v1",
"created_at": "2026-08-19T16:42:13Z",
"data": {
"business": {
"id": "org_7fj3k9m2d8q4n6wx",
"name": "Example Analytics Inc.",
"status": {
"pipeline": "Clients",
"stage": "Underwriting",
"changed_at": "2026-08-19T16:42:13Z"
}
}
}
}
View payload schema
{
"type": "object",
"required": [
"id",
"type",
"api_version",
"created_at",
"data"
],
"properties": {
"id": {
"type": "string",
"pattern": "^evt_[a-z0-9]+$"
},
"type": {
"type": "string",
"const": "business.status_changed"
},
"api_version": {
"type": "string",
"const": "v1"
},
"created_at": {
"type": "string",
"format": "date-time"
},
"data": {
"type": "object",
"required": [
"business"
],
"properties": {
"business": {
"type": "object",
"required": [
"id",
"name",
"status"
],
"properties": {
"id": {
"type": "string",
"pattern": "^org_[a-z0-9]+$"
},
"name": {
"type": "string"
},
"status": {
"type": "object",
"required": [
"pipeline",
"stage",
"changed_at"
],
"properties": {
"pipeline": {
"type": "string"
},
"stage": {
"type": "string"
},
"changed_at": {
"type": "string",
"format": "date-time"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}