> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trajectory.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy and promote

> Serve a checkpoint and make it the active model.

A deployment serves a [checkpoint](https://docs.trajectory.ai/guides/training.md) at an OpenAI-compatible endpoint.

## 1. Create a deployment

```python theme={null}
from trajectory import Client

client = Client()
deployable = [
    c for c in client.training.checkpoints.list("YOUR_TRAINING_RUN_ID")
    if c.is_deployable
]
if not deployable:
    raise RuntimeError("No deployable checkpoint is available for this training run")
checkpoint = max(deployable, key=lambda c: c.step_index)
deployment = client.deployments.create(
    checkpoint_id=checkpoint.checkpoint_id,
    test_model_slug="my-agent-test",
)
print(deployment.deployment_id)
print(client.deployments.retrieve(deployment.deployment_id).status)
```

## 2. Test the deployment

In the same Python session, wait for `DEPLOYED`; stop on `FAILED` or `CANCELLED`.
Until it is promoted, a deployment answers only on its own test slug. Install `openai` for
this optional smoke test, then query the exact route with a new trajectory ID:

```python theme={null}
import os
import time
from contextlib import suppress

from openai import OpenAI
from trajectory import Client

trajectory = Client()
deadline = time.monotonic() + 900
while True:
    state = trajectory.deployments.retrieve(deployment.deployment_id)
    if state.status == "DEPLOYED":
        break
    if state.status in {"FAILED", "CANCELLED"}:
        raise RuntimeError(f"Deployment {state.status}: {state.failure}")
    if time.monotonic() >= deadline:
        raise TimeoutError(f"Deployment still {state.status} after 15 minutes")
    time.sleep(10)

tid = trajectory.trajectories.create().tid
api_origin = os.environ.get("TRAJECTORY_BASE_URL", "https://api.trajectory.ai").rstrip("/")
inference = OpenAI(
    api_key=os.environ["TRAJECTORY_API_KEY"],
    base_url=f"{api_origin}/api/v1/deploy/{deployment.deployment_id}",
)
try:
    response = inference.chat.completions.create(
        model=state.test_model_slug,
        messages=[{"role": "user", "content": "Hello!"}],
        extra_headers={"X-Trajectory-Id": tid},
    )
    choice = response.choices[0]
    answer = (choice.message.content or "").strip()
except Exception:
    with suppress(Exception):
        trajectory.trajectories.complete(tid, termination_reason="ERROR")
    raise
reason = (
    "TRUNCATION" if choice.finish_reason in ("length", "max_tokens")
    else "ENV_DONE" if choice.finish_reason == "stop" and answer
    else "ERROR"
)
trajectory.trajectories.complete(tid, termination_reason=reason)
if reason != "ENV_DONE":
    raise RuntimeError(f"No complete answer: {choice.finish_reason}")
print(answer)
```

The OpenAI-compatible route accepts the
organization key as Bearer authentication and requires `X-Trajectory-Id`; SDK resource calls
use `X-API-Key`.

## 3. Promote

Callers reach production through the owning agent's production model slug, which is
separate from each deployment's test slug. Set it once per agent, then promote. Promotion
points that slug at this deployment; no separate model-endpoint creation is needed. Both
steps are also available from **Deployments** in the platform.

```python theme={null}
from trajectory import Client

client = Client()
state = client.deployments.retrieve(deployment.deployment_id)
if state.production_model_slug is None:
    client.model_slugs.create_production(state.agent_id, production_model_slug="my-agent")
# Pass the current production deployment ID instead of None when replacing one.
client.deployments.promote(state.deployment_id, expected_deployment_id=None)
```

Call the production slug through the SDK, again with a new trajectory:

```python theme={null}
from contextlib import suppress

from trajectory import Client

client = Client()
tid = client.trajectories.create().tid
try:
    response = client.chat.completions.create(
        model="my-agent",
        messages=[{"role": "user", "content": "Hello!"}],
        x_trajectory_id=tid,
    )
    choice = response.choices[0]
    answer = (choice.message.content or "").strip()
except Exception:
    with suppress(Exception):
        client.trajectories.complete(tid, termination_reason="ERROR")
    raise
reason = (
    "TRUNCATION" if choice.finish_reason in ("length", "max_tokens")
    else "ENV_DONE" if choice.finish_reason == "stop" and answer
    else "ERROR"
)
client.trajectories.complete(tid, termination_reason=reason)
if reason != "ENV_DONE":
    raise RuntimeError(f"No complete answer: {choice.finish_reason}")
print(answer)
```

[Collect production traces](https://docs.trajectory.ai/guides/production-traces.md) to learn from the application after it is serving.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.