calcom/cal.diy · error · BadRequestException
Webhook URL is not allowed: ${validation.error}
Error message
Webhook URL is not allowed: ${validation.error} What it means
validateWebhookUrl runs validateUrlForSSRFSync (an SSRF guard from @calcom/platform-libraries) on the subscriberUrl; if the URL is not allowed it throws BadRequestException (HTTP 400) with the validation error. This blocks internal/loopback/private IPs and disallowed schemes to prevent SSRF via webhook callbacks.
Source
Thrown at apps/api/v2/src/modules/webhooks/utils/validate-webhook-url.ts:7
import { BadRequestException } from "@nestjs/common";
import { validateUrlForSSRFSync } from "@calcom/platform-libraries";
export function validateWebhookUrl(subscriberUrl: string): void {
const validation = validateUrlForSSRFSync(subscriberUrl);
if (!validation.isValid) {
throw new BadRequestException(`Webhook URL is not allowed: ${validation.error}`);
}
}
export function validateWebhookUrlIfChanged(
newSubscriberUrl: string | undefined,
existingSubscriberUrl: string | undefined
): void {
if (newSubscriberUrl && newSubscriberUrl !== existingSubscriberUrl) {
validateWebhookUrl(newSubscriberUrl);
}
}
View on GitHub (pinned to 176037d0af)
Solutions
- Use a publicly routable https URL for the subscriberUrl.
- For local testing, expose the callback via a tunnel that yields a public hostname.
- Ensure the URL scheme is allowed (typically https).
- Resolve the host externally and confirm it is not in a private range before submitting.
Example fix
// before body.subscriberUrl = 'http://localhost:3000/hook'; // after body.subscriberUrl = 'https://my-app.example.com/hook';
Defensive patterns
Strategy: validation
Validate before calling
import { isPublicHostname } from './net'; // caller utility
function assertSafeWebhookUrl(url: string) {
try { const u = new URL(url); if (u.protocol !== 'https:') throw new Error('https required'); }
catch (e) { throw new Error(`bad webhook url: ${e.message}`); }
if (/localhost|127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|169\.254\./.test(url)) {
throw new Error('SSRF: private/loopback URL not allowed');
}
}
assertSafeWebhookUrl(body.subscriberUrl); Type guard
const isProbablySafeWebhookUrl = (u: string): boolean => {
try { const url = new URL(u); return url.protocol === 'https:' && !/(localhost|127\.|10\.|192\.168\.|169\.254\.)/.test(url.hostname); }
catch { return false; }
}; Try / catch
try { await api.createWebhook(body); }
catch (e) {
if (e.status === 400 && /Webhook URL is not allowed/.test(e.message)) {
// prompt user for a public https URL, then retry
} else throw e;
} Prevention
- Always use https with a public hostname for webhook callbacks.
- Use a tunnel (e.g. ngrok with a stable public host) for local testing.
- Client-side validate scheme and hostname before submit.
When it happens
Trigger: Creating or updating any webhook with a subscriberUrl that resolves to a private/loopback/link-local address, uses a disallowed scheme, or otherwise fails the SSRF policy (e.g. http://localhost, http://127.0.0.1, http://10.x.x.x, http://169.254.169.254).
Common situations: Local development pointing webhooks at localhost; cloud metadata IP abuse; DNS that resolves internally; missing https; typos in the URL.
Related errors
- DELEGATION_CREDENTIAL_ERROR trigger is only available for or
- DELEGATION_CREDENTIAL_ERROR trigger is only available for or
- DELEGATION_CREDENTIAL_ERROR trigger is only available for or
- ApiKeysService -Cannot set both apiKeyDaysValid and apiKeyNe
- teamId is required for team events, please provide a valid t
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/87810d121cf9d748.
Report an issue: GitHub.