Webhook URL rejected as invalid? Sume's webhook URL rules
Sume answers 400 invalid_request when a webhook URL is not public HTTPS. The rules for scheme, host, port, and credentials, and the check at delivery.

Sume rejects a webhook URL with 400 invalid_request when it is not a public HTTPS URL. Plain http://, localhost, private-network addresses, and credentials in the URL are refused on every job and run, and current code also refuses a non-default port such as :8443. The check runs before anything is created, so fix the URL and submit again.
The documented rules come from Run webhooks, Runs and results, and Webhooks, read 2026-09-27. Rules marked as current code are read from the API source; the docs do not list them. Failed or missing deliveries after a URL was accepted are covered in Sume webhook not received?
Which webhook URLs does Sume refuse?
In current code one check covers webhook_url and its alias callback_url on generation jobs, including callback_url on POST /v1/videos, communication.webhook_url on Format, Action, and Agent Completion runs, and Send test. For hosts written as IP addresses, current code also refuses link-local addresses such as 169.254.169.254, carrier-grade NAT, multicast, and the IPv6 loopback, unique-local, and link-local ranges.
| Webhook URL | Result | Rule from |
|---|---|---|
http://hooks.example.com/sume | 400: not HTTPS | Docs |
https://localhost/sume, https://127.0.0.1/sume | 400: localhost | Docs |
https://10.0.0.5/sume, https://192.168.1.20/sume | 400: private network | Docs |
https://user:pass@hooks.example.com/sume | 400: credentials in the URL | Docs (runs), current code (jobs) |
| A URL longer than 2048 characters | 400 | Docs |
https://hooks.example.com:8443/sume | 400: a port other than the default 443 | Current code |
https://api.default.svc.cluster.local/sume, https://host.docker.internal/sume | 400: a name ending in .local, .internal, .test, or .localhost | Current code |
https://hooks.example.com/sume | Accepted | Docs |
What does the 400 response look like?
Every refusal is 400 invalid_request, but in current code the wording depends on the surface. Run creates and Send test name the field in the message and in details.field. Generation-job submits answer Invalid request body. and put the reason in details.errors[]: a path naming the field, and the message webhook_url must be a valid public HTTPS URL whichever field you used. Both are validation errors with retryable: false and next_action: fix_input, so sending the same URL again fails the same way. A run create's error, trimmed to the fields the docs show:
{
"error": {
"code": "invalid_request",
"message": "webhook_url must be a public HTTPS URL without credentials or an explicit port.",
"request_id": "req_…",
"details": { "field": "webhook_url" }
}
}Why was a URL that works in my browser refused?
Reachable from your browser is not the same as allowed. Reasons to check:
- It has a port.
https://staging.example.com:8443/hooksmay open in a browser, but current code refuses any port other than the default 443 and delivers on port 443. Serve the handler on the default HTTPS port. - It is an internal name, such as
api.default.svc.cluster.localorhost.docker.internal. Current code refuses names ending in.local,.internal,.test, or.localhost. - It uses plain HTTP, or puts a user and password in the URL. Sume signs its deliveries with HMAC-SHA256, so verify the signature instead; Signed webhooks for video runs shows how.
- It is your own machine. The POST comes from Sume, so for local work put a public HTTPS tunnel in front of the handler, as in Test Sume webhooks locally.
- The body disagrees with itself:
webhook_urlandcallback_urlwith different values, or, on a job submit,mode: "webhook"with no URL. Both are400 invalid_requesttoo.
Is the URL checked again when Sume delivers?
Yes. The docs say the URL is re-validated as a public HTTPS URL at delivery time, and redirects are not followed: a 3xx is a failed attempt, so register the final URL. Current code also resolves the hostname at delivery, refuses the delivery if any address it resolves to is non-public, and connects only to the addresses it checked.
So a public-looking name that resolves to an internal address passes the submit and fails at delivery. The job or run keeps its real outcome, its webhook_delivery shows failed with the reason in last_error, and current code does not retry a blocked target. A name that does not resolve at all is treated as a transient failure and retried. Redeliver never sends to a different URL, so a corrected URL needs a new job or run.
How do I check a URL before I submit real work?
Use Send test, the control on /dashboard/webhooks or POST /v1/webhooks/test-deliveries with account:write; it never creates a job or run. It applies the same URL rules, so it answers 400 invalid_request for the same URLs. In current code it also runs the delivery-time DNS check: a name that resolves to a non-public address comes back with status_code: null and the reason in error. Sume webhook not received? covers the rest of its response.
Sources
Related posts
More in Developers
- AI model aggregator: what it is and when to go direct
An AI model aggregator sells many labs' models behind one API key, one request shape and one bill. What you gain, what you give up, and what to check.
- AI model router: what it does and what you give up
An AI model router picks which model serves each request, so your code sends one id. How that differs from pinning a model, and the control you trade.
- Why an API returns 404 Not Found, and how to fix it
An API returns 404 when no route matches your path or method, or when the id doesn't exist for your credentials. How to tell them apart and fix each.
- API vs SDK: what's the difference, and do you need one?
An API is the contract: routes, fields and errors. An SDK is a library in one language that calls that API for you. What an SDK adds, and when to skip it.
Written by Sume