Temporal Paygo starter: submit and poll a Sume job

Temporal's Paygo plan has a $0 monthly minimum. A first workflow can submit a Sume job in one Activity and poll its status in a second. Python code included.

4 min readSume
All posts

Temporal's September 15, 2026 changelog launches a Paygo plan offering "Developer support and a $0/mo minimum", so a first workflow costs nothing to start. A good first workflow for it is two Activities: one that submits a Sume job with an Idempotency-Key, and a second that polls the job's status until it is terminal.

Why two Activities

Submitting spends money, so it must be safe to retry. Sume replays the original job when you resend the same Idempotency-Key, so derive the key from the workflow id and a retry cannot double-bill. Polling spends nothing and can run for minutes, so it gets its own Activity and its own timeout.

Sume says not to resubmit a paid request just because a local process timed out, and to stop polling on completed, failed or canceled. Read terminal and sume_status from GET /v1/jobs/{id}/status, and honor next_poll_after_seconds when it is present.

Plan and job facts (read 2026-10-03)
SourceFact
Temporal changelog, Sep 15, 2026Paygo plan launch: developer support and a $0/mo minimum
Sume jobs docsSubmit with Idempotency-Key; poll /v1/jobs/{id}/status until terminal
Sume video docsPOST /v1/videos returns id and polling_url; the job is also visible at /v1/jobs/{id}/status

The Activities and the Workflow

The code uses the temporalio Python SDK with async Activities, and runs the blocking HTTP calls in a thread. The Workflow passes the model and prompt, and gets back the final status payload. Register both Activities and the Workflow on a Worker as you would for any Temporal app, ideally with the Activities in their own module; I have not shown the worker boot code.

import asyncio, os
from datetime import timedelta
import requests
from temporalio import activity, workflow

API, H = 'https://api.sume.com/v1', {'Authorization': f"Bearer {os.environ['SUME_API_KEY']}"}

@activity.defn
async def submit_job(prompt: str) -> str:
    key = f'{activity.info().workflow_id}-submit'
    r = await asyncio.to_thread(requests.post, f'{API}/videos', headers={**H, 'Idempotency-Key': key}, json={'model': 'seedance-2', 'prompt': prompt})
    r.raise_for_status()
    return r.json()['id']

@activity.defn
async def poll_job(job_id: str) -> dict:
    while True:
        s = (await asyncio.to_thread(requests.get, f'{API}/jobs/{job_id}/status', headers=H)).json()
        activity.heartbeat(s.get('sume_status'))
        if s.get('terminal'):
            return s
        await asyncio.sleep(s.get('next_poll_after_seconds') or 15)

@workflow.defn
class SumeJob:
    @workflow.run
    async def run(self, prompt: str) -> dict:
        job_id = await workflow.execute_activity(submit_job, prompt, start_to_close_timeout=timedelta(seconds=30))
        return await workflow.execute_activity(poll_job, job_id, start_to_close_timeout=timedelta(minutes=20), heartbeat_timeout=timedelta(minutes=2))

Things to watch

  • Use one Idempotency-Key per intent, here per workflow, and never reuse it for a different request.
  • Branch on sume_status after the poll: completed, failed and canceled are all terminal.
  • Fetch GET /v1/jobs/{id}/result once the job is completed, and store the Sume media URL.
  • Check the Temporal page for Paygo limits, which I did not read in detail.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume