---
title: Platform headers
description: The HTTP headers and query parameters that change how a request behaves, one section each.
---

# Platform headers

Use bearer authentication for API calls, an idempotency key for generation retries, and a request id to connect a problem response to your logs. The two query parameters below control a wait or authorize a status stream; they are not headers.

There are no priority, runner-hint, retry-config or payload-storage headers.

The examples use the same client construction as the [Quick Start](./getting-started.html). Set `NOLGIA_TOKEN` on your server and `JOB_ID` to a job you own. TypeScript examples run on the server; see [Proxy setup](./proxy-setup.html) before calling from a browser.

## Authorization

| Property | Value |
| --- | --- |
| Type | HTTP request header; string |
| Default | None; authenticate protected routes |
| Values | `Bearer` followed by a PAT beginning `nol_` or a JWT |
| Applies to | Authenticated API routes; the job SSE route uses a ticket instead |
| SDK parameter | TypeScript client token; Python `token`; Rust builder `bearer_token` |

The token identifies the caller and its available permissions. Put it in the environment, never in a checked-in source file.

```bash tab="curl"
$ curl -s https://api.nolgia.ai/v1/me \
  -H "Authorization: Bearer $NOLGIA_TOKEN"
```
```ts tab="TypeScript"
import { createNolgiaClient } from "@nolgia/sdk";

const nolgia = createNolgiaClient(process.env.NOLGIA_TOKEN!);
const { data, error } = await nolgia.GET("/me");
if (error) throw new Error(`${error.title}: ${error.detail ?? ""}`);
console.log(data);
```
```python tab="Python"
import os
from nolgia import AuthenticatedClient
from nolgia.api.auth import get_current_user
from nolgia.models import User

client = AuthenticatedClient(base_url="https://api.nolgia.ai/v1", token=os.environ["NOLGIA_TOKEN"])
account = get_current_user.sync(client=client)
if not isinstance(account, User):
    raise SystemExit(f"refused: {account}")
print(account.to_dict())
```

> [!WARNING]
> A `401` means the credential is missing, bad or expired. Fix authentication before trying again. A `402` with `code: out_of_credits` means the wallet cannot pay for the generation; replacing a valid token will not replenish it.

## Content-Type

| Property | Value |
| --- | --- |
| Type | HTTP request header; media type |
| Default | Typed clients set it when serializing a JSON body |
| Values | `application/json` for the generation and quote examples |
| Applies to | API requests carrying JSON |
| SDK parameter | TypeScript `body`; Python generated request model passed as `body` |

Declare the representation you send. The typed clients serialize the request and set the JSON content type for you.

```bash tab="curl"
$ curl -s https://api.nolgia.ai/v1/generate/image \
  -H "Authorization: Bearer $NOLGIA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"model":"flux-pro","prompt":"a paper-cut mountain range at dawn"}'
```
```ts tab="TypeScript"
import { createNolgiaClient } from "@nolgia/sdk";

const nolgia = createNolgiaClient(process.env.NOLGIA_TOKEN!);
const { data: image, error } = await nolgia.POST("/generate/image", {
  body: { model: "flux-pro", prompt: "a paper-cut mountain range at dawn", num_images: 1 },
});
if (error) throw new Error(`${error.title}: ${error.detail ?? ""}`);
console.log(image.id);
```
```python tab="Python"
import os
from nolgia import AuthenticatedClient
from nolgia.api.generate import generate_image
from nolgia.models import GenerateImageRequest, ImageModel, Job

client = AuthenticatedClient(base_url="https://api.nolgia.ai/v1", token=os.environ["NOLGIA_TOKEN"])
job = generate_image.sync(client=client, body=GenerateImageRequest(model=ImageModel("flux-pro"), prompt="a paper-cut mountain range at dawn"))
if not isinstance(job, Job):
    raise SystemExit(f"refused: {job}")
print(job.id)
```

## Idempotency-Key

| Property | Value |
| --- | --- |
| Type | HTTP request header; string |
| Default | Without a key, identical request bodies still share the duplicate window |
| Values | A stable caller-chosen key for one intended generation |
| Applies to | Generation submission; five-minute claim window |
| SDK parameter | TypeScript client `options.headers`; Python client `headers`; Rust builder `idempotency_key` |

The same body with the same key, or with no key both times, returns `409 Conflict` inside five minutes and names the earlier `job_id`. A different key means a deliberate new generation of the same body and can return `202`. The `409` is never billed. The CLI accepts `--idempotency-key` or `NOLGIA_IDEMPOTENCY_KEY`.

