Skip to main content

REST API Reference

All Observe API endpoints live under /api/v1/observe/ on your Waxell control plane.

Authentication

Every request must include one of:

  • API key header: X-Wax-Key: wax_sk_...
  • Bearer token: Authorization: Bearer wax_sk_...
info

Endpoints under /api/v1/observe/ use API key authentication (for SDK/programmatic access).

Endpoints under /api/v1/observability/, /api/v1/evaluations/, and /api/v1/prompts/ use session authentication (for the web UI). These require an authenticated user session (cookie-based or JWT).


Runs

Start a Run

Start a new execution run for an agent.

POST /api/v1/observe/runs/start/

Request Body:

{
"agent_name": "my-agent",
"workflow_name": "default",
"inputs": {},
"metadata": {},
"trace_id": ""
}
FieldTypeRequiredDescription
agent_namestringYesName of the agent
workflow_namestringNoWorkflow name (default: "default")
inputsobjectNoInput data for this run
metadataobjectNoArbitrary metadata
trace_idstringNoExternal trace ID for correlation

Response:

{
"run_id": "run_abc123",
"workflow_id": "wf_xyz789",
"started_at": "2025-01-15T10:30:00Z"
}

Example:

curl -X POST "https://acme.waxell.dev/api/v1/observe/runs/start/" \
-H "X-Wax-Key: wax_sk_..." \
-H "Content-Type: application/json" \
-d '{
"agent_name": "support-bot",
"workflow_name": "handle-ticket",
"inputs": {"query": "How do I reset my password?"}
}'

Complete a Run

Mark an execution run as completed.

POST /api/v1/observe/runs/{run_id}/complete/

Request Body:

{
"result": {},
"status": "success",
"error": "",
"steps": []
}
FieldTypeRequiredDescription
resultobjectNoResult data from the run
statusstringNo"success" or "error" (default: "success")
errorstringNoError message if the run failed
stepsarrayNoAdditional steps to record with completion

Response:

{
"status": "ok"
}

Example:

curl -X POST "https://acme.waxell.dev/api/v1/observe/runs/run_abc123/complete/" \
-H "X-Wax-Key: wax_sk_..." \
-H "Content-Type: application/json" \
-d '{
"result": {"answer": "Click Forgot Password on the login page."},
"status": "success"
}'

Record LLM Calls

Record one or more LLM API calls for a run.

POST /api/v1/observe/runs/{run_id}/llm-calls/

Request Body:

{
"calls": [
{
"model": "gpt-4o",
"tokens_in": 150,
"tokens_out": 80,
"cost": 0.001175,
"task": "answer_question",
"prompt_preview": "How do I reset...",
"response_preview": "Click Forgot Password..."
}
]
}
FieldTypeRequiredDescription
callsarrayYesArray of LLM call objects
calls[].modelstringYesModel identifier
calls[].tokens_inintegerYesInput token count
calls[].tokens_outintegerYesOutput token count
calls[].costnumberNoCost in USD
calls[].taskstringNoLabel for this call
calls[].prompt_previewstringNoPreview of the prompt
calls[].response_previewstringNoPreview of the response

Response:

{
"status": "ok",
"count": 1
}

Example:

curl -X POST "https://acme.waxell.dev/api/v1/observe/runs/run_abc123/llm-calls/" \
-H "X-Wax-Key: wax_sk_..." \
-H "Content-Type: application/json" \
-d '{
"calls": [
{
"model": "gpt-4o",
"tokens_in": 150,
"tokens_out": 80,
"cost": 0.001175,
"task": "answer_question"
}
]
}'

Record Steps

Record execution steps for a run.

POST /api/v1/observe/runs/{run_id}/steps/

Request Body:

{
"steps": [
{
"step_name": "retrieve_context",
"output": {"doc_count": 5}
},
{
"step_name": "generate_answer",
"output": {"length": 142}
}
]
}
FieldTypeRequiredDescription
stepsarrayYesArray of step objects
steps[].step_namestringYesName identifying the step
steps[].outputobjectNoOutput data for the step

Response:

{
"status": "ok",
"count": 2
}

Example:

curl -X POST "https://acme.waxell.dev/api/v1/observe/runs/run_abc123/steps/" \
-H "X-Wax-Key: wax_sk_..." \
-H "Content-Type: application/json" \
-d '{
"steps": [
{"step_name": "retrieve_context", "output": {"doc_count": 5}},
{"step_name": "generate_answer", "output": {"length": 142}}
]
}'

Record Scores (SDK)

Record scores (user feedback, quality metrics) for a run from the SDK.

POST /api/v1/observe/runs/{run_id}/scores/

Authentication: API key

Request Body:

{
"scores": [
{
"name": "accuracy",
"data_type": "numeric",
"numeric_value": 0.95,
"comment": "Very accurate response"
},
{
"name": "thumbs_up",
"data_type": "boolean",
"numeric_value": 1.0,
"string_value": "true"
}
]
}
FieldTypeRequiredDescription
scoresarrayYesArray of score objects
scores[].namestringYesScore name (e.g. "accuracy", "thumbs_up")
scores[].data_typestringNoOne of "numeric", "categorical", "boolean" (default: "numeric")
scores[].numeric_valuenumberNoNumeric score value
scores[].string_valuestringNoString score value (for categorical/boolean)
scores[].commentstringNoFree-text comment

Response:

{
"recorded": 2
}

Example:

curl -X POST "https://acme.waxell.dev/api/v1/observe/runs/42/scores/" \
-H "X-Wax-Key: wax_sk_..." \
-H "Content-Type: application/json" \
-d '{
"scores": [
{"name": "accuracy", "data_type": "numeric", "numeric_value": 0.95},
{"name": "thumbs_up", "data_type": "boolean", "numeric_value": 1.0}
]
}'

Fetch Prompt (SDK)

Retrieve a managed prompt for use in your agent code. Supports fetching by label, version number, or latest.

GET /api/v1/observe/prompts/{name}/

Authentication: API key

Query Parameters:

ParameterTypeRequiredDescription
labelstringNoFetch the version this label points to (e.g. "production")
versionintegerNoFetch an exact version number. Takes precedence over label

If neither label nor version is provided, the latest version (highest version number) is returned.

Response:

{
"name": "support-prompt",
"version": 3,
"prompt_type": "chat",
"content": [
{"role": "system", "content": "You are a helpful assistant for {{company}}."},
{"role": "user", "content": "{{user_query}}"}
],
"config": {"model": "gpt-4o", "temperature": 0.7},
"labels": ["production"]
}

Example:

# Fetch by label
curl "https://acme.waxell.dev/api/v1/observe/prompts/support-prompt/?label=production" \
-H "X-Wax-Key: wax_sk_..."

# Fetch specific version
curl "https://acme.waxell.dev/api/v1/observe/prompts/support-prompt/?version=2" \
-H "X-Wax-Key: wax_sk_..."

# Fetch latest
curl "https://acme.waxell.dev/api/v1/observe/prompts/support-prompt/" \
-H "X-Wax-Key: wax_sk_..."

Record Behavior Spans

Record behavior tracking spans (tool calls, retrievals, decisions, reasoning, retries) for a run.

POST /api/v1/observe/runs/{run_id}/spans/

Request Body:

{
"spans": [
{
"name": "web_search",
"kind": "tool",
"status": "ok",
"duration_ms": 250,
"position": 1,
"attributes": {"tool_type": "api"},
"input_data": {"query": "latest news"},
"output_data": {"results": ["..."]}
},
{
"name": "retrieve:pinecone",
"kind": "retriever",
"status": "ok",
"duration_ms": 120,
"position": 2,
"attributes": {"source": "pinecone", "doc_count": 5, "top_k": 10},
"input_data": {"query": "billing FAQ", "top_k": 10},
"output_data": {"documents": ["..."]}
},
{
"name": "route_to_agent",
"kind": "decision",
"status": "ok",
"duration_ms": null,
"position": 3,
"attributes": {"confidence": 0.95},
"input_data": {"options": ["billing", "technical"], "reasoning": "User mentioned invoice"},
"output_data": {"chosen": "billing", "confidence": 0.95}
}
]
}
FieldTypeRequiredDescription
spansarrayYesArray of span objects
spans[].namestringYesSpan name
spans[].kindstringYesOne of: "tool", "retriever", "decision", "reasoning", "retry"
spans[].statusstringNo"ok" or "error" (default: "ok")
spans[].duration_msintegerNoDuration in milliseconds
spans[].positionintegerNoOrdering position within the run
spans[].attributesobjectNoKind-specific attributes
spans[].input_dataobjectNoInput data for this span
spans[].output_dataobjectNoOutput data for this span

