Face swap API webhook: get job.completed instead of polling

Sume's face swap (Beta) takes mode webhook with a public HTTPS webhook_url, then sends one signed terminal event: job.completed, job.failed or job.canceled.

4 min readSume
All posts

To get a callback when a face swap finishes, submit the run with mode: "webhook" and a public HTTPS webhook_url. Sume then sends one signed terminal event to that URL: job.completed, job.failed or job.canceled. You do not poll for completion, although the docs tell you to keep a polling fallback.

The face swap endpoint is POST /v1/models/sume/avatar-face-swap/v1.0/runs, a Beta model run documented on Face swap (Beta). It needs avatar_handle, video_url and quality. Of the four communication modes, only webhook is the right fit for a clip that routinely outlasts a 30-second HTTP wait.

How do I submit a face swap with a webhook?

Send the usual required fields and add the mode and URL. The source video must be a fetchable public HTTPS URL; signed or private URLs and provider task URLs are rejected, and the Beta worker targets sources of about 4-15 seconds with usable audio.

Webhook URLs follow the same rule as media inputs: localhost, private-network and non-HTTPS addresses are rejected at submit time, so a tunnel URL must be HTTPS and reachable from the internet.

curl -X POST https://api.sume.com/v1/models/sume/avatar-face-swap/v1.0/runs \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: faceswap-clip-042" \
  -d '{
    "avatar_handle": "product_host",
    "video_url": "https://example.com/source-clip.mp4",
    "quality": "plus",
    "mode": "webhook",
    "webhook_url": "https://example.com/hooks/sume"
  }'

What events does Sume send?

Sume sends terminal job events only. There are no progress or partial deliveries, so a webhook cannot drive a progress bar.

The same three events cover every generation job, face swap included. Face swap is a model run, so it falls on the generation-jobs side of Sume's two webhook surfaces, not the Action, Format or Agent run side.

Events and payload fields from Sume's Webhooks page, read 2026-10-03.
EventWhen it is sentPayload status
job.completedThe job completed and a public result is availableOK
job.failedThe job failed with a public errorERROR, with an error object
job.canceledThe job reached canceled stateERROR, with an error object

What do I do when the event arrives?

The webhook carries the job_id and, on success, the public artifacts. For a face swap, the finished resource exposes a public-safe video_url under media.sume.com when it is ready, and the face swap page says to prefer resource_status for readiness and job_status for polling. Fetch GET /v1/jobs/{job_id}/result if you want the full result rather than trusting only the callback body.

If a swap succeeds but you see no video_url, read the resource before retrying: a completed face swap job with an empty video_url is usually a readiness read, not a new paid run.

Is a webhook enough on its own?

No. Sume's guidance is to keep the polling fallback in place alongside the webhook, because a delivery can fail on your side. Two rules keep it safe.

First, verify the signature. Sume signs the raw JSON body with HMAC SHA 256 over <timestamp>.<raw_body> and sends x-sume-webhook-timestamp and x-sume-webhook-signature: sume-v1=<hex>. During a secret rotation the header carries one entry per live secret, so accept the delivery when any entry matches. Second, never answer a missed event by submitting a second paid job for the same intent: poll status_url for the job you already have, and reuse the same Idempotency-Key for any retry of the submit itself.

What should my receiver do?

Answer fast with a 2xx and do the work afterward. Reject a timestamp outside your replay window (the docs suggest five minutes as a default), then compare the signature against every sume-v1= entry. Your signing secret is on the Webhooks tab of the dashboard, or from GET /v1/webhooks/signing-secret with an API key that carries account:read.

Store the job_id you got at submit time next to your own record, so a late or repeated event can be matched and ignored if you already handled it. Because you reuse the same Idempotency-Key for retries of the submit, a duplicate submit does not create a second face swap, and your receiver should be just as safe against a duplicate event.

Sources

Related posts

More in Sume Avatar 1.0

All Sume Avatar 1.0 posts

Written by Sume