/webhooks/{id}Updates an endpoint's name, URL, description, subscriptions or enabled state. At least one of name, url, description, eventTypes, enabled must be present or the request is rejected with 400 - an empty object is not a no-op here.
eventTypes replaces the subscription set wholesale, unlike scopes on PATCH /keys/{id} which merges. Send the complete list you want, not a delta. It must stay non-empty; there is no way to subscribe to nothing, so disable the endpoint instead.
url is re-validated against the full https/public-host ruleset on every change. description: null or "" clears the note.
Disabling does not drain the queue. Deliveries already queued for a disabled endpoint are not cancelled: the worker picks them up, sees the endpoint is disabled and marks them failed with the error Endpoint disabled. Expect a burst of failures in the delivery log right after you disable. Nothing new is queued while enabled is false - events that occur during the downtime are lost to this endpoint, not buffered.
The secret is untouched by this route. Use POST /webhooks/{id}/rotate.
Refused for project-restricted keys.
Requires the webhooks:write scope.
curl --request PATCH \
--url 'https://api.envless.cloud/webhooks/<id>' \
--header 'Authorization: Bearer ev_sk_xxxxxxxxxxxx' \
--header 'Content-Type: application/json' \
--data '{
"name": "Billing",
"url": "https://example.com/hooks/envless",
"description": "<string>",
"eventTypes": [
"variable.created",
"variable.updated"
],
"enabled": true
}'https://api.envless.cloud/webhooks/{id}Response headers
Authorization
AuthorizationstringrequiredheaderBearer token: an Envless API key (
ev_sk_…), created in the dashboard or withPOST /keys. The key is bound to one workspace; every request runs inside it.
Path parameters
idstringrequireduuidThe endpoint's
id(UUIDv7). No slug form.
Request body
application/json · required
namestringmin length 1max length 64New label, trimmed, at most 64 characters.
urlstringurimax length 2048New target. Re-validated against the same https and public-host rules as create.
descriptionstring | nullmax length 200New note, at most 200 characters.
nullor""clears it.eventTypesstring[]min items 1REPLACES the subscription set. Must be non-empty and drawn entirely from
GET /webhooks/event-types.enabledbooleanfalsestops new deliveries being queued and causes any already-queued delivery to be marked failed withEndpoint disabled. Must be a boolean; anything else is a 400.
Responses
200OK
Update a webhook endpoint succeeded.
successbooleanrequiredcodeintegerrequiredThe HTTP status of this response, repeated in the body.
messagestringrequirederrornullrequireddataobjectrequiredShow child attributesHide child attributes
idstringrequirednamestring | nullurlstringrequireduridescriptionstring | nulleventTypesstring[]requiredenabledbooleanrequiredsecretLastFourstring | nullLast four characters of the signing secret. The full secret is returned only on create and rotate.
lastSuccessAtstring | nulldate-timelastFailureAtstring | nulldate-timecreatedAtstringrequireddate-timeupdatedAtstring | nulldate-time
_reqobjectShow child attributesHide child attributes
reqIdstringresTimeinteger | null
{
"success": true,
"code": 200,
"message": "OK",
"error": null,
"data": {
"id": "<string>",
"name": "<string>",
"url": "https://example.com",
"description": "<string>",
"eventTypes": [
"<string>"
],
"enabled": true,
"secretLastFour": "<string>",
"lastSuccessAt": "2026-05-30T10:14:22.000Z",
"lastFailureAt": "2026-05-30T10:14:22.000Z",
"createdAt": "2026-05-30T10:14:22.000Z",
"updatedAt": "2026-05-30T10:14:22.000Z"
},
"_req": {
"reqId": "<string>",
"resTime": 1
}
}Error envelope
Every failure below returns the same shape with success: false; branch on error.code, never on message.
successbooleanrequiredcodeintegerrequiredThe HTTP status of this response, repeated in the body.
messagestringrequirederrorobjectrequiredMachine-readable failure detail. Null on success.
Show child attributesHide child attributes
codeenum<string>requiredStable error code. Branch on this, never on
message.Options:
"auth.api_key_required","auth.api_key_not_found","auth.api_key_user_missing","auth.api_key_owner_not_member","auth.scope_missing","auth.scope_ceiling_exceeded","auth.key_project_restricted","auth.product_restricted","auth.permission_denied","auth.self_mutation_forbidden","auth.step_up_required","auth.step_up_fresh_required","auth.cli_token_not_accepted","auth.two_factor_required","auth.workspace_two_factor_required","auth.two_factor_invalid","auth.two_factor_replayed","auth.two_factor_locked","auth.two_factor_not_enabled","auth.two_factor_already_enabled","request.body_must_be_json","request.body_invalid","request.body_too_large","request.rate_limited","request.invalid_pagination","workspace.not_found","workspace.slug_conflict","workspace.limit_reached","workspace.delete_last","workspace.delete_too_early","workspace.delete_has_projects","domain.not_configured","domain.not_found","domain.invalid","domain.taken","domain.attempt_limit","product.not_found","product.slug_conflict","product.locked","project.not_found","project.ambiguous_product","project.slug_conflict","environment.not_found","environment.slug_conflict","environment.expose_disabled","environment.expose_private_product","variable.not_found","variable.name_conflict","variable.ciphertext_invalid","variable.baseline_drifted","version.not_found","member.not_found","member.already_exists","member.self_invite","member.self_mutation","member.owner_immutable","member.owner_cannot_leave","member.seat_limit","invite.already_pending","role.owner_not_assignable","role.limit_reached","role.immutable","role.in_use","invite.not_found","invite.expired","role.not_found","role.name_conflict","key.not_found","key.limit_reached","webhook.not_found","webhook.delivery_not_found","plan.limit_reached","plan.upgrade_required","request.not_found","request.method_not_allowed","request.conflict","auth.unauthorized","auth.forbidden","request.invalid","server.error"resourcestringThe resource or scope the failure relates to.
fieldstringThe request field that caused the failure.
dataobject | nullrequiredNull on most failures. A plan limit carries
{ limit, current }._reqobjectShow child attributesHide child attributes
reqIdstringresTimeinteger | null
{
"success": false,
"code": 400,
"message": "Something went wrong.",
"error": {
"code": "auth.api_key_required",
"resource": "<string>",
"field": "<string>"
},
"data": {},
"_req": {
"reqId": "<string>",
"resTime": 1
}
}400Bad Request
request.body_must_be_json- the body is absent or is not parseable JSON.request.body_invalid- none ofname,url,description,eventTypes,enabledis present;enabledis not a boolean;eventTypesis empty or contains a name outside the catalogue; orname,urlordescriptionfails its length or URL validation.
401Unauthorized
auth.api_key_required- theAuthorizationheader is missing, or its value does not start withev_sk_.auth.api_key_not_found- the key is unknown, inactive or revoked.auth.api_key_user_missing- the key is valid but its owner no longer exists.auth.api_key_owner_not_member- the key's owner is no longer a member of the key's workspace.
403Forbidden
auth.key_project_restricted- the calling key is fenced to specific projects.auth.workspace_two_factor_required- the workspace requires two-factor authentication and the key's owner has not turned it on.auth.scope_missing- the key lacks a scope this operation requires;error.resourcenames it.
404Not Found
webhook.not_found - no live endpoint with that id exists in this workspace.
413Payload Too Large
request.body_too_large - the request body is larger than 1 MiB (1,048,576 bytes).
500Internal Server Error
server.error - an unexpected error; retry with backoff.
503Service Unavailable
server.error - the API is in maintenance mode or is missing its configuration; retry with backoff.