Response:

{
"status": "ok",
"count": 3
}

Example:

curl -X POST "https://acme.waxell.dev/api/v1/observe/runs/run_abc123/spans/" \
-H "X-Wax-Key: wax_sk_..." \
-H "Content-Type: application/json" \
-d '{
"spans": [
{
"name": "web_search",
"kind": "tool",
"status": "ok",
"duration_ms": 250,
"attributes": {"tool_type": "api"},
"input_data": {"query": "latest news"}
}
]
}'

Policy

Check Policy

Check whether an agent is allowed to execute.

POST /api/v1/observe/policy-check/

Request Body:

{
"agent_name": "my-agent",
"workflow_name": "default",
"agent_id": ""
}
FieldTypeRequiredDescription
agent_namestringYesName of the agent
workflow_namestringNoWorkflow name for scoped policies
agent_idstringNoSpecific agent instance ID

Response:

{
"action": "allow",
"reason": "",
"metadata": {}
}
FieldTypeDescription
actionstringOne of: "allow", "block", "warn", "throttle", "redact", "skip", "retry"
reasonstringHuman-readable explanation
metadataobjectAdditional data (e.g., budget info)

Example:

curl -X POST "https://acme.waxell.dev/api/v1/observe/policy-check/" \
-H "X-Wax-Key: wax_sk_..." \
-H "Content-Type: application/json" \
-d '{
"agent_name": "support-bot",
"workflow_name": "handle-ticket"
}'

Events

Record Events

Record governance or audit events.

POST /api/v1/observe/events/

Request Body:

{
"events": [
{
"event_type": "policy_check",
"agent_name": "my-agent",
"action": "allow",
"timestamp": "2025-01-15T10:30:00Z"
}
]
}
FieldTypeRequiredDescription
eventsarrayYesArray of event objects

Event objects are flexible -- include any fields relevant to your audit requirements.

Response:

{
"status": "ok",
"count": 1
}

Example:

curl -X POST "https://acme.waxell.dev/api/v1/observe/events/" \
-H "X-Wax-Key: wax_sk_..." \
-H "Content-Type: application/json" \
-d '{
"events": [
{
"event_type": "manual_override",
"agent_name": "support-bot",
"operator": "admin@acme.com",
"reason": "Emergency bypass"
}
]
}'

Model Costs

Get Model Costs

Retrieve the merged system + tenant model cost table.

GET /api/v1/observe/model-costs/

Response:

Returns the complete model cost table with tenant overrides applied on top of system defaults.

Example:

curl "https://acme.waxell.dev/api/v1/observe/model-costs/" \
-H "X-Wax-Key: wax_sk_..."

Set Model Cost Override

Set a tenant-level cost override for a specific model.

PUT /api/v1/observe/model-costs/{model_id}/

Request Body:

{
"input_cost_per_million": 2.00,
"output_cost_per_million": 8.00
}
FieldTypeRequiredDescription
input_cost_per_millionnumberYesInput token cost per 1M tokens (USD)
output_cost_per_millionnumberYesOutput token cost per 1M tokens (USD)

Example:

curl -X PUT "https://acme.waxell.dev/api/v1/observe/model-costs/gpt-4o/" \
-H "X-Wax-Key: wax_sk_..." \
-H "Content-Type: application/json" \
-d '{
"input_cost_per_million": 2.00,
"output_cost_per_million": 8.00
}'

Delete Model Cost Override

Remove a tenant-level cost override, reverting to system defaults.

DELETE /api/v1/observe/model-costs/{model_id}/

Example:

curl -X DELETE "https://acme.waxell.dev/api/v1/observe/model-costs/gpt-4o/" \
-H "X-Wax-Key: wax_sk_..."

Observability Endpoints (Session Auth)

These endpoints power the Observe web UI. They require an authenticated user session.

List LLM Calls

Search and filter LLM calls across all runs.

GET /api/v1/observability/llm-calls/

Authentication: Session

Query Parameters:

