Direct HTTP API
Call the same SorryAssets capability API used by DesktopCore. Direct HTTP does not require the desktop app, MCP, or a SorryAssets project.
Before You Call
payments.
plaintext sa- value is shown once.
put it in browser code, a mobile binary, logs, or a public repository.
- Sign in at SorryAssets account.
- Make sure the account has enough spendable credits. API keys cannot initiate
- Create a key at API keys. Its
- Keep the key in a server-side secret manager or process environment. Never
An API key is an account bearer credential, not a supplier credential or a per-model grant.
Contract
GET /v1/videos/{id}/content.
- API origin:
https://api.sorryassets.com. - OpenAI SDK base URL:
https://api.sorryassets.com/v1. - Auth:
Authorization: Bearer sa-your-key. - Basic model discovery:
GET /v1/models. - Inputs, parameters, and retail prices:
GET /v1/catalog. - Exact preflight price:
POST /v1/estimate. - Images:
POST /v1/images/generationsandPOST /v1/images/edits. - Videos:
POST /v1/videos,GET /v1/videos/{id}, and - Exact settlement:
GET /v1/usage?taskRef={id}.
The checked contract is https://sorryassets.com/openapi.yaml. Models, Images, and Videos use OpenAI-shaped wire fields. Catalog, estimate, usage, account, and Admin are SorryAssets product APIs.
Discover And Estimate
API_BASE=https://api.sorryassets.com
AUTH_HEADER="Authorization: Bearer ${SORRYASSETS_API_KEY:?set SORRYASSETS_API_KEY}"
curl -fsS -H "$AUTH_HEADER" "$API_BASE/v1/models"
curl -fsS "$API_BASE/v1/catalog"
curl -fsS -X POST "$API_BASE/v1/estimate" \
-H "$AUTH_HEADER" \
-H 'Content-Type: application/json' \
-d '{
"model": "seedance-2.0-fast",
"parameters": {"prompt": "a clean studio product reveal", "duration": 5}
}'
Public model ids are globally unique. Generation requests select only model. Do not send capability, provider, connection, upstream model, project id, local node id, or local path.
Generate An Image
Choose an image model from Catalog. size is an optional hint and is not limited to an incomplete Catalog enum. The selected model may honor it, choose a nearest/default output, or reject an unsupported value.
curl -fsS -X POST "$API_BASE/v1/images/generations" \
-H "$AUTH_HEADER" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: logical-image-request-001' \
-d '{
"model": "gemini-3.1-flash-image",
"prompt": "a lime green studio headset",
"size": "1536x1024",
"n": 1
}' > image-response.json
jq -r '.data[0].b64_json' image-response.json \
| openssl base64 -d -A -out result.image
Inspect the decoded bytes before choosing an extension. SorryAssets may return PNG, JPEG, or WebP and never exposes a supplier URL.
Image edits use image for one reference or ordered repeated image[] parts for multiple references. Do not combine the two field shapes:
curl -fsS -X POST "$API_BASE/v1/images/edits" \
-H "$AUTH_HEADER" \
-H 'Idempotency-Key: logical-image-edit-001' \
-F 'model=gemini-3.1-flash-image' \
-F 'prompt=replace the background with a clean white studio' \
-F 'n=1' \
-F 'image[][email protected]' \
-F 'image[][email protected]' > image-edit-response.json
Create A Video With The OpenAI SDK
Use an official SDK version that still includes the Videos resource:
import os
import time
from openai import OpenAI
client = OpenAI(
api_key=os.environ["SORRYASSETS_API_KEY"],
base_url="https://api.sorryassets.com/v1",
)
video = client.videos.create(
model="seedance-2.0-fast",
prompt="a precise tabletop camera move around a ceramic object",
size="1280x720",
seconds="5",
)
while video.status not in {"completed", "failed"}:
time.sleep(2)
video = client.videos.retrieve(video.id)
if video.status != "completed":
raise RuntimeError(video.error)
content = client.videos.download_content(video.id)
content.write_to_file("result.mp4")
The current OpenAI Videos API and Sora 2 SDK surface are scheduled for retirement on 2026-09-24. SorryAssets keeps /v1/videos as its own resource family; the checked OpenAPI contract remains authoritative if a future OpenAI SDK removes these helpers.
Create And Download A Video With HTTP
The standard JSON shape supports a prompt-only request or one public HTTPS input_reference image:
curl -fsS -X POST "$API_BASE/v1/videos" \
-H "$AUTH_HEADER" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: logical-video-request-001' \
-d '{
"model": "seedance-2.0-fast",
"prompt": "continue the camera move from this frame",
"size": "1280x720",
"seconds": "5",
"input_reference": {"image_url": "https://example.com/reference.png"}
}' > video.json
VIDEO_ID=$(jq -r '.id' video.json)
while :; do
curl -fsS -H "$AUTH_HEADER" "$API_BASE/v1/videos/$VIDEO_ID" > video.json
STATUS=$(jq -r '.status' video.json)
[ "$STATUS" = completed ] && break
[ "$STATUS" = failed ] && { jq . video.json; exit 1; }
sleep 2
done
curl -fsS -H "$AUTH_HEADER" \
"$API_BASE/v1/videos/$VIDEO_ID/content" -o result.mp4
curl -fsS -H "$AUTH_HEADER" \
"$API_BASE/v1/usage?taskRef=$VIDEO_ID" > usage.json
Video state contains no billing fields or output URL. Download is authenticated and owner-scoped. Save content promptly because SorryAssets keeps durable task and billing facts, not a permanent cloud copy of supplier bytes.
Role-Tagged Video References
Some Catalog models declare multiple image, video, or audio roles. The SorryAssets references extension preserves those real roles. In multipart, send a JSON manifest and one ordered field per entry:
curl -fsS -X POST "$API_BASE/v1/videos" \
-H "$AUTH_HEADER" \
-H 'Idempotency-Key: logical-video-request-002' \
-F 'model=seedance-2.0-fast' \
-F 'prompt=animate the first frame with the supplied soundtrack' \
-F 'size=1280x720' \
-F 'seconds=5' \
-F 'references=[{"role":"reference_image","field":"reference_0"},{"role":"reference_audio","field":"reference_1"}];type=application/json' \
-F '[email protected]' \
-F '[email protected]' > video.json
Use only roles declared by the selected Catalog binding. references and input_reference are mutually exclusive. JSON references use ordered {"role":"...","url":"https://..."} objects and accept only public HTTPS destinations.
Idempotency And Errors
Images and Videos accept optional Idempotency-Key. Supply one for durable retries and reuse it only for identical normalized fields and media. A conflict fails before another reservation or supplier attempt. Desktop always persists and sends this identity.
Models, Images, and Videos use the OpenAI error envelope under error. Catalog, estimate, and usage use {code, message}. 401 means the account key is unavailable, 402 means the balance cannot fund the request, 409 is an idempotency conflict or content-not-ready result, and 429 may include Retry-After. provider_unavailable means every compatible hidden route failed before acceptance. Public errors never include supplier responses, URLs, credentials, routes, or diagnostics.
Direct HTTP does not write a Desktop project. The caller owns downloaded bytes and any lineage it keeps.