Webhooks
Webhooks let you receive real-time notifications when events happen in your Odento account. Register a URL, subscribe to events, and we'll send a POST request with the event payload every time something happens.
How Webhooks Work
- You create a webhook endpoint with a URL and a list of events to subscribe to
- When an event occurs (e.g.,
appointment.created), Odento sends an HTTP POST to your URL - The request body contains the event payload, signed with your webhook secret
- You verify the signature, process the event, and respond with a 200 status
- If we don't receive a 200 within 10 seconds, we retry with exponential backoff
Retry Policy
We retry failed deliveries up to 5 times with exponential backoff: 1m, 5m, 30m, 2h, and 12h. After 5 failures, the webhook is automatically disabled.
Available Events
| Event | Description |
|---|---|
| patient.created | A new patient record was created |
| patient.updated | A patient record was updated |
| appointment.created | A new appointment was scheduled |
| appointment.updated | An appointment was modified |
| appointment.cancelled | An appointment was cancelled |
| appointment.completed | An appointment was marked completed |
| invoice.created | A new invoice was created |
| invoice.paid | An invoice was fully paid |
| payment.received | A payment was recorded |
| lab_order.created | A lab order was created |
| lab_order.status_changed | A lab order status changed |
| lab_order.shipped | A lab order was shipped |
Payload Format
Every webhook delivery sends a JSON body with the following structure:
{
"event_id": "evt_abc123def456ghi789",
"event_type": "appointment.created",
"created_at": "2025-01-22T10:05:00Z",
"data": {
"object": {
"id": "apt_7a3b9c1d2e5f8a4b",
"patient_id": "pat_5f8d3a2b1c9e4a7b",
"dentist_id": "usr_abc123",
"start_time": "2025-01-22T10:00:00Z",
"end_time": "2025-01-22T11:00:00Z",
"type": "consultation",
"status": "scheduled",
"location_id": "loc_xyz789"
}
}
}Signature Verification
Every webhook request includes an X-Odento-Signature header containing a HMAC-SHA256 signature of the request body, computed using your webhook signing secret. Always verify this signature before processing the payload.
The signature is computed as:
HMAC-SHA256(webhook_secret, raw_request_body)Here's how to verify the signature in Node.js:
import crypto from 'crypto'
function verifyWebhookSignature(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(signature, 'hex')
)
}
// Express example
app.post('/webhooks/odento', (req, res) => {
const signature = req.headers['x-odento-signature']
const secret = process.env.ODENTO_WEBHOOK_SECRET
if (!verifyWebhookSignature(req.rawBody, signature, secret)) {
return res.status(401).send('Invalid signature')
}
const event = JSON.parse(req.rawBody)
console.log('Received event:', event.event_type)
// Process the event...
res.status(200).send('OK')
})/webhooksCreate a webhook
Creates a new webhook endpoint. You'll receive a signing secret in the response — use it to verify webhook signatures. The secret is shown only once at creation time.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | Required | HTTPS URL to receive webhook deliveries |
| events | array | Required | Array of event types to subscribe to (e.g., ["appointment.created", "invoice.paid"]) |
| description | string | Optional | Human-readable description |
| active | boolean | Optional | Whether the webhook is active. Default: true |
Example Request
curl -X POST https://api.odento.app/api/v1/webhooks \
-H "X-API-Key-ID: ok_live_abc123def456ghi789jkl012" \
-H "X-API-Key-Secret: os_abc123def456ghi789jkl012mno345pqr678stu901vwx234yzab567" \
-H "Content-Type: application/json" \
-d '{
"url": "https://myapp.com/webhooks/odento",
"events": ["appointment.created", "appointment.cancelled", "invoice.paid"],
"description": "Production webhook for appointment and billing events"
}'Example Response
{
"success": true,
"data": {
"id": "wh_abc123def456ghi789",
"url": "https://myapp.com/webhooks/odento",
"events": ["appointment.created", "appointment.cancelled", "invoice.paid"],
"description": "Production webhook for appointment and billing events",
"signing_secret": "whsec_abc123def456ghi789jkl012mno345pqr678",
"active": true,
"created_at": "2025-01-15T10:30:00Z"
}
}/webhooksList webhooks
Returns all webhook endpoints for your organization.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| active | boolean | Optional | Filter by active status |
| limit | integer | Optional | Results per page (1-100). Default: 20 |
Example Request
curl -X GET https://api.odento.app/api/v1/webhooks \
-H "X-API-Key-ID: ok_live_abc123def456ghi789jkl012" \
-H "X-API-Key-Secret: os_abc123def456ghi789jkl012mno345pqr678stu901vwx234yzab567"Example Response
{
"success": true,
"data": {
"items": [
{
"id": "wh_abc123def456ghi789",
"url": "https://myapp.com/webhooks/odento",
"events": ["appointment.created", "appointment.cancelled", "invoice.paid"],
"description": "Production webhook for appointment and billing events",
"active": true,
"last_delivered_at": "2025-01-22T10:05:00Z",
"last_response_status": 200,
"created_at": "2025-01-15T10:30:00Z"
}
],
"pagination": {
"has_more": false,
"next_cursor": null,
"per_page": 20
}
}
}Try It Out
/webhooks/{id}Retrieve a webhook
Retrieves a single webhook endpoint by ID, including delivery statistics.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Required | The webhook ID (wh_*) |
Example Request
curl -X GET https://api.odento.app/api/v1/webhooks/wh_abc123def456ghi789 \
-H "X-API-Key-ID: ok_live_abc123def456ghi789jkl012" \
-H "X-API-Key-Secret: os_abc123def456ghi789jkl012mno345pqr678stu901vwx234yzab567"Example Response
{
"success": true,
"data": {
"id": "wh_abc123def456ghi789",
"url": "https://myapp.com/webhooks/odento",
"events": ["appointment.created", "appointment.cancelled", "invoice.paid"],
"description": "Production webhook for appointment and billing events",
"active": true,
"statistics": {
"total_deliveries": 342,
"successful": 338,
"failed": 4,
"last_24h": 18
},
"last_delivered_at": "2025-01-22T10:05:00Z",
"last_response_status": 200,
"created_at": "2025-01-15T10:30:00Z"
}
}/webhooks/{id}Update a webhook
Updates a webhook endpoint's URL, events, or active status. To rotate the signing secret, use the rotate-secret endpoint.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Required | The webhook ID (wh_*) |
| url | string | Optional | Updated URL |
| events | array | Optional | Updated event subscriptions |
| active | boolean | Optional | Updated active status |
| description | string | Optional | Updated description |
Example Request
curl -X PATCH https://api.odento.app/api/v1/webhooks/wh_abc123def456ghi789 \
-H "X-API-Key-ID: ok_live_abc123def456ghi789jkl012" \
-H "X-API-Key-Secret: os_abc123def456ghi789jkl012mno345pqr678stu901vwx234yzab567" \
-H "Content-Type: application/json" \
-d '{
"events": ["appointment.created", "appointment.cancelled", "invoice.paid", "payment.received"],
"active": true
}'Example Response
{
"success": true,
"data": {
"id": "wh_abc123def456ghi789",
"url": "https://myapp.com/webhooks/odento",
"events": ["appointment.created", "appointment.cancelled", "invoice.paid", "payment.received"],
"active": true,
"updated_at": "2025-01-22T12:00:00Z"
}
}/webhooks/{id}Delete a webhook
Permanently deletes a webhook endpoint. Pending deliveries are cancelled. This action cannot be undone.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Required | The webhook ID (wh_*) |
Example Request
curl -X DELETE https://api.odento.app/api/v1/webhooks/wh_abc123def456ghi789 \
-H "X-API-Key-ID: ok_live_abc123def456ghi789jkl012" \
-H "X-API-Key-Secret: os_abc123def456ghi789jkl012mno345pqr678stu901vwx234yzab567"Example Response
{
"success": true,
"data": {
"id": "wh_abc123def456ghi789",
"deleted": true
}
}/webhooks/{id}/rotate-secretRotate signing secret
Generates a new signing secret for the webhook. The old secret immediately stops working. Update your verification logic before rotating.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Required | The webhook ID (wh_*) |
Example Request
curl -X POST https://api.odento.app/api/v1/webhooks/wh_abc123def456ghi789/rotate-secret \
-H "X-API-Key-ID: ok_live_abc123def456ghi789jkl012" \
-H "X-API-Key-Secret: os_abc123def456ghi789jkl012mno345pqr678stu901vwx234yzab567"Example Response
{
"success": true,
"data": {
"id": "wh_abc123def456ghi789",
"signing_secret": "whsec_new987xyz654abc321def098mno765pqr432",
"rotated_at": "2025-01-22T12:30:00Z"
}
}Was this page helpful?
Help us improve our documentation