ParameterTypeDescription
searchstringFull-text search across model, task, prompt/response previews
modelstringFilter by model name (supports multiple via repeated param)
agentstringFilter by agent name
min_costnumberMinimum cost filter
max_costnumberMaximum cost filter
min_tokensintegerMinimum total token count
max_tokensintegerMaximum total token count
startstringStart date/time (ISO 8601)
endstringEnd date/time (ISO 8601)
sortstringSort field. One of: created_at, -created_at, cost, -cost, total_tokens, -total_tokens, model, -model (default: -created_at)
limitintegerPage size (default: 25, max: 100)
offsetintegerPagination offset (default: 0)

Response:

{
"results": [
{
"id": 42,
"run_id": 17,
"model": "gpt-4o",
"task": "answer_question",
"agent_name": "support-bot",
"workflow_name": "handle-ticket",
"tokens_in": 150,
"tokens_out": 80,
"total_tokens": 230,
"cost": 0.001175,
"created_at": "2025-01-15T10:30:00Z",
"prompt_preview": "How do I reset...",
"response_preview": "Click Forgot Password...",
"prompt_hash": "abc123..."
}
],
"count": 1,
"next": null,
"previous": null,
"aggregates": {
"total_cost": 0.001175,
"total_tokens": 230,
"total_calls": 1,
"models_used": ["gpt-4o"]
}
}

Example:

curl "https://acme.waxell.dev/api/v1/observability/llm-calls/?model=gpt-4o&limit=10" \
-H "Cookie: sessionid=..."

Get LLM Call Detail

Retrieve full details for a single LLM call.

GET /api/v1/observability/llm-calls/{call_id}/

Authentication: Session

Response:

{
"id": 42,
"run_id": 17,
"model": "gpt-4o",
"task": "answer_question",
"agent_name": "support-bot",
"workflow_name": "handle-ticket",
"tokens_in": 150,
"tokens_out": 80,
"total_tokens": 230,
"cost": 0.001175,
"created_at": "2025-01-15T10:30:00Z",
"prompt_preview": "Full prompt text...",
"response_preview": "Full response text...",
"prompt_hash": "abc123..."
}

Example:

curl "https://acme.waxell.dev/api/v1/observability/llm-calls/42/" \
-H "Cookie: sessionid=..."

List Distinct Models

Get the distinct model names used across all LLM calls. Useful for populating filter dropdowns.

GET /api/v1/observability/llm-calls/models/

Authentication: Session

Response:

{
"models": ["claude-3-5-sonnet", "gpt-4o", "gpt-4o-mini"]
}

Example:

curl "https://acme.waxell.dev/api/v1/observability/llm-calls/models/" \
-H "Cookie: sessionid=..."

List Sessions

List sessions with aggregated metrics. A session groups multiple runs that share a session_id.

GET /api/v1/observability/sessions/

Authentication: Session

Query Parameters:

ParameterTypeDescription
searchstringSearch by session ID
agentstringFilter by agent name
startstringStart date/time (ISO 8601)
endstringEnd date/time (ISO 8601)
sortstringOne of: last_activity, -last_activity, first_run, -first_run, run_count, -run_count (default: -last_activity)
limitintegerPage size (default: 25, max: 100)
offsetintegerPagination offset (default: 0)

Response:

{
"results": [
{
"session_id": "sess_a1b2c3d4e5f6g7h8",
"run_count": 5,
"first_run": "2025-01-15T10:00:00Z",
"last_activity": "2025-01-15T10:15:00Z",
"total_duration": 45.5,
"total_cost": 0.0523,
"total_tokens": 12500,
"agents": ["support-bot", "research-agent"]
}
],
"count": 1,
"next": null,
"previous": null
}

Example:

curl "https://acme.waxell.dev/api/v1/observability/sessions/?agent=support-bot" \
-H "Cookie: sessionid=..."

Get Session Detail

Get full session details including all runs and aggregated metrics.

GET /api/v1/observability/sessions/{session_id}/

Authentication: Session

Response:

{
"session_id": "sess_a1b2c3d4e5f6g7h8",
"aggregates": {
"run_count": 3,
"total_duration": 15.2,
"total_cost": 0.0234,
"total_tokens": 5600,
"agents": ["support-bot"]
},
"runs": [
{
"id": 17,
"agent_name": "support-bot",
"workflow_name": "handle-ticket",
"started_at": "2025-01-15T10:00:00Z",
"completed_at": "2025-01-15T10:00:05Z",
"duration": 5.1,
"status": "success",
"cost": 0.0078,
"tokens": 1800,
"trace_id": ""
}
]
}

Example:

