set -eu
umask 077
# Path values must be URL-encoded; query string excludes the leading question mark.
url="${HD_BASE_URL:?Set API origin}/v1/partner/webhook-endpoints/${HD_PATH_ENDPOINT_ID:?Set the path fixture}/rotate-secret/${HD_QUERY_STRING:+?${HD_QUERY_STRING}}"
printf 'Accept: application/json\n' > "${HD_REQUEST_HEADERS_FILE:?Private header file}"
printf 'Authorization: Bearer %s\n' "${HD_PARTNER_API_KEY:?Set partner API key}" >> "$HD_REQUEST_HEADERS_FILE"
curl --silent --show-error --max-time 30 --request POST \
--header "@$HD_REQUEST_HEADERS_FILE" \
--dump-header "${HD_HEADERS_FILE:?Private response header file}" \
--output "${HD_RESPONSE_FILE:?Private response file}" \
--write-out '%{http_code}\n%{content_type}\n' \
"$url"import os, json
from pathlib import Path
from urllib.parse import quote
import requests
os.umask(0o077)
url = os.environ["HD_BASE_URL"].rstrip("/") + '/v1/partner/webhook-endpoints/{endpoint_id}/rotate-secret/'
url = url.replace('{endpoint_id}', quote(os.environ['HD_PATH_ENDPOINT_ID'], safe=""))
if os.environ.get("HD_QUERY_STRING"): url += "?" + os.environ["HD_QUERY_STRING"]
headers = {"Accept": 'application/json'}
headers["Authorization"] = "Bearer " + os.environ["HD_PARTNER_API_KEY"]
kwargs = {}
response = requests.request('POST', url, headers=headers, timeout=30, allow_redirects=False, **kwargs)
Path(os.environ["HD_RESPONSE_FILE"]).write_bytes(response.content)
Path(os.environ["HD_HEADERS_FILE"]).write_text(json.dumps(dict(response.headers)))
print(response.status_code)
print(response.headers.get("Content-Type", ""))
import { readFileSync, writeFileSync } from "node:fs";
const env = process.env;
let url = env.HD_BASE_URL.replace(/\/$/, "") + "/v1/partner/webhook-endpoints/{endpoint_id}/rotate-secret/";
url = url.replace("{endpoint_id}", encodeURIComponent(env.HD_PATH_ENDPOINT_ID));
if (env.HD_QUERY_STRING) url += "?" + env.HD_QUERY_STRING;
const headers = { "Accept": "application/json" };
headers.Authorization = "Bearer " + env.HD_PARTNER_API_KEY;
const options = { method: "POST", headers, redirect: "manual", signal: AbortSignal.timeout(30000) };
const response = await fetch(url, options);
writeFileSync(env.HD_RESPONSE_FILE, Buffer.from(await response.arrayBuffer()), {mode: 0o600});
writeFileSync(env.HD_HEADERS_FILE, JSON.stringify(Object.fromEntries(response.headers)), {mode: 0o600});
console.log(response.status);
console.log(response.headers.get("content-type") ?? "");
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.hi-doctor.ai/v1/partner/webhook-endpoints/{endpoint_id}/rotate-secret/",
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://api.hi-doctor.ai/v1/partner/webhook-endpoints/{endpoint_id}/rotate-secret/"
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://api.hi-doctor.ai/v1/partner/webhook-endpoints/{endpoint_id}/rotate-secret/")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hi-doctor.ai/v1/partner/webhook-endpoints/{endpoint_id}/rotate-secret/")
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_body{
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"object": "webhook_endpoint",
"url": "https://partner.example.com/hooks/hi-doctor",
"description": "Order system",
"mode": "test",
"events": [
"consultation.approved",
"consultation.rejected",
"prescription.issued"
],
"status": "enabled",
"failure_streak": 0,
"last_success_at": "2026-11-03T12:40:00Z",
"last_failure_at": null,
"previous_secret_expires_at": "2026-11-04T12:40:00Z",
"created_at": "2026-11-03T10:15:00Z",
"updated_at": "2026-11-03T12:40:00Z",
"secret": "whsec_Q2hhbmdlTWVJbkFSZWFsU2VjcmV0U3RvcmUxMjM0NTY="
}Rotate a webhook signing secret
Issues a new signing secret, returned once. Deliveries are signed with both the new and the previous secret for 24 hours so you can switch over. Requires the consultations:read, prescriptions:read and webhooks:manage scopes.
Access, effects and retries
Ownership: The API key’s own partner and mode (test or live); another partner’s or mode’s records answer 404.
Prerequisites: A partner API key with the webhooks:manage and consultations:read and prescriptions:read scopes.
Effects: Issues a new signing secret; the previous one keeps signing for 24 hours.
Retries: Each call issues a new secret; keep the last one returned.
set -eu
umask 077
# Path values must be URL-encoded; query string excludes the leading question mark.
url="${HD_BASE_URL:?Set API origin}/v1/partner/webhook-endpoints/${HD_PATH_ENDPOINT_ID:?Set the path fixture}/rotate-secret/${HD_QUERY_STRING:+?${HD_QUERY_STRING}}"
printf 'Accept: application/json\n' > "${HD_REQUEST_HEADERS_FILE:?Private header file}"
printf 'Authorization: Bearer %s\n' "${HD_PARTNER_API_KEY:?Set partner API key}" >> "$HD_REQUEST_HEADERS_FILE"
curl --silent --show-error --max-time 30 --request POST \
--header "@$HD_REQUEST_HEADERS_FILE" \
--dump-header "${HD_HEADERS_FILE:?Private response header file}" \
--output "${HD_RESPONSE_FILE:?Private response file}" \
--write-out '%{http_code}\n%{content_type}\n' \
"$url"import os, json
from pathlib import Path
from urllib.parse import quote
import requests
os.umask(0o077)
url = os.environ["HD_BASE_URL"].rstrip("/") + '/v1/partner/webhook-endpoints/{endpoint_id}/rotate-secret/'
url = url.replace('{endpoint_id}', quote(os.environ['HD_PATH_ENDPOINT_ID'], safe=""))
if os.environ.get("HD_QUERY_STRING"): url += "?" + os.environ["HD_QUERY_STRING"]
headers = {"Accept": 'application/json'}
headers["Authorization"] = "Bearer " + os.environ["HD_PARTNER_API_KEY"]
kwargs = {}
response = requests.request('POST', url, headers=headers, timeout=30, allow_redirects=False, **kwargs)
Path(os.environ["HD_RESPONSE_FILE"]).write_bytes(response.content)
Path(os.environ["HD_HEADERS_FILE"]).write_text(json.dumps(dict(response.headers)))
print(response.status_code)
print(response.headers.get("Content-Type", ""))
import { readFileSync, writeFileSync } from "node:fs";
const env = process.env;
let url = env.HD_BASE_URL.replace(/\/$/, "") + "/v1/partner/webhook-endpoints/{endpoint_id}/rotate-secret/";
url = url.replace("{endpoint_id}", encodeURIComponent(env.HD_PATH_ENDPOINT_ID));
if (env.HD_QUERY_STRING) url += "?" + env.HD_QUERY_STRING;
const headers = { "Accept": "application/json" };
headers.Authorization = "Bearer " + env.HD_PARTNER_API_KEY;
const options = { method: "POST", headers, redirect: "manual", signal: AbortSignal.timeout(30000) };
const response = await fetch(url, options);
writeFileSync(env.HD_RESPONSE_FILE, Buffer.from(await response.arrayBuffer()), {mode: 0o600});
writeFileSync(env.HD_HEADERS_FILE, JSON.stringify(Object.fromEntries(response.headers)), {mode: 0o600});
console.log(response.status);
console.log(response.headers.get("content-type") ?? "");
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.hi-doctor.ai/v1/partner/webhook-endpoints/{endpoint_id}/rotate-secret/",
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://api.hi-doctor.ai/v1/partner/webhook-endpoints/{endpoint_id}/rotate-secret/"
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://api.hi-doctor.ai/v1/partner/webhook-endpoints/{endpoint_id}/rotate-secret/")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hi-doctor.ai/v1/partner/webhook-endpoints/{endpoint_id}/rotate-secret/")
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_body{
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"object": "webhook_endpoint",
"url": "https://partner.example.com/hooks/hi-doctor",
"description": "Order system",
"mode": "test",
"events": [
"consultation.approved",
"consultation.rejected",
"prescription.issued"
],
"status": "enabled",
"failure_streak": 0,
"last_success_at": "2026-11-03T12:40:00Z",
"last_failure_at": null,
"previous_secret_expires_at": "2026-11-04T12:40:00Z",
"created_at": "2026-11-03T10:15:00Z",
"updated_at": "2026-11-03T12:40:00Z",
"secret": "whsec_Q2hhbmdlTWVJbkFSZWFsU2VjcmV0U3RvcmUxMjM0NTY="
}Authorizations
Partner API key sent as Authorization: Bearer . Test keys and live keys are separate.
Headers
Accepted but not used for replay on this operation. Only POST /v1/partner/consultations stores and replays responses by Idempotency-Key.
^[A-Za-z0-9_-]{8,128}$Path Parameters
The webhook endpoint id.
Response
The endpoint and its new secret.
The webhook endpoint id.
Always webhook_endpoint.
webhook_endpoint The https URL (port 443) that receives deliveries.
Your description of the endpoint.
test or live, from the API key that created it.
live, test Event types sent to the endpoint, or ["*"] for all of them.
enabled, disabled or auto_disabled.
enabled, disabled, auto_disabled Consecutive failed deliveries; reset by a successful delivery or by re-enabling the endpoint.
When a delivery to the endpoint last succeeded.
When a delivery to the endpoint last failed.
After a rotation, when the previous secret stops signing deliveries; otherwise null.
When it was created.
When it last changed.
The signing secret: whsec_ followed by the standard base64 encoding of 32 random bytes, which are the HMAC key. It is shown only in this response; store it now.