```bash tab="curl"
$ curl -s https://api.nolgia.ai/v1/generate/image \
  -H "Authorization: Bearer $NOLGIA_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: mountain-take-1" \
  -d '{"model":"flux-pro","prompt":"a paper-cut mountain range at dawn"}'
```
```ts tab="TypeScript"
import { createNolgiaClient } from "@nolgia/sdk";

const nolgia = createNolgiaClient(process.env.NOLGIA_TOKEN!, undefined, {
  headers: { "Idempotency-Key": "mountain-take-1" },
});
const { data: image, error } = await nolgia.POST("/generate/image", {
  body: { model: "flux-pro", prompt: "a paper-cut mountain range at dawn", num_images: 1 },
});
if (error) throw new Error(`${error.title}: ${error.detail ?? ""}`);
console.log(image.id);
```
```python tab="Python"
import os
from nolgia import AuthenticatedClient
from nolgia.api.generate import generate_image
from nolgia.models import GenerateImageRequest, ImageModel, Job

client = AuthenticatedClient(base_url="https://api.nolgia.ai/v1", token=os.environ["NOLGIA_TOKEN"], headers={"Idempotency-Key": "mountain-take-1"})
job = generate_image.sync(client=client, body=GenerateImageRequest(model=ImageModel("flux-pro"), prompt="a paper-cut mountain range at dawn"))
if not isinstance(job, Job):
    raise SystemExit(f"refused: {job}")
print(job.id)
```

> [!TIP]
> On a duplicate response, follow `job_id`. Changing the key to make a retry succeed creates another paid generation; only do that when you want another take.

## X-Nolgia-Surface

| Property | Value |
| --- | --- |
| Type | Optional HTTP request header; string |
| Default | Omitted; a server-authenticated platform agent can still receive `hermes` attribution |
| Values | The known calling surfaces below; the header is not an enum allow-list |
| Applies to | Calling-surface attribution on generation and related writes |
| SDK parameter | TypeScript client `options.headers`; Python client `headers`; Rust builder `surface` |

| Known value | Calling surface |
| --- | --- |
| `cli` | Nolgia CLI |
| `claude-code` | Claude Code integration |
| `codex` | Codex integration |
| `hermes` | Legacy value naming the platform's own agent (NOLGIA Agent). Kept for compatibility; not a product name. |
| `pipeline` | Pipeline runtime |

The CLI sets this header. Values are trimmed, lowercased and capped at 64 characters when persisted with a generation. Use `pipeline` for your own pipeline. `hermes` names the platform's agent: sending that string does not turn a customer token into a server-minted agent credential. The header itself is client-supplied attribution, not proof of identity.

```bash tab="curl"
$ curl -s https://api.nolgia.ai/v1/generate/image \
  -H "Authorization: Bearer $NOLGIA_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Nolgia-Surface: pipeline" \
  -d '{"model":"flux-pro","prompt":"a paper-cut mountain range at dawn"}'
```
```ts tab="TypeScript"
import { createNolgiaClient } from "@nolgia/sdk";

const nolgia = createNolgiaClient(process.env.NOLGIA_TOKEN!, undefined, {
  headers: { "X-Nolgia-Surface": "pipeline" },
});
const { data: image, error } = await nolgia.POST("/generate/image", {
  body: { model: "flux-pro", prompt: "a paper-cut mountain range at dawn", num_images: 1 },
});
if (error) throw new Error(`${error.title}: ${error.detail ?? ""}`);
console.log(image.id);
```
```python tab="Python"
import os
from nolgia import AuthenticatedClient
from nolgia.api.generate import generate_image
from nolgia.models import GenerateImageRequest, ImageModel, Job

client = AuthenticatedClient(base_url="https://api.nolgia.ai/v1", token=os.environ["NOLGIA_TOKEN"], headers={"X-Nolgia-Surface": "pipeline"})
job = generate_image.sync(client=client, body=GenerateImageRequest(model=ImageModel("flux-pro"), prompt="a paper-cut mountain range at dawn"))
if not isinstance(job, Job):
    raise SystemExit(f"refused: {job}")
print(job.id)
```

## X-Request-Id

| Property | Value |
| --- | --- |
| Type | Optional HTTP request header; string |
| Default | Request middleware assigns an id when absent |
| Values | Your request correlation id |
| Applies to | Request tracing and the `request_id` field in problem responses |
| SDK parameter | TypeScript client `options.headers`; Python client `headers` |

Save the request id with your own logs. The API accepts the header, includes it in its CORS allow-list, and echoes the middleware request id as `request_id` in problem bodies. Use `Idempotency-Key`, described above, when intentionally grouping retries of one generation.

```bash tab="curl"
$ curl -s https://api.nolgia.ai/v1/me \
  -H "Authorization: Bearer $NOLGIA_TOKEN" \
  -H "X-Request-Id: mountain-request-1"
```
```ts tab="TypeScript"
import { createNolgiaClient } from "@nolgia/sdk";

const nolgia = createNolgiaClient(process.env.NOLGIA_TOKEN!, undefined, {
  headers: { "X-Request-Id": "mountain-request-1" },
});
const { data, error } = await nolgia.GET("/me");
if (error) throw new Error(`${error.title}: ${error.detail ?? ""}`);
console.log(data);
```
```python tab="Python"
import os
from nolgia import AuthenticatedClient
from nolgia.api.auth import get_current_user
from nolgia.models import User

client = AuthenticatedClient(base_url="https://api.nolgia.ai/v1", token=os.environ["NOLGIA_TOKEN"], headers={"X-Request-Id": "mountain-request-1"})
account = get_current_user.sync(client=client)
if not isinstance(account, User):
    raise SystemExit(f"refused: {account}")
print(account.to_dict())
```