curl "https://acme.waxell.dev/api/v1/observability/sessions/sess_a1b2c3d4e5f6g7h8/" \
-H "Cookie: sessionid=..."

List Users

List tracked end-users with aggregate metrics. Users are identified by the user_id passed during run creation.

GET /api/v1/observability/users/

Authentication: Session

Query Parameters:

ParameterTypeDescription
searchstringSearch by user ID
agentstringFilter by agent name
startstringStart date/time (ISO 8601)
endstringEnd date/time (ISO 8601)
sortstringOne of: last_seen, -last_seen, first_seen, -first_seen, run_count, -run_count (default: -last_seen)
limitintegerPage size (default: 25, max: 100)
offsetintegerPagination offset (default: 0)

Response:

{
"results": [
{
"user_id": "user_abc123",
"run_count": 42,
"first_seen": "2025-01-01T08:00:00Z",
"last_seen": "2025-01-15T10:30:00Z",
"total_duration": 210.5,
"total_cost": 0.5234,
"total_tokens": 125000,
"agents": ["support-bot", "research-agent"]
}
],
"count": 1,
"next": null,
"previous": null
}

Example:

curl "https://acme.waxell.dev/api/v1/observability/users/?sort=-run_count" \
-H "Cookie: sessionid=..."

Get User Detail

Get detailed information for a specific tracked user, including cost breakdown by model and recent runs.

GET /api/v1/observability/users/{user_id}/

Authentication: Session

Response:

{
"user_id": "user_abc123",
"aggregates": {
"run_count": 42,
"total_duration": 210.5,
"total_cost": 0.5234,
"total_tokens": 125000,
"agents": ["support-bot"],
"first_seen": "2025-01-01T08:00:00Z",
"last_seen": "2025-01-15T10:30:00Z"
},
"cost_by_model": [
{
"model": "gpt-4o",
"total_cost": 0.45,
"total_tokens": 100000,
"call_count": 30
}
],
"runs": [
{
"id": 17,
"agent_name": "support-bot",
"workflow_name": "handle-ticket",
"started_at": "2025-01-15T10:30:00Z",
"completed_at": "2025-01-15T10:30:05Z",
"duration": 5.1,
"status": "success",
"cost": 0.0078,
"tokens": 1800
}
]
}

Example:

curl "https://acme.waxell.dev/api/v1/observability/users/user_abc123/" \
-H "Cookie: sessionid=..."

Model Analytics

Get model usage analytics with time-series data for cost, token, and call-count trends.

GET /api/v1/observability/analytics/models/

Authentication: Session

Query Parameters:

ParameterTypeDescription
periodstringTime period: "1d", "7d", or "30d" (default: "7d")
agentstringFilter by agent name

Response:

{
"period": "7d",
"aggregates": {
"total_cost": 12.345,
"total_tokens": 2500000,
"total_calls": 1500
},
"model_totals": [
{
"model": "gpt-4o",
"total_cost": 10.5,
"total_tokens": 2000000,
"total_calls": 1200,
"percentage": 85.1
}
],
"time_series": [
{
"date": "2025-01-15",
"model": "gpt-4o",
"cost": 1.5,
"tokens": 300000,
"calls": 180
}
]
}

Example:

curl "https://acme.waxell.dev/api/v1/observability/analytics/models/?period=30d" \
-H "Cookie: sessionid=..."

Scoring Endpoints (Session Auth)

Endpoints for creating and querying quality scores.

List Scores

GET /api/v1/evaluations/scores/

Authentication: Session

Query Parameters:

ParameterTypeDescription
namestringFilter by score name
sourcestringFilter by source ("manual", "sdk", "evaluator")
data_typestringFilter by data type ("numeric", "categorical", "boolean")
run_idintegerFilter by run ID
llm_call_idintegerFilter by LLM call ID
sortstringSort field (default: -created_at). Allowed: created_at, -created_at, name, -name, numeric_value, -numeric_value, source, -source
limitintegerPage size (default: 25, max: 100)
offsetintegerPagination offset

Response:

{
"results": [
{
"id": "uuid",
"name": "accuracy",
"data_type": "numeric",
"source": "manual",
"numeric_value": 0.95,
"string_value": null,
"comment": "Very accurate",
"metadata": {},
"author_user_id": "1",
"evaluator_id": null,
"evaluator_name": null,
"run_id": "17",
"run_agent_name": "support-bot",
"llm_call_id": null,
"llm_call_model": null,
"created_at": "2025-01-15T10:30:00Z"
}
],
"count": 1,
"next": null,
"previous": null,
"aggregates": {
"total_count": 1,
"avg_numeric_value": 0.95,
"score_names": ["accuracy"]
}
}

