mode webhook returns 400 without a webhook_url: what Sume checks

Submit with mode webhook and no URL and Sume returns 400 invalid_request. Omit mode and send a URL and Sume picks webhook for you. Both rules explained.

5 min readSume
All posts

A generation submit with mode: "webhook" and no webhook_url (or callback_url) fails with 400 invalid_request and the message "mode webhook requires webhook_url or callback_url." The reverse also holds in the API code: if you send a URL and leave mode out, Sume sets the mode to webhook for you.

These rules live in the submit validation for generation jobs, and they sit next to a few siblings that produce the same 400. The Webhooks page covers delivery, this page covers the submit-time checks.

How does Sume pick the mode?

Submit endpoints accept mode, webhook_url, callback_url, and wait_timeout_seconds. In the API's communication-options code, the effective mode is the one you sent, otherwise webhook when a URL is present, otherwise async.

That gives a short decision table.

Effective mode on a job submit (API code and docs read 2026-10-02)
You sendResult
No mode, no URLasync: 202 with polling URLs
No mode, webhook_url setwebhook
mode webhook, no URL400 invalid_request
webhook_url and callback_url that differ400 invalid_request
mode sync or subscribeBounded wait, wait_timeout_seconds clamped to 0..30

What are the other 400s on this path?

webhook_url and callback_url are aliases. If both are present and differ, the API answers 400 with "Send only one callback destination. webhook_url and callback_url must match when both are present." Send one.

The URL itself must be a public HTTPS address. The docs say localhost, private-network, and non-HTTPS URLs are rejected. A tunnel URL from your laptop works only if it is public HTTPS.

Does mode change cost or runtime?

No. The communication modes section says the mode decides how you learn the outcome and never changes whether a job is created, what it costs, or how long it takes.

So a 400 on mode is always safe to fix and resend: it is rejected before a job exists. Still send an Idempotency-Key on paid submits you might replay.

A submit that cannot hit the mode 400

Setting the URL and the mode from one variable keeps them consistent. Keep a polling fallback anyway: webhook delivery is an optimization and the docs tell you to keep status_url polling available.

curl -X POST https://api.sume.com/v1/image-1.0/generate \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: hero-shot-001" \
  -d '{
    "prompt": "Product hero shot of a matte black bottle on marble",
    "mode": "webhook",
    "webhook_url": "https://hooks.example.com/sume"
  }'

What Sume does not do

Job webhooks are terminal-only: job.completed, job.failed, and job.canceled. There are no progress callbacks, so a webhook-mode submit cannot replace reading GET /v1/jobs/:id/events if you need a timeline.

Quick checklist

The points above reduce to a short list you can paste into a runbook.

  • Send webhook_url or callback_url, not both, unless the values match.
  • Use a public HTTPS URL; localhost, private-network and non-HTTPS URLs are rejected.
  • Remember mode never changes price or runtime, only how you learn the outcome.
  • Keep status_url polling as a backup for deliveries that never arrive.
  • Send an Idempotency-Key on every paid submit that might be retried.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume