# API surfaces (/concepts/api-surfaces)



Zumik exposes two public APIs over one internal execution system. They serve different goals and they are kept rigorously separate so that neither compromises the other.

## `/v1`: strict compatibility [#v1-strict-compatibility]

The purpose of `/v1` is migration with minimal code changes. You point an OpenAI client at `https://api.zumik.ai/v1`, change nothing else, and it works.

<CodeGroup>
  ```python title="Python"
  from openai import OpenAI

  client = OpenAI(base_url="https://api.zumik.ai/v1", api_key="zk_live_...")
  r = client.responses.create(model="code.balanced", input="Review the latest patch.")
  ```

  ```bash title="curl"
  curl https://api.zumik.ai/v1/responses \
    -H "Authorization: Bearer zk_live_..." \
    -H "Content-Type: application/json" \
    -d '{"model":"code.balanced","input":"Review the latest patch."}'
  ```
</CodeGroup>

The contract rule is absolute: mirror OpenAI request and response shapes exactly.

* Do not add proprietary JSON fields to request or response objects.
* Do not rename upstream fields or overload them with internal meaning.
* Do not invent `/v1` streaming events.

When a backend cannot implement an upstream feature correctly, the request is rejected with a compatible error or routed to a backend that can. Meaningful fields are never silently ignored.

<Info>
  `/v1` deliberately keeps surfaces small where OpenAI keeps them small. For example, `/v1/models` returns the exact upstream object shape (`id`, `object`, `created`, `owned_by`). Rich alias metadata lives on [`/v2/model-aliases`](/api-v2/model-aliases), not here.
</Info>

## `/v2`: native state [#v2-native-state]

The purpose of `/v2` is everything the OpenAI shape has no room for: explicit state objects, branch control, replay, purge receipts, QoS outcomes, and rich telemetry. These are first-class fields, not headers.

```bash
curl https://api.zumik.ai/v2/artifacts \
  -H "Authorization: Bearer zk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"artifact_type":"policy","content":"Run the linter before every commit."}'
```

`/v2` is REST-style and organized around the [state object model](/concepts/artifacts): artifacts, bundles, sessions and their branches, snapshots, responses, plus diagnostics, replay runs, purge jobs, and model aliases.

## The extension rule [#the-extension-rule]

This is the rule that lets both surfaces coexist without contaminating each other.

> Proprietary behavior is transported through optional HTTP headers or `/v2`, never by changing upstream JSON shapes.

A client that omits every Zumik header must still receive valid OpenAI behavior on `/v1`. The optional request headers are:

| Header                  | Carries                                                                        |
| ----------------------- | ------------------------------------------------------------------------------ |
| `Agent-Hints-Ref`       | A reference to a stored [Agent Hints](/concepts/agent-hints) object (`ah_...`) |
| `Agent-Session-Ref`     | A [session](/concepts/sessions) reference (`ses_...`)                          |
| `Agent-Trace-Mode`      | `metadata`, `tokenized`, or `encrypted_full_fidelity`                          |
| `Agent-Idempotency-Key` | An idempotency key for safe retries                                            |

Proprietary signals flow back the same way. QoS results, for instance, ride on response headers on `/v1` so the body stays OpenAI-shaped, while the full [QoS outcome](/concepts/qos) object is a JSON field on `/v2`.

```http
Agent-QoS-Admission: admitted
Agent-QoS-Target-Met: true
Agent-QoS-Fallback-Used: false
Agent-Trace-Id: trc_...
```

<Tabs>
  <Tab title="On /v1">
    Reuse capture is exposed through the standard `usage.prompt_tokens_details.cached_tokens` field plus headers. A vanilla OpenAI SDK can read it without knowing Zumik exists.
  </Tab>

  <Tab title="On /v2">
    The full reuse report, QoS outcome, alias resolution record, and trace ID are explicit response fields. Nothing is hidden in headers because there is no compatibility constraint to honor.
  </Tab>
</Tabs>

## Choosing a surface [#choosing-a-surface]

<CardGroup cols="2">
  <Card title="Migrating from OpenAI" icon="arrow-right-arrow-left" href="/guides/openai-migration">
    Start on `/v1`. The optimized path stays optional; the compatible path stays correct.
  </Card>

  <Card title="Building stateful agents" icon="layer-group" href="/guides/sessions-and-branching">
    Use `/v2` for artifacts, sessions, branches, and snapshots from the start.
  </Card>
</CardGroup>

<Note>
  You do not have to pick one. A common pattern is to keep request bodies on `/v1` for SDK compatibility while attaching state and hints by reference through headers, then read rich telemetry from the `/v2` usage and diagnostics endpoints.
</Note>