Example:

curl "https://acme.waxell.dev/api/v1/evaluations/scores/?name=accuracy&sort=-numeric_value" \
-H "Cookie: sessionid=..."

Create Score

Create a manual score for a run or LLM call.

POST /api/v1/evaluations/scores/create/

Authentication: Session

Request Body:

{
"run_id": 17,
"name": "accuracy",
"data_type": "numeric",
"value": 0.95,
"comment": "Very accurate response",
"metadata": {}
}
FieldTypeRequiredDescription
namestringYesScore name
run_idintegerConditionalRun to score (at least one of run_id or llm_call_id required)
llm_call_idintegerConditionalLLM call to score
data_typestringNoOne of "numeric", "categorical", "boolean" (default: "numeric")
valueanyYesScore value (number for numeric, string for categorical, bool for boolean)
commentstringNoFree-text comment
metadataobjectNoArbitrary metadata

Response: 201 Created

{
"id": "uuid",
"name": "accuracy",
"data_type": "numeric",
"source": "manual",
"numeric_value": 0.95,
"string_value": null,
"run_id": "17",
"llm_call_id": null,
"created_at": "2025-01-15T10:30:00Z"
}

Example:

curl -X POST "https://acme.waxell.dev/api/v1/evaluations/scores/create/" \
-H "Cookie: sessionid=..." \
-H "Content-Type: application/json" \
-d '{
"run_id": 17,
"name": "accuracy",
"data_type": "numeric",
"value": 0.95,
"comment": "Very accurate response"
}'

Score Analytics

Get score distributions and time-series trends.

GET /api/v1/evaluations/scores/analytics/

Authentication: Session

Query Parameters:

ParameterTypeDescription
namestringFilter by score name
periodstringTime period: "1d", "7d", "30d" (default: "7d")
agentstringFilter by agent name

Response:

{
"period": "7d",
"since": "2025-01-08T10:30:00Z",
"distributions": [
{
"name": "accuracy",
"data_type": "numeric",
"total_count": 150,
"avg": 0.87,
"min": 0.2,
"max": 1.0,
"time_series": [
{"date": "2025-01-15", "avg": 0.89, "count": 25}
]
},
{
"name": "sentiment",
"data_type": "categorical",
"total_count": 80,
"value_counts": [
{"value": "positive", "count": 50},
{"value": "neutral", "count": 20},
{"value": "negative", "count": 10}
]
}
]
}

Example:

curl "https://acme.waxell.dev/api/v1/evaluations/scores/analytics/?name=accuracy&period=30d" \
-H "Cookie: sessionid=..."

Prompt Endpoints (Session Auth)

Endpoints for managing versioned prompts, labels, and the playground.

List Prompts

GET /api/v1/prompts/

Authentication: Session

Response:

{
"results": [
{
"id": "uuid",
"name": "support-prompt",
"description": "Main support agent prompt",
"prompt_type": "chat",
"tags": ["support", "production"],
"latest_version": 3,
"version_count": 3,
"labels": ["production", "staging"],
"created_at": "2025-01-10T08:00:00Z",
"updated_at": "2025-01-15T10:00:00Z"
}
],
"count": 1
}

Example:

curl "https://acme.waxell.dev/api/v1/prompts/" \
-H "Cookie: sessionid=..."

Create Prompt

Create a new prompt with an initial version (version 1).

POST /api/v1/prompts/

Authentication: Session

Request Body:

{
"name": "support-prompt",
"description": "Main support agent prompt",
"prompt_type": "chat",
"content": [
{"role": "system", "content": "You are a helpful assistant for {{company}}."},
{"role": "user", "content": "{{user_query}}"}
],
"config": {"model": "gpt-4o", "temperature": 0.7},
"tags": ["support"],
"commit_message": "Initial version"
}
FieldTypeRequiredDescription
namestringYesUnique prompt name
contentanyYesPrompt content. String for text prompts, array of message objects for chat prompts
descriptionstringNoDescription
prompt_typestringNo"text" or "chat" (default: "text")
configobjectNoDefault config (model, temperature, max_tokens)
tagsarrayNoSearchable tags
metadataobjectNoArbitrary metadata
commit_messagestringNoVersion commit message (default: "Initial version")

