Manage Outgoing Webhooks
Manage outgoing webhook endpoints over the API instead of the Open API → Outgoing Webhooks tab in the CMS. This is the same set of capabilities — create an endpoint, choose the events it subscribes to (optionally filtered to specific walls, forms or achievements, or to completion types), configure authentication and custom headers, send a test delivery, and revoke or deactivate it — so external systems can set up integrations without a human in the CMS.
For the catalog of events and their delivery payloads, see Outgoing Webhooks.
Required scope: read:webhooks, write:webhooks.
Endpoints
/api/open/v1/webhooksList your webhook endpoints
/api/open/v1/webhooks/{id}Get a single endpoint
/api/open/v1/webhooks/{id}/secretReveal the full signing secret
/api/open/v1/webhooksCreate an endpoint
/api/open/v1/webhooks/{id}Update an endpoint
/api/open/v1/webhooks/{id}/revokeRevoke an endpoint (permanent)
/api/open/v1/webhooks/{id}/toggleActivate or deactivate an endpoint
/api/open/v1/webhooks/{id}/pingSend a test delivery
Create
curl -X POST https://customer.monotree.com/api/open/v1/webhooks \
-H "Authorization: Bearer mono_your_token_here" \
-H "Content-Type: application/json" \
-d '{
"name": "My integration",
"url": "https://example.com/monotree/webhook",
"events": ["monotree.post.created", "monotree.user.registered"],
"filters": { "monotree.post.created": [1, 3] },
"auth_type": "bearer_token",
"auth_value": "your-receiving-endpoint-token",
"custom_headers": { "X-Tenant": "acme" }
}'Request body
| Field | Type | Notes |
|---|---|---|
name | string | Required. A label for the endpoint, at most 255 characters. |
url | string | Required. The URL deliveries are sent to, at most 2048 characters. It must use https, carry no credentials, and its host must resolve to public addresses only; otherwise 422. |
events | array of strings | Required. One or more event names. An unrecognised event returns 422. |
filters | object | Optional. Restricts entity-scoped events to specific entity IDs, and completions to specific types — see Filtering events. |
auth_type | string | Optional. One of bearer_token, basic_auth, api_key. See Authenticating deliveries. |
auth_value | string | Optional. The token, credentials, or API key value for the chosen auth_type, at most 2048 characters. |
auth_header | string | The header name for api_key auth (e.g. X-Api-Key), at most 255 characters. Send it whenever auth_type is api_key: the API accepts an api_key endpoint without it, but every delivery to that endpoint then fails. |
custom_headers | object | Optional. Extra headers sent with every delivery, as a { "Header-Name": "value" } map. Each value is a string of at most 2048 characters. A custom header with the same name as the auth header replaces it. |
A new endpoint is active immediately and is issued a signing secret. The response masks the secret — fetch the full value once with GET /webhooks/{id}/secret.
Filtering events
Some events are tied to an entity and can be narrowed to specific instances by passing their IDs under filters, keyed by event name. An event with no filter (or a non-filterable event) fires for all matching activity.
| Event | Filter by |
|---|---|
monotree.post.created | Wall IDs |
monotree.comment.created, monotree.comment.updated, monotree.comment.deleted | Wall IDs |
monotree.formresponse.created, monotree.formresponse.updated | Form IDs |
monotree.formresponse.comment.created, monotree.formresponse.comment.deleted | Form IDs |
monotree.formresponse.alarm_triggered | Form IDs |
monotree.achievement.awarded | Achievement IDs |
monotree.completion.created | Completion types: course, journey, handbook |
Each ID must be an integer, or the request returns 422. IDs are not checked against existing walls, forms or achievements, and filters for an event that is not in events are ignored. Their values are still validated, so an invalid value still returns 422.
monotree.completion.created is filtered on type instead of on IDs. Pass one or more of course, journey and handbook; leave the filter out to receive all three. Any other value, including an ID, returns 422:
{
"events": ["monotree.completion.created"],
"filters": { "monotree.completion.created": ["course", "journey"] }
}All other events (e.g. monotree.user.registered, monotree.onboarding.completed, monotree.announcement.published) are not entity-scoped; any filters supplied for them are ignored, though each value must still be an integer or the request returns 422.
Authenticating deliveries
Beyond the HMAC signature (see Outgoing Webhooks), you can have Monotree add an auth header to every delivery:
auth_type | Header sent | auth_value | auth_header |
|---|---|---|---|
bearer_token | Authorization: Bearer <auth_value> | the token | — |
basic_auth | Authorization: Basic <base64(auth_value)> | user:password | — |
api_key | <auth_header>: <auth_value> | the key | the header name (required) |
Update
PUT accepts the same fields as create; only what you send is changed, with two exceptions:
- Events and filters are replaced together. Sending
eventsreplaces the endpoint's event rules with the events andfiltersin the same request;eventswithoutfiltersremoves any filters.filterssent withouteventsis ignored. - Authentication is replaced as a set, and only when the request contains
auth_type. Sendauth_type,auth_valueand, forapi_key,auth_headertogether.auth_valueorauth_headersent withoutauth_typeis ignored, andauth_typesent alone clears the stored value. Send"auth_type": nullto remove authentication.
Sending custom_headers replaces all custom headers; send null to remove them.
curl -X PUT https://customer.monotree.com/api/open/v1/webhooks/42 \
-H "Authorization: Bearer mono_your_token_here" \
-H "Content-Type: application/json" \
-d '{ "events": ["monotree.announcement.published"] }'Response
{
"data": {
"id": 42,
"name": "My integration",
"url": "https://example.com/monotree/webhook",
"is_active": true,
"events": ["monotree.post.created", "monotree.user.registered"],
"filters": { "monotree.post.created": [1, 3] },
"auth_type": "bearer_token",
"custom_headers": { "X-Tenant": "••••" },
"secret": "a1b2c3d4...",
"total_deliveries": 0,
"last_used_at": null,
"revoked_at": null,
"created_at": "2026-06-09T10:00:00+02:00",
"updated_at": "2026-06-09T10:00:00+02:00"
}
}| Field | Type | Notes |
|---|---|---|
events | array | Distinct event names the endpoint subscribes to. |
filters | object | Entity IDs per event, for entity-scoped events only. For monotree.completion.created the values are type tokens (strings) instead of IDs. |
custom_headers | object or null | Header names only — values are masked as ••••. |
secret | string | A hint (first 8 characters). Fetch the full secret with GET /webhooks/{id}/secret. |
is_active | boolean | false when deactivated with toggle or revoked. A revoked endpoint receives no event deliveries even if this is true, so check revoked_at as well. |
total_deliveries | integer | An approximate usage counter of the deliveries sent to the endpoint, pings included. Don't rely on it as an exact count. |
last_used_at | datetime or null | When a delivery (pings included) was last sent to the endpoint. null if none has been sent. |
revoked_at | datetime or null | When the endpoint was revoked, if ever. A non-null value means the endpoint receives no event deliveries. |
Revealing the secret
Returns the full HMAC signing secret so you can verify delivery signatures.
curl https://customer.monotree.com/api/open/v1/webhooks/42/secret \
-H "Authorization: Bearer mono_your_token_here"{ "data": { "secret": "a1b2c3d4e5f6..." } }Activate / deactivate
POST /webhooks/{id}/toggle flips is_active. A deactivated endpoint stops receiving event deliveries but keeps its configuration and history, so you can switch it back on later. Toggling does not undo a revoke: on a revoked endpoint it still flips is_active, but the endpoint stays revoked and receives no events.
Revoke
POST /webhooks/{id}/revoke permanently deactivates the endpoint: it sets is_active to false and records revoked_at. An endpoint receives event deliveries only when is_active is true and revoked_at is null, and nothing undoes a revoke, neither toggle nor PUT. The record is kept and its delivery logs are kept for 90 days; there is no hard delete — this mirrors the CMS.
An endpoint also stops with the token that created it: revoking or deleting the token revokes the endpoint, and while the token is expired the endpoint receives no event deliveries. Endpoints created in the CMS, and endpoints that already existed before v0.59.0, keep running whatever the state of a token.
Test delivery
POST /webhooks/{id}/ping sends one signed delivery to the endpoint URL right away, using its current secret, auth, and custom headers. It is sent once, without retries, and works on a deactivated or revoked endpoint too. The body is the usual envelope with "event": "monotree.test" and "payload": { "message": "This is a test webhook." }.
The call answers 200 whatever the receiver did, so read ok:
{ "ok": true, "status": "delivered" }{ "ok": false, "status": "failed", "status_code": 500, "error": "The receiver answered HTTP 500" }| Field | Notes |
|---|---|
ok | true when the receiver answered 2xx. |
status | delivered or failed. |
status_code | Only on a failure: the receiver's HTTP status, or null when it gave none (timeout, refused connection, refused URL). Left out when the test could not be sent at all because of an error on Monotree's side; error then says so. |
error | Only on a failure: the reason, for example The receiver answered HTTP 500, The receiver could not be reached, a connection error, a refused URL such as The webhook url must point at a public address., or No delivery result was recorded when the attempt left no result to read (status_code is then null). |