Documentation Index
Fetch the complete documentation index at: https://docs.shoal.xyz/llms.txt
Use this file to discover all available pages before exploring further.
Register a webhook URL to receive radar and/or signal events as they occur. Shoal will POST a JSON payload to your URL whenever a matching event is detected.
Webhook creation is available only on plans that include webhook access.
Request Body
| Field | Type | Required | Description |
|---|
url | string | Yes | HTTPS endpoint to receive events. Must not point to a private/internal address. |
event_types | string[] | Yes | Array of event types to subscribe to: radar, signal, or both. |
Request
curl -X POST "https://api.shoal.xyz/v1/webhooks" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/shoal-webhook",
"event_types": ["radar", "signal"]
}'
import os, requests
API_KEY = os.environ.get("SHOAL_API_KEY", "YOUR_API_KEY")
r = requests.post(
"https://api.shoal.xyz/v1/webhooks",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json={
"url": "https://example.com/shoal-webhook",
"event_types": ["radar", "signal"],
},
)
print(r.json())
const API_KEY = process.env.SHOAL_API_KEY || 'YOUR_API_KEY';
const res = await fetch('https://api.shoal.xyz/v1/webhooks', {
method: 'POST',
headers: {
Authorization: `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://example.com/shoal-webhook',
event_types: ['radar', 'signal'],
}),
});
console.log(await res.json());
Response (201)
{
"data": {
"id": 12,
"url": "https://example.com/shoal-webhook",
"secret": "a1b2c3d4e5f6...64-char-hex-string",
"event_types": ["radar", "signal"],
"active": true,
"created_at": "2026-03-10T12:00:00Z"
}
}
Save the secret from the response — it is only returned once at creation time. You’ll need it to verify webhook signatures. See Webhook Signatures.
Errors
| Status | Error | Cause |
|---|
| 400 | url is required | Missing URL |
| 400 | URL must use HTTPS | Non-HTTPS URL |
| 400 | URL must not point to a private address | URL resolves to localhost/private IP |
| 400 | event_types must be a non-empty array | Missing or empty event types |
| 400 | event_types must only contain: radar, signal | Invalid event type |
| 400 | Maximum 5 webhooks allowed | Per-account webhook limit reached |