Model APIs
Streaming
On this page
There is no progressive media output; models return finished files. Server-sent events stream job status and agent session events so your application can react while work is in progress.
Job status over SSE #
How it works #
Call POST /jobs/{id}/sse-ticket with your bearer token, then open GET /jobs/{id}/sse?ticket=…. A ticket expires after 60 seconds and authorizes exactly one stream connection; the stream uses only the ticket, with no bearer header. It sends the current state immediately, follows changes, then emits complete and closes for a terminal job. No client polling is needed.
Set JOB_ID to an accepted job id. These TypeScript, Python and Rust blocks are examples, not live-proven programs. They print the wire frames without assuming that a network chunk is a complete event. The Rust example additionally requires futures-util and the stream feature on reqwest.
curl -sS -X POST "https://api.nolgia.ai/v1/jobs/$JOB_ID/sse-ticket" -H "Authorization: Bearer $NOLGIA_TOKEN"
curl -N "https://api.nolgia.ai/v1/jobs/$JOB_ID/sse?ticket=<ticket from the response>"
# A job SSE command is not available in the CLI; use long-poll instead.
nolgia wait "$JOB_ID"
import { createNolgiaClient } from "@nolgia/sdk";
const nolgia = createNolgiaClient(process.env.NOLGIA_TOKEN!);
const id = process.env.JOB_ID!;
const { data, error } = await nolgia.POST("/jobs/{id}/sse-ticket", {
params: { path: { id } },
});
if (error) throw new Error(`${error.title}: ${error.detail ?? ""}`);
const response = await fetch(`https://api.nolgia.ai/v1/jobs/${id}/sse?ticket=${encodeURIComponent(data.ticket)}`);
if (!response.ok || !response.body) throw new Error(`stream HTTP ${response.status}`);
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
for (;;) {
const { done, value } = await reader.read();
if (done) break;
process.stdout.write(value);
}
import os
import httpx
from nolgia import AuthenticatedClient
from nolgia.api.jobs import create_job_sse_ticket
from nolgia.models import SSETicket
client = AuthenticatedClient(base_url="https://api.nolgia.ai/v1", token=os.environ["NOLGIA_TOKEN"])
job_id = os.environ["JOB_ID"]
ticket = create_job_sse_ticket.sync(job_id, client=client)
if not isinstance(ticket, SSETicket):
raise SystemExit(f"refused: {ticket}")
with httpx.stream("GET", f"https://api.nolgia.ai/v1/jobs/{job_id}/sse", params={"ticket": ticket.ticket}, timeout=None) as response:
response.raise_for_status()
for line in response.iter_lines():
print(line)
use std::io::Write;
use futures_util::StreamExt;
use nolgia_client::ClientBuilder;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = ClientBuilder::new("https://api.nolgia.ai/v1")
.bearer_token(std::env::var("NOLGIA_TOKEN")?)
.build()?;
let id = std::env::var("JOB_ID")?.parse::<uuid::Uuid>()?;
let ticket = client.create_job_sse_ticket().id(id).send().await?.into_inner();
let response = reqwest::Client::new()
.get(format!("https://api.nolgia.ai/v1/jobs/{id}/sse"))
.query(&[("ticket", ticket.ticket)])
.send().await?.error_for_status()?;
let mut stream = response.bytes_stream();
while let Some(chunk) = stream.next().await {
std::io::stdout().write_all(&chunk?)?;
std::io::stdout().flush()?;
}
Ok(())
}
The ticket response is 201 Created; this is the captured response with the single-use secret redacted:
{
"ticket": "…",
"expires_at": "2026-09-21T03:34:15.423881465Z"
}
| Field | Type | Required | Description |
|---|---|---|---|
ticket |
string | Yes | Opaque single-use secret to pass as the ticket query parameter. |
expires_at |
string | Yes | Instant after which the ticket can no longer be redeemed. |
The following three frames are from the production stream. The stream's signed asset_url is shortened for display.
event: status
data: {"status":"queued"}
event: status
data: {"status":"running","progress":0}
event: complete
data: {"status":"succeeded","progress":0,"asset_url":"https://storage.googleapis.com/nolgia-generations-prod/dad27b53-a85b-4…"}
Parameters #
| Parameter | In | Required | Description |
|---|---|---|---|
id |
path | Yes | Job UUID. |
| Parameter | In | Required | Description |
|---|---|---|---|
id |
path | Yes | Job UUID. |
ticket |
query | Yes | Single-use ticket from POST /jobs/{id}/sse-ticket. |
Event reference #
| Event | Fields | Meaning |
|---|---|---|
status |
status, optional progress |
Current state immediately, then updates while queued or running; progress is a number from 0 to 1 when known. |
complete on success |
status: succeeded, optional progress, asset_url when available |
Terminal success; the stream closes. asset_url may be a gs:// URI or an expired signed URL. Fetch GET /jobs/{id} or GET /assets/{id} with bearer authentication for a fresh asset signed_url before downloading. |
complete on failure |
status: failed, optional error, failure_kind, failure_code, credits_refunded |
Terminal failure; read the typed failure and recorded refund result. |
complete on cancellation |
status: canceled, optional error (the plain statement of what the cancel did), credits_refunded once the hold settles |
The job was canceled with POST /jobs/{id}/cancel; read GET /jobs/{id} for the full cancellation. |
Fields are omitted when there is no value to report. Heartbeats are SSE comment lines, not state changes. The failure frame below is a schema-shaped example of a terminal state, not a production capture:
event: complete
data: {"status":"failed","error":"generation failed","failure_kind":"error","failure_code":"job_failed","credits_refunded":true}
Read GET /jobs/{id} for the full terminal Job and embedded asset. Store its id and download the file; do not retain signed URLs as permanent asset identifiers.
Agent session events #
GET /agent/sessions/{id}/events is the only live transport for agent turns. It begins with a retry: reconnection hint, puts a monotonic id: on each message frame, and replays persisted messages from the database when you reconnect with Last-Event-ID. It uses bearer authentication, unlike the ticket-based job stream.
curl --fail-with-body -N "https://api.nolgia.ai/v1/agent/sessions/$SESSION_ID/events" \
-H "Authorization: Bearer $NOLGIA_TOKEN"
# On reconnect, LAST_EVENT_ID is the last id received from a message frame.
curl --fail-with-body -N "https://api.nolgia.ai/v1/agent/sessions/$SESSION_ID/events" \
-H "Authorization: Bearer $NOLGIA_TOKEN" \
-H "Last-Event-ID: $LAST_EVENT_ID"
| Frame detail | Purpose |
|---|---|
retry: |
Server hint for reconnection delay. |
id: on message |
Cursor to retain for reconnect. |
Last-Event-ID request header |
Resume persisted message replay from the database. |
See Agent Sessions API for session creation, turns and event payloads.
When to use streaming #
Use SSE for a live status display or agent conversation. Use long-poll for a worker that simply needs the finished job: it receives a normal JSON response and can repeat a timed-out wait. Neither transport changes the generation, its deadline or its credit hold.
Error responses #
| Operation | Status | Meaning |
|---|---|---|
| Mint ticket | 401 |
Missing or invalid bearer token. |
| Mint ticket | 404 |
Job not found for this caller. |
| Open stream | 401 |
Missing, expired or already-used ticket. |
| Open stream | 403 |
The ticket is bound to a different job and has been consumed. Mint a fresh ticket for the correct job. |
| Either job SSE endpoint | 503 |
Job streaming is not configured. |
These authentication and stream-availability problems do not carry a GenerationErrorCode. A generation failure is delivered as a terminal complete event with failure_code, rather than changing the already-open stream's HTTP status.

