Why is my TTS job slow? Read the Sume job events timeline
GET /v1/jobs/{id}/events returns a sanitized lifecycle timeline. Use it to tell queue wait from generation time and failed webhook deliveries.

To find out why a Sume TTS job was slow, call GET /v1/jobs/{id}/events. It returns a sanitized timeline of the job lifecycle, so you can see when the job was created, queued and started, when generation was submitted, when it finished, and whether a webhook delivery failed or is retrying. Raw provider URLs, provider task ids, callback signatures and secrets are left out.
That one list answers most slow-job tickets: was it waiting in the queue, was it generating, or did the audio finish and only your callback fail?
Which events can you see?
The Sume OpenAPI spec defines the event types, each with a source of sume or webhook, a status and a timestamp.
| Event type | What it tells you |
|---|---|
| job.created | The request was accepted |
| job.queued | The job is waiting for capacity |
| job.started | Work began |
| generation.submitted | Generation was sent; cancel is no longer possible |
| job.completed, job.failed, job.canceled | Terminal outcome |
| webhook.delivery | A callback attempt, with status such as succeeded, failed or retrying |
How do you read a slow job?
Compare the timestamps between neighbouring events. A long gap between job.queued and job.started is waiting. A long gap after generation.submitted is generation. If job.completed is early but you saw nothing, look at the webhook.delivery rows; a failed one points at your receiver, and you can redeliver the webhook without a rerun.
- Use the
limitquery parameter to cap how many events you pull. - Log the job id with your own request id so you can fetch the timeline later.
- Do not treat the timeline as an SLA; it is for debugging.
What does the call look like?
Print the events in order:
import os, requests
H = {"Authorization": "Bearer " + os.environ["SUME_API_KEY"]}
job_id = os.environ["JOB_ID"]
r = requests.get(
f"https://api.sume.com/v1/jobs/{job_id}/events",
headers=H, params={"limit": 50}, timeout=30,
)
r.raise_for_status()
for e in r.json()["data"]["events"]:
print(e["created_at"], e["type"], e["status"], e["message"])
What next?
If the timeline shows the job is still in flight, keep polling status rather than resubmitting; see what to do when a sync wait times out. For the polling loop itself, async TTS polling on Sume has a complete example.
Sources
Related posts
More in Developers
- Jupyter contact sheet: one prompt across four Sume image models
A notebook cell that sends one prompt to four Sume image models, tiles the results into a labeled contact sheet with Pillow, and prints each billed cost.
- Keep your Sora-style create_video() call: map it onto Sume
Sora's seconds, size and input_reference become duration, resolution plus aspect_ratio, and a first frame. Here is that map as a Python wrapper over Sume.
- Kestra webhook trigger has no HMAC: verify Sume deliveries first
Kestra's Webhook trigger is protected by its key alone. Verify Sume's sume-v1 signature in a thin relay, then start the flow, and submit with a stable key.
- Kling 3 on Sume rejects reference_*_urls: which model takes references
The kling-3 row supports text-to-video and start/end frames only. Send reference_image_urls and you get a 400; here is where references do work.
Written by Sume