Webhook Management API
Manage webhook subscriptions programmatically using the REST API. Create endpoints, update event subscriptions, review delivery logs, retry failed deliveries, and rotate signing secrets.
Authentication
Webhook management endpoints require a Bearer token (JWT). API keys cannot be used to create, update, or delete webhooks.
Authorization: Bearer {token}
Webhook management controls what data leaves your organization. Restricting it to authenticated users (not API keys) ensures only authorized team members can change where events are delivered. Your API keys are designed for reading and writing operational data, not for configuring integrations.
How API Keys and Webhooks Work Together
API keys and webhook signing secrets serve different purposes and travel in opposite directions:
| API Keys | Webhook Signing Secrets | |
|---|---|---|
| Direction | You → Infodeck | Infodeck → You |
| Purpose | Authenticate your API calls | Verify our webhook deliveries |
| Header | X-Api-Key: idt_live_... | x-infodeck-signature: t=...,v1=... |
| Who creates | You, in Settings → API Keys | Infodeck, when you register a webhook |
| Who stores | You store the API key | You store the signing secret |
| Who verifies | Infodeck verifies your key | You verify our signature |
Typical integration flow:
- An admin creates an API key in Settings → API Keys and a webhook in Settings → Webhooks (or via this API with a Bearer token)
- Your integration uses the API key to call Infodeck endpoints (
GET /work-orders,POST /assets, etc.) - When something changes in Infodeck, we deliver a webhook to your endpoint, signed with HMAC-SHA256
- Your endpoint verifies the
x-infodeck-signatureheader using the signing secret you stored at creation
Base URL
https://app.infodeck.io/api/v2/organizations/{organizationId}/webhooks
Create Webhook
Register a new endpoint to receive webhook events.
POST /organizations/{organizationId}/webhooks
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | HTTPS endpoint URL (public, no private IPs) |
events | string[] | Yes | Event types to subscribe to (1–20 items), or ["*"] for all |
payloadMode | string | No | thin (default, IDs only) or fat (full entity data) |
description | string | No | Human-readable description (max 200 chars) |
metadata | object | No | Custom key-value pairs (max 10 keys) |
filtering | object | No | Optional include-list filters. Use mode: "include" and curated ID fields such as assetId, locationId, siteId, or workOrderId |
Example
curl -X POST https://app.infodeck.io/api/v2/organizations/{orgId}/webhooks \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks/infodeck",
"events": ["workorder.created", "workorder.status.changed", "asset.status.changed"],
"payloadMode": "thin",
"description": "CMMS sync - work orders and assets",
"filtering": {
"mode": "include",
"criteria": {
"locationId": ["loc_hq_01"]
}
}
}'
Response 201 Created
{
"data": {
"webhookId": "whk_k7x9m2nq4b",
"url": "https://your-server.com/webhooks/infodeck",
"events": ["workorder.created", "workorder.status.changed", "asset.status.changed"],
"payloadMode": "thin",
"status": "active",
"description": "CMMS sync - work orders and assets",
"filtering": {
"mode": "include",
"criteria": {
"locationId": ["loc_hq_01"]
}
},
"signingSecret": "whsec_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
"createdAt": 1771911526000
},
"requestId": "req_abc123"
}
The signingSecret is returned only once at creation. It cannot be retrieved later. If you lose it, rotate the secret to get a new one.
Error Responses
| Status | Code | Description |
|---|---|---|
400 | VALIDATION_ERROR | Invalid URL, events format, or payload mode |
401 | UNAUTHORIZED | Missing or invalid Bearer token |
403 | FORBIDDEN | Insufficient permissions or plan does not include webhooks |
409 | LIMIT_EXCEEDED | Maximum 25 webhooks per organization reached |
422 | INVALID_URL | URL must be a publicly accessible HTTPS endpoint |
List Webhooks
Retrieve all webhooks for the organization.
GET /organizations/{organizationId}/webhooks
Query Parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status: active, disabled, suspended |
cursor | string | Pagination cursor from previous response |
limit | number | Results per page (default 25, max 100) |
Example
curl https://app.infodeck.io/api/v2/organizations/{orgId}/webhooks \
-H "Authorization: Bearer {token}"
Response 200 OK
{
"data": [
{
"webhookId": "whk_k7x9m2nq4b",
"url": "https://your-server.com/webhooks/infodeck",
"events": ["workorder.created", "workorder.status.changed"],
"payloadMode": "thin",
"status": "active",
"description": "CMMS sync",
"consecutiveFailures": 0,
"lastDeliveryAt": 1771915126000,
"createdAt": 1771911526000,
"updatedAt": 1771915126000
}
],
"meta": {
"cursor": "cGFnZTI...",
"hasMore": false
},
"requestId": "req_def456"
}
Get Webhook
Retrieve a single webhook by ID.
GET /organizations/{organizationId}/webhooks/{webhookId}
Example
curl https://app.infodeck.io/api/v2/organizations/{orgId}/webhooks/whk_k7x9m2nq4b \
-H "Authorization: Bearer {token}"
Response 200 OK
Returns the same webhook object as the list endpoint, with full details including lastFailureAt and lastFailureReason if applicable.
Update Webhook
Update a webhook's URL, events, status, or other configuration. Only include the fields you want to change.
PATCH /organizations/{organizationId}/webhooks/{webhookId}
Request Body
| Field | Type | Description |
|---|---|---|
url | string | New HTTPS endpoint URL |
events | string[] | Replace event subscriptions (1–20 items) |
payloadMode | string | thin or fat |
status | string | active or disabled |
description | string | Updated description |
metadata | object | Replace custom metadata |
filtering | object or null | Replace filtering, or send null to clear it |
Example: Disable a Webhook
curl -X PATCH https://app.infodeck.io/api/v2/organizations/{orgId}/webhooks/whk_k7x9m2nq4b \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{ "status": "disabled" }'
Example: Change Subscribed Events
curl -X PATCH https://app.infodeck.io/api/v2/organizations/{orgId}/webhooks/whk_k7x9m2nq4b \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"events": ["workorder.created", "workorder.completed", "asset.created"]
}'
Example: Add or Replace Filtering
curl -X PATCH https://app.infodeck.io/api/v2/organizations/{orgId}/webhooks/whk_k7x9m2nq4b \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"filtering": {
"mode": "include",
"criteria": {
"assetId": ["ast_ahu_01"],
"locationId": ["loc_hq_l1", "loc_hq_l2"]
}
}
}'
Example: Clear Filtering
curl -X PATCH https://app.infodeck.io/api/v2/organizations/{orgId}/webhooks/whk_k7x9m2nq4b \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{ "filtering": null }'
Response 200 OK
Returns the updated webhook object.
Delete Webhook
Remove a webhook subscription. This is a soft delete — the webhook stops receiving events immediately, and delivery logs are retained.
DELETE /organizations/{organizationId}/webhooks/{webhookId}
Example
curl -X DELETE https://app.infodeck.io/api/v2/organizations/{orgId}/webhooks/whk_k7x9m2nq4b \
-H "Authorization: Bearer {token}"
Response 204 No Content
A successful delete returns 204 with no response body. This is standard REST behavior — the webhook has been deactivated and there is nothing to return.
If your HTTP client expects a JSON body, a 204 response may cause a parse error. Handle it explicitly:
Node.js (axios):
const response = await axios.delete(
`https://app.infodeck.io/api/v2/organizations/${orgId}/webhooks/${webhookId}`,
{ headers: { Authorization: `Bearer ${token}` } }
);
// response.status === 204
// response.data is empty — do not try to parse it
Python (requests):
response = requests.delete(
f"https://app.infodeck.io/api/v2/organizations/{org_id}/webhooks/{webhook_id}",
headers={"Authorization": f"Bearer {token}"}
)
# response.status_code == 204
# response.text is empty — do not call response.json()
cURL:
# -w shows the status code so you can confirm 204
curl -X DELETE https://app.infodeck.io/api/v2/organizations/{orgId}/webhooks/{webhookId} \
-H "Authorization: Bearer {token}" \
-w "\nHTTP Status: %{http_code}\n"
Error Responses
| Status | Code | Description |
|---|---|---|
401 | UNAUTHORIZED | Missing or invalid Bearer token |
404 | NOT_FOUND | Webhook does not exist or was already deleted |
Test Webhook
Send a test event to verify your endpoint is reachable and processing events correctly.
POST /organizations/{organizationId}/webhooks/{webhookId}/test
Example
curl -X POST https://app.infodeck.io/api/v2/organizations/{orgId}/webhooks/whk_k7x9m2nq4b/test \
-H "Authorization: Bearer {token}"
Response 202 Accepted
{
"data": {
"message": "Test event queued for delivery"
},
"requestId": "req_ghi789"
}
Your endpoint will receive a webhook.test event within a few seconds. Check your server logs or webhook.site to confirm receipt.
webhook.test is delivered even when the webhook has filters configured. It is a connectivity check, not a filter match simulation.
List Delivery Logs
View the delivery history for a webhook, including successful and failed attempts.
GET /organizations/{organizationId}/webhooks/{webhookId}/deliveries
Query Parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter: success or failed |
eventType | string | Filter by event type (e.g., asset.created) |
cursor | string | Pagination cursor |
limit | number | Results per page (default 25, max 100) |
Example
curl "https://app.infodeck.io/api/v2/organizations/{orgId}/webhooks/whk_k7x9m2nq4b/deliveries?status=failed" \
-H "Authorization: Bearer {token}"
Response 200 OK
{
"data": [
{
"deliveryId": "dlv_abc123",
"webhookId": "whk_k7x9m2nq4b",
"eventType": "workorder.created",
"eventId": "evt_xyz789",
"attempt": 3,
"status": "failed",
"statusCode": 500,
"responseTimeMs": 2340,
"failureReason": "Server returned 500 Internal Server Error",
"deliveredAt": 1771915126000
}
],
"meta": {
"cursor": "cGFnZTI...",
"hasMore": true
},
"requestId": "req_jkl012"
}
Retry Failed Delivery
Re-enqueue a specific failed delivery for another attempt.
POST /organizations/{organizationId}/webhooks/{webhookId}/deliveries/{deliveryId}/retry
Example
curl -X POST https://app.infodeck.io/api/v2/organizations/{orgId}/webhooks/whk_k7x9m2nq4b/deliveries/dlv_abc123/retry \
-H "Authorization: Bearer {token}"
Response 202 Accepted
{
"data": {
"message": "Delivery re-queued for retry"
},
"requestId": "req_mno345"
}
Rotate Signing Secret
Replace the signing secret used to sign webhook deliveries. Use graceful mode for planned rotations and immediate mode for emergencies.
POST /organizations/{organizationId}/webhooks/{webhookId}/rotate-secret
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
mode | string | Yes | graceful (24-hour overlap) or immediate (old secret invalidated instantly) |
Example: Graceful Rotation
curl -X POST https://app.infodeck.io/api/v2/organizations/{orgId}/webhooks/whk_k7x9m2nq4b/rotate-secret \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{ "mode": "graceful" }'
Response 200 OK
{
"data": {
"newSecret": "whsec_new_secret_value_here...",
"previousSecretExpiresAt": 1772000000,
"overlapHours": 24
},
"requestId": "req_pqr678"
}
During the 24-hour overlap period, Infodeck accepts both secrets for signature verification. Update your endpoint to use the new secret, then the old one expires automatically.
Common Error Codes
These error codes apply across all webhook management endpoints:
| HTTP Status | Meaning | Common Cause |
|---|---|---|
200 | Success | Request processed successfully |
201 | Created | Webhook registered successfully |
202 | Accepted | Async action queued (test, retry) |
204 | No Content | Delete successful, no body returned |
400 | Bad Request | Invalid request body or parameters |
401 | Unauthorized | Missing, expired, or invalid Bearer token |
403 | Forbidden | Insufficient permissions or plan limitation |
404 | Not Found | Webhook or delivery ID does not exist |
409 | Conflict | Organization webhook limit reached (25 max) |
422 | Unprocessable | URL blocked (private IP, localhost, non-HTTPS) |
429 | Too Many Requests | Rate limit exceeded |
Webhook Limits
| Limit | Value |
|---|---|
| Webhooks per organization | 25 |
| Events per webhook | 20 |
| Metadata keys per webhook | 10 |
| Metadata key length | 40 characters |
| Metadata value length | 500 characters |
| Description length | 200 characters |
| Delivery timeout | 30 seconds |
| Retry window | 24 hours with exponential backoff |
Related
- Webhooks Overview — How webhooks work
- Quick Start — Set up your first webhook in 5 minutes
- Event Catalog — Full payload reference for every event type
- Webhook Security — Verify signatures and rotate secrets
- Best Practices — Production-ready patterns
- API Authentication — Bearer tokens and API keys