Skip to main content

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}
Why not API keys?

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 KeysWebhook Signing Secrets
DirectionYou → InfodeckInfodeck → You
PurposeAuthenticate your API callsVerify our webhook deliveries
HeaderX-Api-Key: idt_live_...x-infodeck-signature: t=...,v1=...
Who createsYou, in Settings → API KeysInfodeck, when you register a webhook
Who storesYou store the API keyYou store the signing secret
Who verifiesInfodeck verifies your keyYou verify our signature

Typical integration flow:

  1. An admin creates an API key in Settings → API Keys and a webhook in Settings → Webhooks (or via this API with a Bearer token)
  2. Your integration uses the API key to call Infodeck endpoints (GET /work-orders, POST /assets, etc.)
  3. When something changes in Infodeck, we deliver a webhook to your endpoint, signed with HMAC-SHA256
  4. Your endpoint verifies the x-infodeck-signature header 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

FieldTypeRequiredDescription
urlstringYesHTTPS endpoint URL (public, no private IPs)
eventsstring[]YesEvent types to subscribe to (1–20 items), or ["*"] for all
payloadModestringNothin (default, IDs only) or fat (full entity data)
descriptionstringNoHuman-readable description (max 200 chars)
metadataobjectNoCustom key-value pairs (max 10 keys)
filteringobjectNoOptional 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"
}
Store the signing secret immediately

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

StatusCodeDescription
400VALIDATION_ERRORInvalid URL, events format, or payload mode
401UNAUTHORIZEDMissing or invalid Bearer token
403FORBIDDENInsufficient permissions or plan does not include webhooks
409LIMIT_EXCEEDEDMaximum 25 webhooks per organization reached
422INVALID_URLURL must be a publicly accessible HTTPS endpoint

List Webhooks

Retrieve all webhooks for the organization.

GET /organizations/{organizationId}/webhooks

Query Parameters

ParameterTypeDescription
statusstringFilter by status: active, disabled, suspended
cursorstringPagination cursor from previous response
limitnumberResults 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

FieldTypeDescription
urlstringNew HTTPS endpoint URL
eventsstring[]Replace event subscriptions (1–20 items)
payloadModestringthin or fat
statusstringactive or disabled
descriptionstringUpdated description
metadataobjectReplace custom metadata
filteringobject or nullReplace 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.

Handling 204 in your code

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

StatusCodeDescription
401UNAUTHORIZEDMissing or invalid Bearer token
404NOT_FOUNDWebhook 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

ParameterTypeDescription
statusstringFilter: success or failed
eventTypestringFilter by event type (e.g., asset.created)
cursorstringPagination cursor
limitnumberResults 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

FieldTypeRequiredDescription
modestringYesgraceful (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 StatusMeaningCommon Cause
200SuccessRequest processed successfully
201CreatedWebhook registered successfully
202AcceptedAsync action queued (test, retry)
204No ContentDelete successful, no body returned
400Bad RequestInvalid request body or parameters
401UnauthorizedMissing, expired, or invalid Bearer token
403ForbiddenInsufficient permissions or plan limitation
404Not FoundWebhook or delivery ID does not exist
409ConflictOrganization webhook limit reached (25 max)
422UnprocessableURL blocked (private IP, localhost, non-HTTPS)
429Too Many RequestsRate limit exceeded

Webhook Limits

LimitValue
Webhooks per organization25
Events per webhook20
Metadata keys per webhook10
Metadata key length40 characters
Metadata value length500 characters
Description length200 characters
Delivery timeout30 seconds
Retry window24 hours with exponential backoff

Was this page helpful?