REST API
Ship Check
On this page
Prompt versioning, typing, and evaluation — plus a zero-LLM PR gate. Ship Check treats prompts as versioned artifacts so a prompt change is reviewable like a code change, and catches regressions before they ship.
List prompts
GET /v1/observ/orgs/{org}/workspaces/{ws}/shipcheck/prompts
Every prompt key seen in the window, with its current version and usage.
Auth: API key or JWT · Role: viewer
Query parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
days | integer | ➖ | 7 | Lookback window in days |
Response 200 — not schema-modelled
{
"prompts": [
{
"prompt_key": "checkout.system",
"current_version": "v7",
"version_count": 7,
"call_count": 8120,
"total_cost_usd": 141.20,
"last_seen": "2026-09-01T14:23:05Z"
}
]
}
Examples
p = evigauge.get("/v1/observ/orgs/{org}/workspaces/{ws}/shipcheck/prompts",
params={"days": 30})
for x in p["prompts"]:
print(f"{x['prompt_key']:30} {x['current_version']:6} ${x['total_cost_usd']:.2f}")
const p = await evigauge.get(
"/v1/observ/orgs/{org}/workspaces/{ws}/shipcheck/prompts", { days: 30 });
p.prompts.forEach((x: any) =>
console.log(`${x.prompt_key.padEnd(30)} ${x.current_version} $${x.total_cost_usd.toFixed(2)}`));
List prompt versions
GET /v1/observ/orgs/{org}/workspaces/{ws}/shipcheck/prompts/{prompt_key}/versions
Auth: API key or JWT · Role: viewer
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
org | string | ✅ | Organization slug |
ws | string | ✅ | Workspace slug |
prompt_key | string | ✅ | Prompt identifier, URL-encoded if it contains / |
Query parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
days | integer | ➖ | 30 | Lookback window in days |
Examples
from urllib.parse import quote
key = quote("checkout.system", safe="")
v = evigauge.get(
f"/v1/observ/orgs/{{org}}/workspaces/{{ws}}/shipcheck/prompts/{key}/versions",
params={"days": 90},
)
print(v)
const key = encodeURIComponent("checkout.system");
const v = await evigauge.get(
`/v1/observ/orgs/{org}/workspaces/{ws}/shipcheck/prompts/${key}/versions`, { days: 90 });
console.log(v);
Get one prompt version
GET /v1/observ/orgs/{org}/workspaces/{ws}/shipcheck/prompts/{prompt_key}/versions/{version}
Auth: API key or JWT · Role: viewer
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
org, ws | string | ✅ | Slugs |
prompt_key | string | ✅ | Prompt identifier |
version | string | ✅ | Version identifier, e.g. v7 |
Query parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
days | integer | ➖ | 30 | Lookback window in days |
Examples
d = evigauge.get(
f"/v1/observ/orgs/{{org}}/workspaces/{{ws}}/shipcheck/prompts/{key}/versions/v7"
)
print(d)
const d = await evigauge.get(
`/v1/observ/orgs/{org}/workspaces/{ws}/shipcheck/prompts/${key}/versions/v7`);
console.log(d);
Diff two prompt versions
GET /v1/observ/orgs/{org}/workspaces/{ws}/shipcheck/prompts/{prompt_key}/diff
Text diff plus the behavioural delta — cost, latency and grade change — between two versions.
Auth: API key or JWT · Role: viewer
Query parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
from_version | string | ✅ | — | Baseline version |
to_version | string | ✅ | — | Comparison version |
days | integer | ➖ | 30 | Lookback window in days |
Examples
diff = evigauge.get(
f"/v1/observ/orgs/{{org}}/workspaces/{{ws}}/shipcheck/prompts/{key}/diff",
params={"from_version": "v6", "to_version": "v7"},
)
print(diff)
const diff = await evigauge.get(
`/v1/observ/orgs/{org}/workspaces/{ws}/shipcheck/prompts/${key}/diff`,
{ from_version: "v6", to_version: "v7" },
);
console.log(diff);
Errors
| Status | Cause |
|---|---|
422 | from_version or to_version omitted — both are required |
404 | A named version does not exist inside the days window |
Pipeline diff
GET /v1/observ/orgs/{org}/workspaces/{ws}/shipcheck/pipeline/diff
What changed across the whole prompt pipeline in the window — every prompt whose version moved, and the aggregate effect.
Auth: API key or JWT · Role: viewer
Query parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
days | integer | ➖ | 7 | Lookback window in days |
Examples
pd = evigauge.get("/v1/observ/orgs/{org}/workspaces/{ws}/shipcheck/pipeline/diff",
params={"days": 14})
print(pd)
const pd = await evigauge.get(
"/v1/observ/orgs/{org}/workspaces/{ws}/shipcheck/pipeline/diff", { days: 14 });
console.log(pd);
Reprice a prompt
POST /v1/observ/orgs/{org}/workspaces/{ws}/shipcheck/reprice
Recompute what a prompt's historical traffic would have cost on a different model. A pure what-if — it replays nothing and changes nothing.
Auth: API key or JWT · Role: viewer
Request body — RepriceBody
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
prompt_key | string | ✅ | — | Prompt to reprice |
model | string | ✅ | — | Target model id, e.g. claude-sonnet-4-6 |
provider | string | ➖ | "" | Provider hint when the model id is ambiguous |
days | integer | ➖ | 30 | Window of historical traffic to reprice |
Examples
rp = evigauge.post("/v1/observ/orgs/{org}/workspaces/{ws}/shipcheck/reprice", json={
"prompt_key": "checkout.system",
"model": "claude-sonnet-4-6",
"days": 30,
})
print(rp)
const rp = await evigauge.post(
"/v1/observ/orgs/{org}/workspaces/{ws}/shipcheck/reprice",
{ prompt_key: "checkout.system", model: "claude-sonnet-4-6", days: 30 },
);
console.log(rp);
Submit a candidate prompt
POST /v1/observ/shipcheck/candidate
Evaluate a proposed prompt against the live baseline before shipping it. This is the endpoint the CI gate calls on a pull request.
Auth: API key only (x-opexia-api-key / dr_*) — this endpoint does not
accept a JWT, because it is designed for CI. Workspace scope comes from the key,
so there is no {org} / {ws} in the path.
Request body — CandidateBody
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
candidate_text | string | ✅ | — | The proposed prompt text |
prompt_key | string | null | ➖ | null | Existing prompt to compare against |
name | string | null | ➖ | null | Human label for the candidate |
model | string | ➖ | "" | Model the candidate targets |
provider | string | ➖ | "" | Provider hint |
days | integer | ➖ | 7 | Baseline window in days |
policy | object | ➖ | {} | Gate policy overrides (thresholds) |
Examples
import httpx, os, pathlib
candidate = pathlib.Path("prompts/checkout.system.md").read_text()
r = httpx.post(
"https://api.opexia.dev/v1/observ/shipcheck/candidate",
headers={"x-opexia-api-key": os.environ["OPEXIA_API_KEY"]},
json={
"candidate_text": candidate,
"prompt_key": "checkout.system",
"model": "claude-opus-4-6",
"days": 7,
},
timeout=120,
)
r.raise_for_status()
result = r.json()
# Fail the CI job when the gate does not pass.
if result.get("verdict") != "pass":
raise SystemExit(f"Ship Check failed: {result}")
import { readFile } from "node:fs/promises";
const candidate = await readFile("prompts/checkout.system.md", "utf8");
const res = await fetch("https://api.opexia.dev/v1/observ/shipcheck/candidate", {
method: "POST",
headers: {
"x-opexia-api-key": process.env.OPEXIA_API_KEY!,
"content-type": "application/json",
},
body: JSON.stringify({
candidate_text: candidate,
prompt_key: "checkout.system",
model: "claude-opus-4-6",
days: 7,
}),
});
const result = await res.json();
// Fail the CI job when the gate does not pass.
if (result.verdict !== "pass") {
console.error("Ship Check failed:", result);
process.exit(1);
}
The
opexia shipcheckCLI wraps this endpoint with policy loading and PR comment rendering — see the SDK reference.