Create a webhook endpoint
The response holds the endpoint's signing secret (whsec_…): the only time it is shown. Public HTTPS addresses only (400 webhook_url_not_allowed for http, private or reserved addresses, names that resolve to one, mail ports, or a user name or password in the URL). The plan limits how many endpoints a project has (402 plan_limit). 60 changes per minute per project.
/v1/webhooksRequest
| Method and path | POST/v1/webhooks |
|---|---|
| Base URL | https://api.boxline.dev |
| Authentication | Your API key in the x-api-key header (or Authorization: Bearer). The examples read it from BOXLINE_API_KEY. |
| Rate limit | Yes. See Rate limits for the numbers and headers. |
Body
JSON with content-type: application/json. Required.
| Name | Type | Default | Allowed values | Description |
|---|---|---|---|---|
urlrequired | string | – | – | Up to 2048 characters. Format: uri. |
eventsrequired | string[] | – | – | 1 to 100 items. |
description | string | – | – | Up to 200 characters. |
curl -X POST "https://api.boxline.dev/v1/webhooks" \ -H "x-api-key: $BOXLINE_API_KEY" \ -H "content-type: application/json" \ -d '{ "url": "https://example.com/webhooks/boxline", "events": ["session.stopped", "agent_run.finished"] }'import { Boxline } from "@boxline/sdk";const bx = new Boxline(); // reads BOXLINE_API_KEY and BOXLINE_API_URLconst endpoint = await bx.webhooks.create({ url: "https://example.com/webhooks/boxline", events: ["session.stopped", "agent_run.finished"],});// endpoint.secret is shown only now: store it with your other secrets.console.log(endpoint.id, endpoint.events);from boxline import Boxlinebx = Boxline() # reads BOXLINE_API_KEY and BOXLINE_API_URLendpoint = bx.webhooks.create("https://example.com/webhooks/boxline", ["session.stopped", "agent_run.finished"])# endpoint["secret"] is shown only now: store it with your other secrets.print(endpoint["id"], endpoint["events"])Responses
201 Created
The endpoint, with its signing secret (shown once).
Headers
RateLimit-Limit: Requests allowed in the current window of this route's limit. (Rate limits)RateLimit-Remaining: Requests left in the current window. (Rate limits)RateLimit-Reset: Seconds until the window starts again. (Rate limits)
Fields
| Name | Type | Default | Allowed values | Description |
|---|---|---|---|---|
id | string | – | – | |
url | string | – | – | Format: uri. |
events | string[] | – | – | The types it gets, or ["*"] for all of them. |
description | string | null | – | – | |
enabled | boolean | – | – | |
disabledReason | string | null | – | user, gone, failing, account_recovered, null | user: switched off with PATCH; gone: it answered 410; failing: 3 days of failed deliveries; account_recovered: a password reset recovered an account whose email was not confirmed. null while enabled. |
disabledAt | string | null | – | – | Format: date-time. |
failingSince | string | null | – | – | The first failed delivery since the last success. Format: date-time. |
lastSuccessAt | string | null | – | – | Format: date-time. |
lastFailureAt | string | null | – | – | Format: date-time. |
secretRotatedAt | string | null | – | – | Format: date-time. |
previousSecretExpiresAt | string | null | – | – | Until then deliveries are signed with the secret before the last rotation too. Format: date-time. |
pausedUntil | string | null | – | – | Deliveries wait until then because the endpoint timed out 3 times in a row (test events still go). Format: date-time. |
createdAt | string | – | – | Format: date-time. |
updatedAt | string | – | – | Format: date-time. |
secret | string | – | – | The signing secret, whsec_…. Shown only in this response. |
{ "id": "wh_Hk2Lq8Zx0Vb5Nm1C", "url": "https://example.com/", "events": [ "session.started" ], "description": "…", "enabled": false, "disabledReason": "user", "disabledAt": "2026-09-29T10:00:00.000Z", "failingSince": "2026-09-29T10:00:00.000Z", "lastSuccessAt": "2026-09-29T10:00:00.000Z", "lastFailureAt": "2026-09-29T10:00:00.000Z", "secretRotatedAt": "2026-09-29T10:00:00.000Z", "previousSecretExpiresAt": "2026-09-29T10:00:00.000Z", "pausedUntil": "2026-09-29T10:00:00.000Z", "createdAt": "2026-09-29T10:00:00.000Z", "updatedAt": "2026-09-29T10:00:00.000Z", "secret": "whsec_…"}Errors
Errors have a matching HTTP status and the same body. code is stable, so check it in your code; message is for people. See Errors and request ids.
| Status | Codes | When |
|---|---|---|
| 400 Bad Request | invalid_request, invalid_json, invalid_cursor, invalid_idempotency_key, invalid_extension, variables_with_extensions, terms_not_accepted, invalid_reset_token, invalid_confirmation_code, wrong_password, no_password, invalid_next, invalid_oauth_token | The request is not valid. |
| 401 Unauthorized | unauthorized | Missing or invalid API key. |
| 402 Payment Required | feature_not_in_plan, spend_limit, out_of_credit, plan_limit, spend_limit, out_of_credit, organization, project, plan, credit | The plan does not include this, a monthly allowance or spending limit is used up, the organization has no credit left, or the plan allows no more of this. spend_limit and out_of_credit carry error.details.limit: organization, project, plan or credit. |
| 429 Too Many Requests | rate_limited, concurrency_limit | Too many requests, or the organization's concurrent sessions (its plan's) or the project's concurrencyCap are in use. |
| 503 Service Unavailable | no_capacity, proxy_unavailable, provider_unavailable, search_unavailable, webhooks_unavailable, mail_unavailable, browser_unreachable | No machine free in time, proxies not set up, the model provider is not configured or has no key, or web search is not set up or its provider is not answering, or webhook delivery is not set up, or an email cannot be sent, or the session's browser did not answer in time. |
{ "error": { "code": "not_found", "message": "session not found", "requestId": "req_4fJx0aQm2Lk9Pz7N" }}Learn more
These docs describe a beta API. Endpoints, fields and SDK methods may change before general availability; breaking changes will be listed in the changelog.