Response: 201 Created

Example:

curl -X POST "https://acme.waxell.dev/api/v1/prompts/" \
-H "Cookie: sessionid=..." \
-H "Content-Type: application/json" \
-d '{
"name": "support-prompt",
"prompt_type": "chat",
"content": [{"role": "system", "content": "You are a helpful assistant."}],
"config": {"model": "gpt-4o"}
}'

Get/Update/Delete Prompt

GET    /api/v1/prompts/{prompt_id}/
PUT /api/v1/prompts/{prompt_id}/
DELETE /api/v1/prompts/{prompt_id}/

Authentication: Session

  • GET returns prompt with all versions and labels
  • PUT updates metadata fields only (name, description, tags). To update content, create a new version
  • DELETE permanently deletes the prompt and all versions/labels

Example:

# Get prompt detail with all versions
curl "https://acme.waxell.dev/api/v1/prompts/uuid-here/" \
-H "Cookie: sessionid=..."

# Update metadata
curl -X PUT "https://acme.waxell.dev/api/v1/prompts/uuid-here/" \
-H "Cookie: sessionid=..." \
-H "Content-Type: application/json" \
-d '{"description": "Updated description", "tags": ["support", "v2"]}'

List Versions

GET /api/v1/prompts/{prompt_id}/versions/

Authentication: Session

Response:

{
"results": [
{
"version": 3,
"content_preview": "[{\"role\": \"system\", \"content\": \"You are...",
"config": {"model": "gpt-4o"},
"content_hash": "sha256...",
"commit_message": "Improved system prompt",
"created_by": "admin@acme.com",
"labels": ["production"],
"created_at": "2025-01-15T10:00:00Z"
}
],
"count": 3
}

Example:

curl "https://acme.waxell.dev/api/v1/prompts/uuid-here/versions/" \
-H "Cookie: sessionid=..."

Create Version

Create a new version of a prompt. The version number auto-increments.

POST /api/v1/prompts/{prompt_id}/versions/

Authentication: Session

Request Body:

{
"content": [
{"role": "system", "content": "Updated system prompt for {{company}}."},
{"role": "user", "content": "{{user_query}}"}
],
"config": {"model": "gpt-4o", "temperature": 0.5},
"commit_message": "Improved system prompt tone"
}
FieldTypeRequiredDescription
contentanyYesPrompt content
configobjectNoConfig overrides for this version
commit_messagestringNoCommit message

Response: 201 Created

{
"version": 4,
"content_hash": "sha256...",
"commit_message": "Improved system prompt tone",
"created_by": "admin@acme.com",
"created_at": "2025-01-15T10:30:00Z"
}

Example:

curl -X POST "https://acme.waxell.dev/api/v1/prompts/uuid-here/versions/" \
-H "Cookie: sessionid=..." \
-H "Content-Type: application/json" \
-d '{"content": "Updated prompt text", "commit_message": "Simplified wording"}'

Get Version Detail

GET /api/v1/prompts/{prompt_id}/versions/{version_num}/

Authentication: Session

Response:

{
"prompt_id": "uuid",
"prompt_name": "support-prompt",
"prompt_type": "chat",
"version": 3,
"content": [...],
"config": {"model": "gpt-4o"},
"content_hash": "sha256...",
"commit_message": "Improved system prompt",
"created_by": "admin@acme.com",
"labels": ["production"],
"created_at": "2025-01-15T10:00:00Z"
}

Example:

curl "https://acme.waxell.dev/api/v1/prompts/uuid-here/versions/3/" \
-H "Cookie: sessionid=..."

Set/Remove Label

Labels are movable pointers to specific versions (e.g., "production", "staging").

PUT    /api/v1/prompts/{prompt_id}/labels/{label}/
DELETE /api/v1/prompts/{prompt_id}/labels/{label}/

Authentication: Session

PUT Request Body:

{
"version": 3
}
FieldTypeRequiredDescription
versionintegerYesVersion number to point the label to

PUT Response:

{
"label": "production",
"version": 3,
"prompt_id": "uuid",
"created": false
}

Example:

