> The full uetr.ai documentation index is at https://uetr.ai/llms.txt, and every page under https://uetr.ai/docs/api is also Markdown at its address plus `.md`.

# Change a webhook endpoint's address

Send a webhook endpoint's events to a new HTTPS address, for example after the receiver moved. The endpoint keeps its id, events and signing secret, and returns to pending: until the account owner confirms the new address on the website it receives only test events, and the owner is normally notified by email. Deliveries still waiting for the old address are dropped. If the owner declines, or nobody confirms within 7 days, the endpoint is removed. Sending the address an endpoint is already waiting on answers with its waiting request again. A change counts toward the hourly limit on adding endpoints. Only the account owner's API keys and connected apps can change webhooks.

`PATCH https://uetr.ai/api/v1/webhooks/{id}/url`

- MCP tool: `change_webhook_endpoint_url`
- Access: a key or connection with read and write access
- Only the account owner's keys and connections

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string (uuid) | Yes | The webhook endpoint's id. |

## 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. |

## 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 |  |
| `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

```sh
curl -X PATCH "https://uetr.ai/api/v1/webhooks/3b1f8c2e-6a4d-4f5e-9c7b-2d8e1a0f4b6c/url" \
  -H "Authorization: Bearer $UETR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://receiver.example.com/hooks/uetr"}'
```

The answer:

```json
{
  "status": "pending_owner_confirmation",
  "request": {
    "id": "6e2a9c4d-1b7f-4e3a-8d5c-9f0b2e4a6c8d",
    "kind": "webhook_endpoint",
    "status": "pending",
    "created_at": "2026-10-15T09:40:00.000Z",
    "expires_at": "2026-10-22T09:40:00.000Z",
    "decided_at": null,
    "invite_email": null,
    "uetr": null,
    "record_url": null,
    "endpoint_url": "receiver.example.com/••••"
  },
  "detail": "The account owner must confirm the webhook endpoint receiver.example.com/•••• on the website before it takes effect. They have been notified by email.",
  "endpoint": {
    "id": "3b1f8c2e-6a4d-4f5e-9c7b-2d8e1a0f4b6c",
    "url": "receiver.example.com/••••",
    "destination": "json",
    "events": [
      "status_changed"
    ],
    "status": "pending",
    "paused_at": null,
    "created_at": "2026-10-14T09:35:00.000Z",
    "created_by": "api",
    "api_version": "2026-10-14"
  }
}
```

## Refusals

Besides the [access refusals](/docs/api/errors#access-refusals) every call can meet, this call can answer:

- [`read_only_credential`](/docs/api/errors#read_only_credential) (403): Read-only key or connection.
- [`machine_writes_disabled`](/docs/api/errors#machine_writes_disabled) (503): Changes are switched off.
- [`request_too_large`](/docs/api/errors#request_too_large) (413): Request too large.
- [`not_permitted`](/docs/api/errors#not_permitted) (403): Not permitted.
- [`not_found`](/docs/api/errors#not_found) (404): Not found.
- [`destination_not_allowed`](/docs/api/errors#destination_not_allowed) (400): Webhook address not accepted.
- [`endpoint_exists`](/docs/api/errors#endpoint_exists) (409): Webhook address already registered.