Developer docs
Add a webhook endpoint
Ask to send the account's transfer events to an HTTPS address: your own receiver, or a Slack or Microsoft Teams incoming webhook, which gets a readable message. The endpoint starts pending: it receives only test events, on a sample transfer, until the account owner confirms it on the website, and the owner is normally notified by email. The signing secret is returned once, here; deliveries carry Standard Webhooks headers signed with it. Only the account owner's API keys and connected apps can add endpoints.
POST
https://uetr.ai/api/v1/webhooks- MCP tool:
create_webhook_endpoint - Access: a key or connection with read and write access
- Only the account owner's keys and connections
Request body
| Field | Type | Required | Description |
|---|---|---|---|
url | string (at most 2048 characters) | Yes | The full HTTPS address. It must use a domain name and the standard port. |
events | array of status_changed, final_state, checks_ended (at least 1 item, at most 3 items) | No | The events to send. Defaults to all of them. |
Response
| Field | Type | Required | Description |
|---|---|---|---|
status | string | Yes | |
endpoint | object | Yes | |
endpoint.id | string (uuid) | Yes | |
endpoint.url | string | Yes | The address's host and a masked path. The full address is never shown. |
endpoint.destination | json, slack, teams | Yes | json: signed JSON. slack and teams: a readable message for a Slack or Microsoft Teams incoming webhook. |
endpoint.events | array of status_changed, final_state, checks_ended | Yes | |
endpoint.status | pending, active, paused | Yes | pending: waiting for the account owner to confirm it on the website; until then it receives test events only. active: receiving events. paused: deliveries stopped after sustained failures; a test event that succeeds resumes it. |
endpoint.paused_at | string or null | Yes | |
endpoint.created_at | string | Yes | |
endpoint.created_by | website, api | Yes | website: added on the website. api: added by an API key or a connected app. |
endpoint.api_version | string | Yes | |
signing_secret | string | Yes | The secret that signs every delivery, starting whsec_. It is shown only now: store it in the receiver's secret settings. |
request | object | Yes | |
request.id | string (uuid) | Yes | |
request.kind | team_invite, record_share, webhook_endpoint | Yes | team_invite: an invitation to the team. record_share: a payment record link. webhook_endpoint: a webhook endpoint that receives status changes. |
request.status | pending, confirmed, declined, expired | Yes | pending: waiting for the account owner. confirmed: the owner approved it and it took effect. declined: the owner refused it. expired: nobody answered within seven days, or a later request for the same transfer with different settings replaced it. |
request.created_at | string | Yes | |
request.expires_at | string | Yes | |
request.decided_at | string or null | Yes | |
request.invite_email | string or null | Yes | For a team invite: the address to invite. |
request.uetr | string or null | Yes | For a record share: the transfer's UETR. |
request.record_url | string or null | Yes | For a confirmed record share: the payment record link, while the record still has that link. Null otherwise. |
request.endpoint_url | string or null | Yes | For a webhook endpoint: its host and a masked path. Null otherwise. |
detail | string | Yes | Plain English to pass on to the person behind the request. |
Example
curl -X POST "https://uetr.ai/api/v1/webhooks" \
-H "Authorization: Bearer $UETR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://hooks.slack.com/services/T000/B000/XXXX","events":["status_changed"]}'The answer:
{
"status": "pending_owner_confirmation",
"request": {
"id": "4c7e1a3b-8d2f-4b6e-a9c5-3f6b9e2d5a8c",
"kind": "webhook_endpoint",
"status": "pending",
"created_at": "2026-10-14T09:35:00.000Z",
"expires_at": "2026-10-21T09:35:00.000Z",
"decided_at": null,
"invite_email": null,
"uetr": null,
"record_url": null,
"endpoint_url": "hooks.slack.com/services/••••"
},
"detail": "The account owner must confirm the webhook endpoint hooks.slack.com/services/•••• on the website before it takes effect. They have been notified by email.",
"endpoint": {
"id": "3b1f8c2e-6a4d-4f5e-9c7b-2d8e1a0f4b6c",
"url": "hooks.slack.com/services/••••",
"destination": "slack",
"events": [
"status_changed"
],
"status": "pending",
"paused_at": null,
"created_at": "2026-10-14T09:35:00.000Z",
"created_by": "api",
"api_version": "2026-10-14"
},
"signing_secret": "whsec_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
}Refusals
Besides the access refusals every call can meet, this call can answer:
read_only_credential(403): Read-only key or connection.machine_writes_disabled(503): Changes are switched off.request_too_large(413): Request too large.not_permitted(403): Not permitted.destination_not_allowed(400): Webhook address not accepted.endpoint_exists(409): Webhook address already registered.endpoint_limit(409): Webhook endpoint limit reached.