Check a webhook URL before you give it to Sume: https and public

Sume rejects localhost, private-network and non-HTTPS webhook URLs. A Python preflight that catches all three, and the 2048-character limit, before you submit.

4 min readSume
All posts

Before you pass webhook_url on a submit, check that it is HTTPS, not localhost, not longer than 2048 characters, and that its hostname resolves to a public address. Sume rejects localhost, private-network and non-HTTPS URLs with 400 invalid_request, so a local check turns a failed paid-path call into a one-line message in your own tool. The check below uses only the standard library.

What Sume says about the URL

The 2048-character limit is stated for the Format run field. The check applies it to every URL, which is a safe upper bound.

Webhook URL rules from the docs, read 2026-10-08
RuleWhere it is stated
Public HTTPS URLs onlyJob webhooks and run webhooks
localhost, private-network and non-HTTPS URLs are rejectedJob webhooks and run webhooks
Up to 2048 characterscommunication.webhook_url on run endpoints
Redirects are not followedDelivery behavior
Passing webhook_url or callback_url without a mode selects webhook modeCommunication modes

The preflight

The function returns None for a good URL and a reason string for a bad one. It resolves the hostname and tests every address, so a name that points at a private range fails even though the text looks public. Sume does its own check on its side, and this one only saves you a round trip.

import ipaddress, socket
from urllib.parse import urlparse

def check_webhook_url(url):
    u = urlparse(url)
    if u.scheme != "https" or not u.hostname:
        return "must be an https URL"
    if len(url) > 2048:
        return "longer than 2048 characters"
    if u.hostname == "localhost" or u.hostname.endswith(".localhost"):
        return "localhost is rejected"
    try:
        infos = socket.getaddrinfo(u.hostname, u.port or 443, type=socket.SOCK_STREAM)
    except socket.gaierror:
        return "hostname does not resolve"
    for info in infos:
        ip = ipaddress.ip_address(info[4][0])
        if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved:
            return f"resolves to a non-public address ({ip})"
    return None

if __name__ == "__main__":
    for url in ("http://example.com/h", "https://localhost/h", "https://127.0.0.1/h", "https://example.com/hooks/sume"):
        print(url, "->", check_webhook_url(url) or "ok")

Limits of a local check

Keep the preflight in the same module that builds the submit body, so no code path can send an unchecked URL.

  • DNS can change between your check and Sume's delivery. Treat the check as a lint, not a guarantee.
  • A URL that answers 301 or 302 counts as a failed delivery, because Sume does not follow redirects. Register the final URL.
  • During development, use a tunnel with a public HTTPS address, and run POST /v1/webhooks/test-deliveries before the first paid job.

Where the 400 shows up

If you skip the preflight, the failure is a 400 invalid_request on the create call, with a request_id in the error envelope. No job starts, so there is nothing to clean up, but a batch loop that does not stop on the first 400 will repeat the same mistake for every item. Run the check once at start-up for a fixed URL, or once per item if each customer supplies their own.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume