OpenAI Python 3.23 turn artifacts: hand over a Sume render
OpenAI Python SDK 3.23.0 adds file staging and turn artifact downloads for agents. Sume job webhooks give you a public media.sume.com URL to hand over.

To give an OpenAI agent a Sume render, pass it the artifact URL from the finished job: Sume copies generated output to a public media.sume.com URL, and the job webhook payload carries it in artifacts[].url. The OpenAI side is the part that changed: the release notes list Python SDK v3.23.0 (Oct 1, 2026) with "session traces and Realtime translations", and say agents can stage files and download turn artifacts.
What the release notes say
I am not describing the exact OpenAI method names here, because the release page lists the capability and not the call signatures. Use the SDK reference for those.
| Item | Detail |
|---|---|
| Release date | Oct 1, 2026 |
| Headline | Session traces and Realtime translations |
| Agents | Can stage files and download turn artifacts |
Where the Sume side comes from
A Sume generation job reaches a terminal state and, if you submitted with mode webhook and a webhook_url, Sume POSTs a signed job.completed event. The payload holds an artifacts array; each entry has an id, a url on media.sume.com, a type and a content type. Treat the URL as opaque, store the Sume URL and not a provider URL, and use job_id as your idempotency key.
The script below verifies the signature over the timestamp, a dot and the raw body, refuses an empty secret, and returns the artifact URLs. It runs as written; it signs a sample body itself so you can see a pass and a fail.
import hashlib, hmac, json, time
def verify(raw: bytes, ts: str, header: str, secret: str, tol=300) -> bool:
if not secret:
raise ValueError('empty webhook secret')
if abs(int(time.time()) - int(ts)) > tol:
return False
digest = hmac.new(secret.encode(), f'{ts}.'.encode() + raw, hashlib.sha256).hexdigest()
want = f'sume-v1={digest}'
ok = False
for entry in header.split(','):
ok |= hmac.compare_digest(entry.strip(), want)
return ok
def artifact_urls(raw: bytes) -> list[str]:
body = json.loads(raw)
if body.get('event') != 'job.completed':
return []
return [a['url'] for a in body['payload']['artifacts']]
if __name__ == '__main__':
secret = 'test-secret'
raw = json.dumps({'event': 'job.completed', 'job_id': 'job_1', 'payload': {'artifacts': [
{'id': 'artf_1', 'url': 'https://media.sume.com/artifacts/x.mp4', 'type': 'video'}]}}).encode()
ts = str(int(time.time()))
sig = 'sume-v1=' + hmac.new(secret.encode(), f'{ts}.'.encode() + raw, hashlib.sha256).hexdigest()
print(verify(raw, ts, sig, secret), artifact_urls(raw))
print(verify(raw, ts, 'sume-v1=bad', secret))Hand it to the agent
Once you have the URL, give it to the agent as the file it should work from, using whichever staging call the SDK documents. If your agent must read the file, a public HTTPS URL is simpler than a local path. Keep status_url polling in place for deliveries that never arrive.
Sources
Related posts
More in Developers
- OpenAI Realtime GA migration: drop the OpenAI-Beta header
Beta Realtime integrations must move to GA and stop sending OpenAI-Beta: realtime=v1. A short checklist, a code scan for the header, and where Sume jobs fit.
- OpenAI TTS: 13 built-in voices vs 9 legacy ones, pick the model first
OpenAI's guide lists gpt-4o-mini-tts with 13 built-in voices and legacy tts-1 and tts-1-hd with 9. Formats, custom voice consent and disclosure, as a checklist.
- OpenTelemetry spans for MCP tools: tag the Sume request_id
Claude Code v2.1.283 adds MCP tool outputs to OpenTelemetry spans. Put the Sume request_id on the span and keep signed URLs off it.
- Parallel tool calls: fan out Sume renders, wait once
ElevenAgents can run multiple tools in one turn. On Sume's hosted MCP, submit several jobs, then wait on all of them with one jobs_wait call of up to 20 ids.
Written by Sume