def create(
body: WebhookCreate,
*,
timeout: float | None = None,
) -> CreatedWebhookResourceasync def create(
body: WebhookCreate,
*,
timeout: float | None = None,
) -> CreatedWebhookResourceRegisters an HTTPS receiver and subscribes it to a set of events. The endpoint is created with enabled set to True, so any matching event emitted after this call is queued for it straight away. The delivery worker drains the queue on a 5 second tick, so the first request usually lands a moment after this call returns.
This response is the only place the API hands you the full secret. Every read gives you secretLastFour instead, so persist the secret before you do anything else. If you lose it you have two ways back: reveal it in the dashboard, which needs the webhooks:secret_read permission plus a step up verification, or call rotate_secret from here. Rotation is immediate and there is no overlap window, because each delivery is signed at send time from the endpoint's current secret, so queued deliveries and retries switch to the new secret too. The secret is formatted ev_whsec_ followed by base64url of 24 random bytes, and the HMAC key is the full string including that prefix: do not strip it, and if a verification library wants a base64 secret pass base64(utf8(secret)).
url is validated hard. It must be https, at most 2048 characters, free of embedded credentials, and pointed at a publicly routable host, which rules out loopback, private and carrier grade NAT ranges, link local and unique local IPv6, localhost, cloud metadata hosts and anything ending .internal, .local, .localdomain or .home.arpa. The host is checked again at delivery time: it is resolved and the attempt fails if any resolved address is private. Redirects are never followed, so a 3xx from your receiver is recorded as a failed attempt: register the final URL. eventTypes must be non empty and drawn from list_event_types, and a 400 request.body_invalid names each offending value.
Example
from envless import envless
created = envless.webhooks.create(
{
'name': 'production sync',
'url': 'https://hooks.example.com/envless',
'eventTypes': [
'variable.created',
'variable.updated',
],
}
)
print(created['secret'])
print(created['id'], created['secretLastFour'])import asyncio
from envless import AsyncEnvless
async def main() -> None:
async with AsyncEnvless() as envless:
created = await envless.webhooks.create(
{
'name': 'production sync',
'url': 'https://hooks.example.com/envless',
'eventTypes': [
'variable.created',
'variable.updated',
],
}
)
print(created['secret'])
print(created['id'], created['secretLastFour'])
asyncio.run(main())Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
body['name'] | str | Yes | Label shown in the dashboard and matched by list search, at most 64 characters. |
body['url'] | str | Yes | The https receiver URL, publicly routable and with no credentials in it. |
body['eventTypes'] | list[str] | Yes | Non empty list of event names taken from list_event_types. |
body['description'] | str | No | Free text note, at most 200 characters. |
timeout | float | No | Seconds each attempt of this call may take, the response body included, before it raises EnvlessNetworkError with is_timeout set. Overrides the client's timeout for this call, and 0 turns the limit off. |
Returns
CreatedWebhookResource, a WebhookResource plus the plaintext secret. Alongside it you get id, name, url, description, eventTypes, enabled, secretLastFour, lastSuccessAt, lastFailureAt, createdAt and updatedAt.
Worth knowing
- Verify every incoming request with
verify_webhook_signaturefrom the package root: it checks the HMAC over id, timestamp and body plus the timestamp drift, then returns the parsed event. - Delivery is at least once with up to 5 attempts, retrying 30s, 2m, 10m then 30m after the failed attempt, and a 10 second request timeout, so make the handler idempotent on the
webhook-idheader. - A workspace is capped at 20 endpoints, and creating the 21st is a 409
request.conflict. The separate 500 queued deliveries ceiling is enforced onredeliver, not on normal fan out. - An API key restricted to specific projects cannot call this at all: webhooks are workspace wide, so a project locked key gets 403
auth.key_project_restricted.
Required scopes
webhooks:write
Underlying endpoint
POST /webhooks
Request and response schema, every error code, and a live playground.
In TypeScript
envless.webhooks.create()
The same method in @goenvless/sdk, with the same request, retries and errors.