Rotate a webhook secret
curl --request POST \
--url https://agp.eu.hcompany.ai/api/v2/webhooks/{webhook_id}/rotate \
--header 'Authorization: Bearer <token>'import requests
url = "https://agp.eu.hcompany.ai/api/v2/webhooks/{webhook_id}/rotate"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://agp.eu.hcompany.ai/api/v2/webhooks/{webhook_id}/rotate', 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://agp.eu.hcompany.ai/api/v2/webhooks/{webhook_id}/rotate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://agp.eu.hcompany.ai/api/v2/webhooks/{webhook_id}/rotate"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://agp.eu.hcompany.ai/api/v2/webhooks/{webhook_id}/rotate")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://agp.eu.hcompany.ai/api/v2/webhooks/{webhook_id}/rotate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_bodyWebhooks
Rotate a webhook secret
Replace the signing secret without missing a verification.
POST
/
api
/
v2
/
webhooks
/
{webhook_id}
/
rotate
Rotate a webhook secret
curl --request POST \
--url https://agp.eu.hcompany.ai/api/v2/webhooks/{webhook_id}/rotate \
--header 'Authorization: Bearer <token>'import requests
url = "https://agp.eu.hcompany.ai/api/v2/webhooks/{webhook_id}/rotate"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://agp.eu.hcompany.ai/api/v2/webhooks/{webhook_id}/rotate', 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://agp.eu.hcompany.ai/api/v2/webhooks/{webhook_id}/rotate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://agp.eu.hcompany.ai/api/v2/webhooks/{webhook_id}/rotate"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://agp.eu.hcompany.ai/api/v2/webhooks/{webhook_id}/rotate")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://agp.eu.hcompany.ai/api/v2/webhooks/{webhook_id}/rotate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_bodyReplaces the webhook’s signing secret. The response includes the new
secret; like Create, this is the only time it is returned, so store it securely. Deliveries are signed at send time, so events (including retries already in flight) are signed with the new secret from this point on.
Returns the webhook object plus its new secret.
Update your receiver to accept both the old and the new secret before calling this endpoint. The SDK verify helpers accept a list of secrets for exactly this overlap:
verify_webhook(body, sig, ts, ["whsec_old", "whsec_new"]). Remove the old secret once deliveries verify against the new one.Path parameters
The webhook’s
id (UUID).Examples
curl -X POST https://agp.eu.hcompany.ai/api/v2/webhooks/f47ac10b-58cc-4372-a567-0e02b2c3d479/rotate \
-H "Authorization: Bearer $HAI_API_KEY"
from hai_agents import Client
client = Client()
webhook = client.webhooks.rotate_webhook_secret(webhook_id="f47ac10b-58cc-4372-a567-0e02b2c3d479")
print(webhook.secret) # store it now; it is never returned again
import { HaiAgentsClient } from "hai-agents";
const client = new HaiAgentsClient();
const webhook = await client.webhooks.rotateWebhookSecret({ webhookId: "f47ac10b-58cc-4372-a567-0e02b2c3d479" });
console.log(webhook.secret); // store it now; it is never returned again
Response
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"url": "https://example.com/hooks/h",
"enabled_events": ["*"],
"description": "Production listener",
"disabled": false,
"last_delivery_status": "succeeded",
"last_delivery_error": null,
"last_delivery_at": "2026-07-02T08:30:00Z",
"last_success_at": "2026-07-02T08:30:00Z",
"consecutive_failures": 0,
"created_at": "2026-06-11T15:04:05Z",
"updated_at": "2026-07-02T09:00:00Z",
"secret": "whsec_nZbY0eXaMpLeOnLyDoNotUse0aQ3rT5uV7wX9yZ1aB3c"
}
Errors
| Status | Cause |
|---|---|
404 | Webhook not found or you don’t have access. |
Was this page helpful?
⌘I