API reference / Webhooks
Update a webhook endpoint
PATCH/v1/webhook-endpoints/{id}
API key required
Changes the URL, label, subscribed event types or enabled state. Re-enabling an endpoint resets its failure counter and resumes its pending deliveries. The environment cannot be changed.
Authentication#
Send a secret API key as Authorization: Bearer <key>. Both tp_test_… and tp_live_… keys work; the key decides which environment you read and write. See Authentication.
Parameters#
Path
| Name | Type | Description |
|---|---|---|
idrequired | string | Identifier of the webhook endpoint.Pattern ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ |
Request body#
urlstringPublic HTTPS URL. In sandbox only,
http://localhostis accepted for local development.min length: 1 · max length: 2048
Example
"https://example.com/webhooks/tilt-pay"descriptionstring | nullFree-form label.
max length: 200
Example
"Production backend"event_typesarray of stringEvent types delivered to this endpoint, or
["*"]for all of them. Duplicates are removed.One of
user.createduser.suspendeduser.unsuspendedkyc.approvedkyc.rejectedtransaction.receivedtransaction.completedwithdrawal.completedwithdrawal.failed*enabledbooleanEnable or disable the endpoint.
Example
true
{
"event_types": [
"*"
],
"enabled": true
}Responses#
200The updated endpoint.
Headers
X-Request-IdRequest identifier.X-RateLimit-LimitQuota of the current window.X-RateLimit-RemainingRequests remaining in the window.X-RateLimit-ResetSeconds before the quota renews.
Body
idstringrequiredOpaque identifier.
Example
"0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"objectstringrequiredalways "webhook_endpoint"
Example
"webhook_endpoint"urlstringrequiredHTTPS URL that receives the events.
Example
"https://example.com/webhooks/tilt-pay"descriptionstring | nullrequiredFree-form label.
Example
"Production backend"event_typesarray of stringrequiredEvent types delivered to this endpoint, or
["*"]for all of them. Duplicates are removed.One of
user.createduser.suspendeduser.unsuspendedkyc.approvedkyc.rejectedtransaction.receivedtransaction.completedwithdrawal.completedwithdrawal.failed*enabledbooleanrequiredThe endpoint receives events.
Example
truedisabled_reasonstring | nullrequiredmanualwhen you disabled it,consecutive_failureswhen Tilt Pay disabled it after 20 failed attempts in a row.One of
manualconsecutive_failuresconsecutive_failuresintegerrequiredFailed attempts in a row (reset by a success).
Example
0secret_last4stringrequiredLast four characters of the current signing secret.
Example
"x9Yz"previous_secret_expires_atstring | nullrequiredDuring a rotation, until when the previous secret also signs events.
format: date-time
Example
"2026-10-08T09:30:00.000Z"livemodebooleanrequiredtruefor a live endpoint,falsefor a sandbox endpoint.Example
falsecreated_atstringrequiredCreation date.
format: date-time
Example
"2026-10-08T09:30:00.000Z"updated_atstringrequiredLast update.
format: date-time
Example
"2026-10-08T09:30:00.000Z"
{
"id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21",
"object": "webhook_endpoint",
"url": "https://example.com/webhooks/tilt-pay",
"description": "Production backend",
"event_types": [
"*"
],
"enabled": true,
"disabled_reason": null,
"consecutive_failures": 0,
"secret_last4": "x9Yz",
"previous_secret_expires_at": null,
"livemode": false,
"created_at": "2026-10-08T09:30:00.000Z",
"updated_at": "2026-10-08T09:30:00.000Z"
}400Invalid request (malformed JSON, unknown cursor, invalid Idempotency-Key header).
Example: invalid_cursor
{
"error": {
"type": "invalid_request_error",
"code": "invalid_cursor",
"message": "starting_after: unknown cursor",
"request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
}
}401Missing, invalid or revoked API key.
Example: missing_api_key
{
"error": {
"type": "authentication_error",
"code": "missing_api_key",
"message": "Provide your API key as \"Authorization: Bearer <key>\"",
"request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
}
}Example: invalid_api_key
{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "The API key is invalid",
"request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
}
}Example: api_key_revoked
{
"error": {
"type": "authentication_error",
"code": "api_key_revoked",
"message": "This API key has been revoked",
"request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
}
}403Organization suspended, live access not enabled, or sandbox-only route.
Example: organization_suspended
{
"error": {
"type": "permission_error",
"code": "organization_suspended",
"message": "Your organization is suspended. Contact Tilt Pay support.",
"request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
}
}Example: live_not_enabled
{
"error": {
"type": "permission_error",
"code": "live_not_enabled",
"message": "Live access has not been enabled for your organization yet",
"request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
}
}Example: sandbox_only
{
"error": {
"type": "permission_error",
"code": "sandbox_only",
"message": "This route is only available with a sandbox API key",
"request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
}
}404Resource not found (or belonging to another project or environment).
Example: user_not_found
{
"error": {
"type": "invalid_request_error",
"code": "user_not_found",
"message": "User not found",
"request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
}
}409Conflict with the state of the resource, or the original idempotent request is still in progress.
Example: user_already_exists
{
"error": {
"type": "invalid_request_error",
"code": "user_already_exists",
"message": "A user with this phone number already exists",
"request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
}
}422Invalid parameter, or Idempotency-Key reused with a different body.
Example: validation_error
{
"error": {
"type": "invalid_request_error",
"code": "validation_error",
"message": "phone: has an invalid format",
"request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
}
}Example: idempotency_key_reuse
{
"error": {
"type": "idempotency_error",
"code": "idempotency_key_reuse",
"message": "This Idempotency-Key was already used with a different request",
"request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
}
}429Rate limit exceeded. Retry after the Retry-After delay (seconds).
Example: rate_limit_exceeded
{
"error": {
"type": "rate_limit_error",
"code": "rate_limit_exceeded",
"message": "Too many requests. Slow down and retry after the delay in Retry-After.",
"request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
}
}500Internal error. Retry; give request_id to support if it persists.
Example: internal_error
{
"error": {
"type": "api_error",
"code": "internal_error",
"message": "Internal server error",
"request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
}
}Headers on every response
X-Request-IdIdentifier of the request, also present in error bodies. Give it to support.X-API-VersionVersion of the API that answered.X-RateLimit-Limit / -Remaining / -ResetQuota of your key for the current window. See Rate limits.
Errors#
Errors share one format, described in Errors. Branch your code on error.code.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_cursor | Invalid request (malformed JSON, unknown cursor, invalid Idempotency-Key header). |
| 401 | missing_api_key | Missing, invalid or revoked API key. |
| 401 | invalid_api_key | Missing, invalid or revoked API key. |
| 401 | api_key_revoked | Missing, invalid or revoked API key. |
| 403 | organization_suspended | Organization suspended, live access not enabled, or sandbox-only route. |
| 403 | live_not_enabled | Organization suspended, live access not enabled, or sandbox-only route. |
| 403 | sandbox_only | Organization suspended, live access not enabled, or sandbox-only route. |
| 404 | user_not_found | Resource not found (or belonging to another project or environment). |
| 404 | webhook_endpoint_not_found | Unknown endpoint (or belonging to another project or environment). |
| 409 | user_already_exists | Conflict with the state of the resource, or the original idempotent request is still in progress. |
| 409 | webhook_endpoint_exists | Another endpoint already uses this URL in this environment. |
| 422 | validation_error | Invalid parameter, or Idempotency-Key reused with a different body. |
| 422 | idempotency_key_reuse | Invalid parameter, or Idempotency-Key reused with a different body. |
| 422 | invalid_webhook_url | The URL is not acceptable: https is required (http://localhost is accepted in sandbox, outside production), private or internal addresses, credentials and sensitive ports are refused. |
| 429 | rate_limit_exceeded | Rate limit exceeded. Retry after the Retry-After delay (seconds). |
| 500 | internal_error | Internal error. Retry; give request_id to support if it persists. |