## timeout_seconds

| Property | Value |
| --- | --- |
| Type | Query parameter; integer seconds |
| Default | OpenAPI declares 300 seconds; the current handler uses 30 seconds when omitted, so pass an explicit value |
| Values | 1 through 900 |
| Applies to | `GET /jobs/{id}/wait` |
| SDK parameter | TypeScript `params.query.timeout_seconds`; Python `timeout_seconds`; Rust request builder `timeout_seconds` |

The server holds this request until the job is terminal or the wait window closes. This parameter changes how long you wait for a response, not the job's generation deadline.

```bash tab="curl"
$ curl -s "https://api.nolgia.ai/v1/jobs/$JOB_ID/wait?timeout_seconds=120" \
  -H "Authorization: Bearer $NOLGIA_TOKEN"
```
```ts tab="TypeScript"
import { createNolgiaClient } from "@nolgia/sdk";

const nolgia = createNolgiaClient(process.env.NOLGIA_TOKEN!);
const { data, error, response } = await nolgia.GET("/jobs/{id}/wait", {
  params: { path: { id: process.env.JOB_ID! }, query: { timeout_seconds: 120 } },
});
if (response.status === 408) console.log("Still running; wait again.");
else if (error) throw new Error(`${error.title}: ${error.detail ?? ""}`);
else console.log(data);
```
```python tab="Python"
import os
from nolgia import AuthenticatedClient
from nolgia.api.jobs import wait_for_job
from nolgia.models import Job

client = AuthenticatedClient(base_url="https://api.nolgia.ai/v1", token=os.environ["NOLGIA_TOKEN"])
response = wait_for_job.sync_detailed(os.environ["JOB_ID"], client=client, timeout_seconds=120)
if response.status_code == 408:
    print("Still running; wait again.")
elif isinstance(response.parsed, Job):
    print(response.parsed.to_dict())
else:
    raise SystemExit(f"refused: {response.parsed}")
```

> [!NOTE]
> A `408` from `wait` is not a job failure. Wait again with the same job id; do not submit the generation again.

<!-- gen:params op=waitForJob -->
| Parameter | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | Yes | Job UUID. |
| `timeout_seconds` | query | No |  |
<!-- /gen -->

## ticket

| Property | Value |
| --- | --- |
| Type | Query parameter; opaque string |
| Default | None; required for the job stream |
| Values | A fresh ticket from `POST /jobs/{id}/sse-ticket`, valid for 60 seconds and one use |
| Applies to | `GET /jobs/{id}/sse` |
| SDK parameter | Ticket endpoint's `ticket` response field, passed in the stream URL |

Create the ticket with your bearer token, then use it as the stream's only credential. Do not add a bearer header to the stream request. On reconnect, issue a new ticket; a consumed ticket cannot be reused. The TypeScript and Python stream readers below are examples that print the received SSE bytes; [Streaming](./streaming.html) explains the frames.

```bash tab="curl"
$ 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>"
```
```ts tab="TypeScript" title="example"
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: ${response.status}`);
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
for (;;) {
  const { done, value } = await reader.read();
  if (done) break;
  process.stdout.write(value);
}
```
```python tab="Python" title="example"
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)
```

## Response headers

These headers describe a response; do not send them to configure model retries.

| Header | Value and where it appears | What to do |
| --- | --- | --- |
| `Retry-After` | Seconds until retry: public share resolver `429` responses; OAuth retry responses use `5`; busy edit sessions use `2` | Wait at least that interval before retrying the refused operation |
| `Retry-After-Reset` | RFC 3339 reset timestamp on the per-account generation-quota `429` | Wait until the quota resets; a concurrent-generation refusal does not supply this header |
| `Content-Type: application/problem+json` | Authentication and handler problem responses | Parse the problem body and branch on `status` and, when present, `code` |
| `Content-Type: application/json` | Ordinary JSON API responses | Parse the endpoint's response schema |
| `Content-Type: text/event-stream` | Job and agent SSE streams | Consume event frames until the stream closes |

<!-- gen:fields schema=Error -->
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `code` | string | No | Machine-readable error code.… |
| `type` | string | Yes | A URI reference identifying the problem type. |
| `title` | string | Yes |  |
| `status` | integer | Yes |  |
| `detail` | string | No |  |
| `instance` | string | No |  |
| `job_id` | string | No | The job this refusal points at, so a client can follow it without reading `detail`.… |
| `request_id` | string | No |  |
<!-- /gen -->

## Next steps

:::cards
- [Asynchronous: submit and poll](./jobs.html): Submit once, follow the id and read the finished asset.
- [Concurrency limits](./concurrency-limits.html): Understand the two kinds of generation rate-limit refusal.
- [Errors](./errors.html): Read typed problems and choose whether to retry.
:::
