Update Webhook Endpoint
curl --request PATCH \
--url https://api.hopnow.io/v1/customers/{customer_id}/webhook-endpoints/{endpoint_id} \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"url": "<string>",
"events": [
{}
],
"description": "<string>"
}
'import requests
url = "https://api.hopnow.io/v1/customers/{customer_id}/webhook-endpoints/{endpoint_id}"
payload = {
"url": "<string>",
"events": [{}],
"description": "<string>"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({url: '<string>', events: [{}], description: '<string>'})
};
fetch('https://api.hopnow.io/v1/customers/{customer_id}/webhook-endpoints/{endpoint_id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.hopnow.io/v1/customers/{customer_id}/webhook-endpoints/{endpoint_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'url' => '<string>',
'events' => [
[
]
],
'description' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.hopnow.io/v1/customers/{customer_id}/webhook-endpoints/{endpoint_id}"
payload := strings.NewReader("{\n \"url\": \"<string>\",\n \"events\": [\n {}\n ],\n \"description\": \"<string>\"\n}")
req, _ := http.NewRequest("PATCH", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.patch("https://api.hopnow.io/v1/customers/{customer_id}/webhook-endpoints/{endpoint_id}")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"url\": \"<string>\",\n \"events\": [\n {}\n ],\n \"description\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hopnow.io/v1/customers/{customer_id}/webhook-endpoints/{endpoint_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"url\": \"<string>\",\n \"events\": [\n {}\n ],\n \"description\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"object": "<string>",
"url": "<string>",
"events": [
{}
],
"description": "<string>",
"created": "<string>",
"updated": "<string>",
"created_by": "<string>",
"updated_by": "<string>",
"last_used_at": "<string>",
"deleted_at": "<string>"
}Webhook Endpoints
Update Webhook Endpoint
Update an active webhook endpoint’s URL, events, or description
PATCH
/
v1
/
customers
/
{customer_id}
/
webhook-endpoints
/
{endpoint_id}
Update Webhook Endpoint
curl --request PATCH \
--url https://api.hopnow.io/v1/customers/{customer_id}/webhook-endpoints/{endpoint_id} \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"url": "<string>",
"events": [
{}
],
"description": "<string>"
}
'import requests
url = "https://api.hopnow.io/v1/customers/{customer_id}/webhook-endpoints/{endpoint_id}"
payload = {
"url": "<string>",
"events": [{}],
"description": "<string>"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({url: '<string>', events: [{}], description: '<string>'})
};
fetch('https://api.hopnow.io/v1/customers/{customer_id}/webhook-endpoints/{endpoint_id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.hopnow.io/v1/customers/{customer_id}/webhook-endpoints/{endpoint_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'url' => '<string>',
'events' => [
[
]
],
'description' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.hopnow.io/v1/customers/{customer_id}/webhook-endpoints/{endpoint_id}"
payload := strings.NewReader("{\n \"url\": \"<string>\",\n \"events\": [\n {}\n ],\n \"description\": \"<string>\"\n}")
req, _ := http.NewRequest("PATCH", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.patch("https://api.hopnow.io/v1/customers/{customer_id}/webhook-endpoints/{endpoint_id}")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"url\": \"<string>\",\n \"events\": [\n {}\n ],\n \"description\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hopnow.io/v1/customers/{customer_id}/webhook-endpoints/{endpoint_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"url\": \"<string>\",\n \"events\": [\n {}\n ],\n \"description\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"object": "<string>",
"url": "<string>",
"events": [
{}
],
"description": "<string>",
"created": "<string>",
"updated": "<string>",
"created_by": "<string>",
"updated_by": "<string>",
"last_used_at": "<string>",
"deleted_at": "<string>"
}Changes the URL, the subscription list, or the description of an active endpoint. Requires
write access on the webhooks scope. The signing secret is unaffected and is never returned.
Only fields you send are changed, and a field sent as null is ignored, not cleared. So a description cannot be removed once it is set; you can only replace it with different text. An empty body is accepted and returns the endpoint unchanged. A key this endpoint does not accept is rejected with 422.
Event names are validated before the endpoint is looked up, so an invalid event name on an unknown endpoint id returns 422, not 404.
Path Parameters
string
required
The customer’s external ID (starts with
cus_)string
required
The webhook endpoint’s external ID (starts with
wh_)Request Body
string
New delivery URL. Must use
https and include a host, and is normalised the same way as on create.array
Replacement subscription list. It replaces the whole list instead of adding to it, so send every event you still want. An empty array is accepted and leaves the endpoint subscribed to nothing; it keeps existing but receives no further deliveries.Valid names:
payin.received, payin.returned, payout.created, payout.completed, payout.failed, transfer.completed, transfer.failed, account.created, account.updated, payout_destination.updatedstring
New description (maximum 255 characters). Sending
null does not clear the existing one.Response
string
Webhook endpoint identifier (starts with
wh_)string
Always returns
"webhook_endpoint"string
Delivery URL, as stored after normalisation
array
Subscribed event types after the update
string
Description, or
nullstring
ISO 8601 timestamp when created
string
ISO 8601 timestamp when last updated
string
Identifier of the actor that created the endpoint, or
nullstring
Identifier of the actor that last updated the endpoint, or
nullstring
Reserved. Always
null. Hop does not record delivery times on the endpoint today.string
Deletion timestamp. Always
null here.Errors
| Status | code | When |
|---|---|---|
422 | VALIDATION_ERROR | The body carries a field this endpoint does not accept; validation_errors names it |
404 | RESOURCE_NOT_FOUND | No endpoint with this ID belongs to the customer, or it has been deleted |
422 | INVALID_FIELD_VALUE | events contains a name Hop does not emit; details.field is "events" and details.allowed_values lists the accepted set. Raised before the endpoint lookup |
422 | VALIDATION_ERROR | url is not an https URL with a host, or description is longer than 255 characters |
Request Example
{
"events": ["payout.created", "payout.completed", "payout.failed"],
"description": "Updated production webhook"
}
Response Example
{
"id": "wh_g738im3ueie9s9zexmf9cl87",
"object": "webhook_endpoint",
"url": "https://api.mycompany.com/webhooks/hopnow",
"events": ["payout.created", "payout.completed", "payout.failed"],
"description": "Updated production webhook",
"created": "2026-07-15T02:00:00Z",
"updated": "2026-07-16T02:00:00Z",
"created_by": null,
"updated_by": null,
"last_used_at": null,
"deleted_at": null
}