TikTok publish_id (64 chars): store it next to the Sume job id
TikTok's publish_id can be up to 64 characters and drives status polling. Keep it with the Sume job id and Idempotency-Key so a retry never double-posts.

TikTok returns a publish_id when you start a post, and the Direct Post reference caps it at 64 characters. Store it in a column that can hold 64 characters, and key the row by your own post attempt that also holds the Sume job id and the Idempotency-Key you used, so a retry reuses work rather than creating a second video or a second post.
Two systems, two ids: Sume owns the clip, TikTok owns the post. Linking them is a small table, not a framework.
Which ids are in play?
The Direct Post reference says publish_id has a maximum length of 64 characters, and the upload guide says it tracks progress asynchronously through the status endpoint. Sume's side is described in jobs and results: a job id, plus an Idempotency-Key that Sume requires on write calls such as video trim.
| Id | Owner | Why keep it |
|---|---|---|
| Sume job id | Sume | Fetch the result or the MP4 again |
| Idempotency-Key | You, sent to Sume | Retry a Sume write without duplicating it |
| publish_id | TikTok | Poll the status of the post |
| Your attempt id | You | The row that ties the three together |
What table do I need?
A single table is enough. This one runs as written and shows the idea: create the row before the first Sume call, so a crash between steps leaves a record you can resume.
import sqlite3
db = sqlite3.connect(":memory:")
db.execute("""CREATE TABLE post_attempt (
id INTEGER PRIMARY KEY,
idempotency_key TEXT UNIQUE NOT NULL,
sume_job_id TEXT,
publish_id TEXT CHECK (length(publish_id) <= 64)
)""")
db.execute("INSERT INTO post_attempt (idempotency_key) VALUES (?)", ("tt-2026-10-02-001",))
db.execute("UPDATE post_attempt SET sume_job_id = ? WHERE idempotency_key = ?", ("job_123", "tt-2026-10-02-001"))
print(db.execute("SELECT * FROM post_attempt").fetchall())How does this stop a double post?
The unique idempotency_key means a second attempt with the same key finds the existing row instead of inserting a new one. If the row already holds a publish_id, poll that post's status rather than start another. If it holds only a Sume job id, the clip exists and you resume at the TikTok step. If it holds neither, resend the Sume call with the same Idempotency-Key; a call that already went through is not duplicated.
Mind the rate limit while polling: the reference lists 6 requests per minute for each user access token, so poll on a timer rather than in a tight loop.
What does Sume not store for you?
Sume does not know your publish_id, your TikTok token or the post status. It tracks the job only. Jobs can be fetched later by id, so you do not need to keep the video bytes just to be able to retry a failed post, but keep your own copy if your TikTok upload window is short.
Checklist
Before wiring the loop:
- Column for
publish_idallows 64 characters. - Row exists before the first external call.
- Same Idempotency-Key reused on every retry of the same attempt.
- Status polling respects 6 requests per minute per token.
Sources
Related posts
More in Developers
- TikTok upload URL 403 expired and 416 Content-Range mismatch
A chunked TikTok upload returns 206 per chunk and 201 at the end. 403 means the upload_url expired after one hour; 416 means Content-Range does not match.
- Timeline 1.0 warnings after stitching shots: which need a fix
Timeline 1.0 returns soft warnings, not failures. A list of the codes you will see on a stitched AI film, what each means, and which ones are worth acting on.
- Trigger.dev version skew protection and in-flight Sume jobs
Trigger.dev pins each run to the deployment from the same commit. What that means for tasks that created a Sume job before a deploy, and what to store.
- TTS boundary_lead_ms: why sentence slices end 70 ms after a word
Sume TTS sentence segments cut boundary_lead_ms after a sentence's last word: 70 ms by default, 0 to 500. The next segment absorbs the pause, and no gap opens.
Written by Sume