# Set production label to version 3
curl -X PUT "https://acme.waxell.dev/api/v1/prompts/uuid-here/labels/production/" \
-H "Cookie: sessionid=..." \
-H "Content-Type: application/json" \
-d '{"version": 3}'

# Remove staging label
curl -X DELETE "https://acme.waxell.dev/api/v1/prompts/uuid-here/labels/staging/" \
-H "Cookie: sessionid=..."

Playground: Execute Prompt

Execute a prompt against an LLM with variable substitution.

POST /api/v1/prompts/playground/

Authentication: Session

Request Body:

{
"content": [
{"role": "system", "content": "You help users of {{company}}."},
{"role": "user", "content": "{{query}}"}
],
"config": {
"model": "gpt-4o-mini",
"temperature": 0.7,
"max_tokens": 1024
},
"variables": {
"company": "Acme Corp",
"query": "How do I reset my password?"
}
}
FieldTypeRequiredDescription
contentanyYesPrompt content (string or chat messages)
configobjectNoLLM configuration
config.modelstringNoModel to use (default: "gpt-4o-mini")
config.temperaturenumberNoSampling temperature (default: 0.7)
config.max_tokensintegerNoMax output tokens (default: 1024)
variablesobjectNoTemplate variables to replace {{var}} placeholders

Response:

{
"output": "To reset your password, click the Forgot Password link...",
"model": "gpt-4o-mini",
"tokens_in": 45,
"tokens_out": 120,
"cost": 0.000079,
"latency_ms": 1250
}

Example:

curl -X POST "https://acme.waxell.dev/api/v1/prompts/playground/" \
-H "Cookie: sessionid=..." \
-H "Content-Type: application/json" \
-d '{
"content": "Summarize: {{text}}",
"config": {"model": "gpt-4o-mini"},
"variables": {"text": "The quick brown fox..."}
}'

Playground: Compare Variants

Execute multiple prompt variants side-by-side for comparison.

POST /api/v1/prompts/playground/compare/

Authentication: Session

Request Body:

{
"variants": [
{
"content": "Summarize concisely: {{text}}",
"config": {"model": "gpt-4o-mini", "temperature": 0.3},
"variables": {"text": "..."}
},
{
"content": "Provide a detailed summary of: {{text}}",
"config": {"model": "gpt-4o", "temperature": 0.7},
"variables": {"text": "..."}
}
]
}
FieldTypeRequiredDescription
variantsarrayYesArray of variant objects (max 10). Each has content, config, and variables

Response:

{
"results": [
{"output": "...", "model": "gpt-4o-mini", "tokens_in": 30, "tokens_out": 50, "cost": 0.00003, "latency_ms": 800},
{"output": "...", "model": "gpt-4o", "tokens_in": 35, "tokens_out": 150, "cost": 0.0016, "latency_ms": 2100}
]
}

Example:

curl -X POST "https://acme.waxell.dev/api/v1/prompts/playground/compare/" \
-H "Cookie: sessionid=..." \
-H "Content-Type: application/json" \
-d '{
"variants": [
{"content": "Short: {{text}}", "config": {"model": "gpt-4o-mini"}, "variables": {"text": "hello"}},
{"content": "Long: {{text}}", "config": {"model": "gpt-4o"}, "variables": {"text": "hello"}}
]
}'

Prompt Metrics

Get usage metrics per prompt version, linked via content_hash to actual LLM call records.

GET /api/v1/prompts/{prompt_id}/metrics/

Authentication: Session

Response:

{
"prompt_id": "uuid",
"prompt_name": "support-prompt",
"totals": {
"call_count": 1500,
"total_tokens": 350000,
"total_cost": 3.45
},
"versions": [
{"version": 1, "content_hash": "abc...", "call_count": 500, "total_tokens": 100000, "total_cost": 1.0},
{"version": 2, "content_hash": "def...", "call_count": 1000, "total_tokens": 250000, "total_cost": 2.45}
]
}

Example:

curl "https://acme.waxell.dev/api/v1/prompts/uuid-here/metrics/" \
-H "Cookie: sessionid=..."

Error Responses

API errors return standard HTTP status codes with a JSON body:

{
"error": "Description of the error"
}
Status CodeMeaning
400Bad request (invalid JSON, missing required fields)
401Unauthorized (missing or invalid API key)
404Not found (invalid run ID or model ID)
409Conflict (duplicate name)
429Rate limited
500Internal server error

Next Steps