Cursor self-hosted machines can't take Sume webhooks on a private URL
Sume rejects localhost, private-network and non-HTTPS webhook URLs. An agent on a self-hosted machine should poll status_url or use a public HTTPS receiver.

An agent running on a self-hosted machine usually cannot receive a Sume webhook directly, because the webhook_url must be a public HTTPS URL. Localhost, private-network and non-HTTPS URLs are rejected, so the machine should poll status_url, or a public receiver should sit in front of it.
Cursor Self-hosted Machines (Sep 2, 2026) offers My Machines and Team pools, and works with AWS Lambda, Vercel, Coder and Cloudflare.
What Sume accepts
Run webhooks apply the same validation, with a 2048-character limit, and re-check the URL at delivery time, not only at submit.
| URL | Result |
|---|---|
| Public HTTPS | Accepted. |
| http:// | Rejected. |
| localhost or a private-network address | Rejected. |
| A URL that redirects (3xx) | Not a delivery; redirects are not followed. |
Option one: poll from the machine
A machine that can reach the internet outbound does not need an inbound endpoint. Submit with mode: async, keep the job id, and poll GET /v1/jobs/{id}/status until terminal is true, honoring next_poll_after_seconds. Then fetch the result when result_ready is true.
The wait lives in your client, so its timeout can run to minutes without holding an HTTP request open. For a video, around 20 minutes is the docs' suggested client deadline.
Option two: a small public receiver
If you want to be told rather than ask, put a public HTTPS receiver somewhere that is reachable, and hand off from it. The Sume signature scheme is the same for job and run webhooks, so one verifier serves both.
- Verify the HMAC over
<timestamp>.<raw_body>and thesume-v1=header. - Store the event, answer 2xx within 10 seconds, and key on
job_id. - Let the self-hosted agent read the stored result on its next step.
- Keep status polling as a backup, because Sume tries a delivery up to 10 times and then stops.
Which events you will get
Job webhooks are terminal-only: job.completed, job.failed and job.canceled. There are no progress or partial events. A Format, Action or Agent Completion run sends a single *.run.terminal event per run instead, whatever number of clips it made.
If you need progress inside a long job, read GET /v1/jobs/:id/events, which is a pull snapshot rather than a stream.
Where the key lives
Whichever option you choose, the API key belongs in the machine's secret store, not in the repository. The signing secret is separate: read it from the dashboard Webhooks tab or GET /v1/webhooks/signing-secret, and store it as SUME_COM_WEBHOOK_SIGNING_SECRET for the receiver.
Sources
Related posts
More in Developers
- D-ID API uses Basic auth; Sume uses a bearer API key
D-ID's API docs say requests use the Authorization header with Basic credentials. Sume sends Authorization: Bearer with an API key. A short migration checklist.
- Dagster asset retries and Sume: stop a retry from billing twice
A Dagster RetryPolicy re-runs the whole asset function. Give the Sume submit a stable Idempotency-Key and fail terminal jobs with allow_retries=False.
- Dart Shelf: verify a Sume webhook with package:crypto HMAC-SHA256
A Dart Shelf server that reads the raw body, checks the sume-v1 HMAC with package:crypto in constant time and exits at boot when the signing secret is empty.
- Datadog DogStatsD metrics for Sume jobs: tags that stay small
Count Sume submits, terminal statuses and durations with DogStatsD, tagged by route, status and code, and keep job ids and request ids out of the tag set.
Written by Sume