Supabase Queues visibility timeout as a Sume job poller
Supabase Queues (pgmq) hide a read message for a visibility timeout, which fits polling a Sume job: read, check status, archive on terminal, else it reappears.

A Supabase Queue read hides the message from other consumers for a visibility timeout, so a message you do not delete or archive comes back on its own. That is exactly the shape of polling a Sume job: enqueue the job id, read it, check GET /v1/jobs/{id}/status, archive it when the job is terminal, and let it reappear otherwise.
The Supabase facts below are from its Queues API page, read on 2026-10-02. It describes Queues as a Postgres-native durable message queue, with the public functions listed in the table.
Which queue functions do you need?
Do not use pop for polling. It deletes on read, so a crash between the read and your status call loses the job id. Use read and delete or archive only after the check.
| Function | What it does |
|---|---|
| pgmq_public.send | Adds a message, optionally delayed by a number of seconds |
| pgmq_public.read | Reads up to n messages, hiding them for a visibility timeout |
| pgmq_public.pop | Reads the next message and deletes it |
| pgmq_public.archive | Moves a message to the queue's archive table |
| pgmq_public.delete | Deletes a message permanently |
How does that map onto a Sume job?
Sume jobs are durable and keep running after your process dies, and the jobs and results docs tell you to store the job id and resume from status_url. A queue row is that stored id with retry built in. If a worker dies mid-check, the message becomes visible again after the timeout and another worker picks it up, and nothing is resubmitted, so no second paid job is created.
What does the consumer loop do?
Each pass reads a few messages, calls the status endpoint for each, and branches on the booleans the endpoint returns:
terminalis true andsume_statusiscompleted: fetchGET /v1/jobs/{id}/result, store the artifact URLs, then archive the message.terminalis true and the jobfailedor wascanceled: record the error fromGET /v1/jobs/{id}, then archive.terminalis false: leave the message alone. It reappears after the visibility timeout. Set that timeout near thenext_poll_after_secondsvalue Sume returns rather than a fixed guess.- The status call returns
429: leave the message and wait forretry-after, per the errors docs.
What should the job record hold?
Put the job_id and the Idempotency-Key you used in the message, along with a count of how many times you have read it. A message that has been read 50 times without reaching a terminal state deserves a human look rather than another round, and the count lets you stop. You can cancel a job with POST /v1/jobs/{id}/cancel only before generation starts; after that Sume returns 409 job_generation_already_started and the job runs on.
Keep results out of the message. Store the artifact URLs in your own table keyed by job_id, and keep the queue for work still to do. That makes the archive table a clean audit of which jobs you have finished checking.
What does this not do?
The queue does not start jobs, and it does not know about Sume's concurrency. Jobs beyond your plan's concurrency sit as queued, which is a normal state, and a full workspace queue answers 429 queue_full at submit. Handle that at the submit step.
It also adds cost of its own: each read is a database operation on your project. For a handful of long video jobs a webhook is simpler. Reach for the queue when you have many jobs, want crash safety, and already run Supabase.
When is a webhook better than this?
A queue poller costs a status read per job per pass. A Sume webhook tells you once, at the terminal event; a receiver is shown in Supabase Edge Function video webhook. The two work well together: the webhook archives the message the moment it arrives, and the queue is the backstop for deliveries that never come after Sume's 10 attempts.
One honest limit: Sume does not push progress, only terminal events, so there is nothing to show between submit and finish except the status you read.
Sources
Related posts
More in Integrations
- Svix App Portal MCP and SDK v2: debugging Sume webhook deliveries
Svix now ships an App Portal MCP server for coding agents. For Sume webhooks, debug with the signature check, a test delivery and redeliver endpoints.
- Tabnine agent MCP: mcp_servers.json with requestInit headers for Sume
Add Sume's hosted MCP to Tabnine with a url entry in mcp_servers.json and an x-api-key header under requestInit. File locations, transport detection, caveats.
- Telegram sendAnimation: H.264 without sound, 50 MB, how to trim one
sendAnimation takes a GIF or H.264/MPEG-4 clip without sound, up to 50 MB. Cut a short silent loop from a generated video with Sume video-trim audio drop.
- sendAnimation or sendVideo for a short AI clip: which Telegram method
Pick sendAnimation for a silent looping clip, sendVideo for sound or a poster cover. How the Bot API methods differ and how Sume video-trim preps both.
Written by Sume