Skip to main content
POST
https://app.artanis.ai
/
api
/
v1
/
observations
Add Observation
curl --request POST \
  --url https://app.artanis.ai/api/v1/observations \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "trace_id": "<string>",
  "type": "<string>",
  "data": "<any>",
  "key": "<string>",
  "timestamp": "<string>"
}
'
{
  "status": "<string>",
  "observation_id": "<string>"
}

Overview

The /api/v1/observations endpoint captures data flowing through your AI operations. Each observation represents a specific piece of information:
  • input — Data going into the operation
  • output — The final result
  • state — Context needed for replay
You can send multiple observations per trace, and they’re recorded immediately for crash resilience.
Observations are sent immediately as they occur (not batched) to ensure data is captured even if your application crashes.

Request Body

trace_id
string
required
The trace ID this observation belongs to. Must match a trace created via /api/v1/traces.Example: "trace_a3f9c2e1b8d4f7a6c9e2"
type
string
required
Type of observation being recorded.
  • "input" — Data going into the operation
  • "output" — Final result of the operation
  • "state" — Context needed for replay
Example: "input"
data
any
required
The observation data. Can be any JSON-serializable value (string, number, object, array, boolean, null).Keep data size reasonable (< 1MB per observation recommended).Examples:
  • {"question": "What is AI?", "model": "gpt-4"}
  • "AI stands for Artificial Intelligence"
  • ["doc-123", "doc-456", "doc-789"]
key
string
Required for type: "state" observations. Identifies the state variable.Common keys:
  • "config" — Model configuration
  • "documents" — Document corpus
  • "retrieved_chunks" — Retrieved chunks with scores
  • "guidelines" — Prompt instructions
Ignored for input and output observations.Example: "config"
timestamp
string
required
ISO 8601 timestamp with milliseconds and UTC timezone.Format: YYYY-MM-DDTHH:mm:ss.sssZExample: "2025-12-22T10:30:45.234Z"Should represent when this observation occurred.

Response

status
string
Response status indicator.Returns "created" when the observation is successfully recorded.
observation_id
string
The unique identifier for the created observation.Example: "obs_abc123xyz789"You can use this ID to submit feedback targeting this specific observation.

Observation Types

Input Observations

Record data going into your operation. You can call this multiple times to progressively capture inputs. When to use:
  • Initial user query or prompt
  • Configuration parameters (model, temperature, etc.)
  • Search queries or filters
  • Any data that influences the output
import requests
from datetime import datetime

response = requests.post(
    "https://app.artanis.ai/api/v1/observations",
    headers={
        "Authorization": "Bearer ak_...",
        "Content-Type": "application/json"
    },
    json={
        "trace_id": trace_id,
        "type": "input",
        "data": {
            "question": "What is your refund policy?",
            "model": "gpt-4",
            "temperature": 0.7
        },
        "timestamp": datetime.utcnow().isoformat(timespec='milliseconds') + 'Z'
    }
)
You can call this multiple times to progressively add inputs as your operation proceeds.

Output Observations

Record the final result of your operation. Typically called once at the end. Output can be any JSON-serializable value:
{
  "trace_id": "trace_...",
  "type": "output",
  "data": "We offer a 30-day money-back guarantee.",
  "timestamp": "2025-12-22T10:30:46.567Z"
}

State Observations

Capture context that existed at inference time. Essential for replay functionality.
Why capture state? When replaying a trace, you need to know not just the inputs, but what context existed at that moment (documents, config, guidelines, etc.).
Common state keys:
KeyPurposeExample Value
configModel configuration{"model": "gpt-4", "temperature": 0.7}
documentsDocument corpus at time of inference["doc-123", "doc-456", "doc-789"]
retrieved_chunksRetrieved chunks with scores[{"id": "chunk-1", "score": 0.95}]
guidelinesPrompt instructions or rules"Be helpful and concise"
contextAdditional context used{"user_history": [...]}
# State for configuration
requests.post(
    "https://app.artanis.ai/api/v1/observations",
    headers={
        "Authorization": "Bearer ak_...",
        "Content-Type": "application/json"
    },
    json={
        "trace_id": trace_id,
        "type": "state",
        "key": "config",
        "data": {
            "model": "gpt-4",
            "temperature": 0.7,
            "max_tokens": 500,
            "guidelines": "Be concise and professional"
        },
        "timestamp": datetime.utcnow().isoformat(timespec='milliseconds') + 'Z'
    }
)

# State for retrieved documents
requests.post(
    "https://app.artanis.ai/api/v1/observations",
    headers={
        "Authorization": "Bearer ak_...",
        "Content-Type": "application/json"
    },
    json={
        "trace_id": trace_id,
        "type": "state",
        "key": "documents",
        "data": [
            {"id": "doc-123", "score": 0.95},
            {"id": "doc-456", "score": 0.87}
        ],
        "timestamp": datetime.utcnow().isoformat(timespec='milliseconds') + 'Z'
    }
)
Best practice: Store IDs and references, not full content. For large documents, store references or summaries rather than full text.

RAG Pipeline Example

Here’s how to capture a complete RAG (Retrieval-Augmented Generation) pipeline:
import requests
import secrets
from datetime import datetime

API_KEY = "ak_..."
BASE_URL = "https://app.artanis.ai"
headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}

trace_id = f"trace_{secrets.token_hex(11)}"

# Helper function
def send_observation(obs_type, data, key=None):
    payload = {
        "trace_id": trace_id,
        "type": obs_type,
        "data": data,
        "timestamp": datetime.utcnow().isoformat(timespec='milliseconds') + 'Z'
    }
    if key:
        payload["key"] = key

    requests.post(f"{BASE_URL}/api/v1/observations", headers=headers, json=payload)

# 1. Record input (user query)
send_observation("input", {
    "question": "What is your refund policy?",
    "model": "gpt-4"
})

# 2. Capture state (current document corpus)
send_observation("state", ["doc-123", "doc-456", "doc-789"], key="documents")

# 3. Your retrieval logic
retrieved_docs = [
    {"id": "doc-123", "score": 0.95, "title": "Refund Policy"},
    {"id": "doc-456", "score": 0.87, "title": "Returns FAQ"}
]

# 4. Capture retrieved chunks
send_observation("state", retrieved_docs, key="retrieved_chunks")

# 5. Capture generation config
send_observation("state", {
    "model": "gpt-4",
    "temperature": 0.7,
    "prompt_template": "Answer based on: {context}"
}, key="config")

# 6. Your generation logic
response = "We offer a 30-day money-back guarantee on all purchases."

# 7. Record output
send_observation("output", response)

print(f"Recorded complete RAG pipeline for trace {trace_id}")

Best Practices

1. Send Observations Immediately

Don’t buffer observations - send them as they occur. This ensures data is captured even if your application crashes.

2. Keep Data Sizes Reasonable

  • Inputs/Outputs: < 100KB per observation
  • State: Store IDs and references, not full documents
  • Total per trace: < 10MB recommended

3. Use Meaningful State Keys

Choose descriptive state keys that clearly indicate what’s being captured: ✅ Good: "retrieved_chunks", "generation_config", "prompt_template" ❌ Bad: "data", "info", "temp"

4. Capture Replay Context

Include everything needed to reproduce the operation:
  • Model configuration (version, parameters)
  • Document corpus state (IDs, not full content)
  • Retrieved chunks with scores
  • Prompt templates and guidelines

Error Responses

Status CodeErrorSolution
400Invalid request formatCheck JSON syntax and required fields
401Invalid API keyVerify your API key is correct
404Trace not foundEnsure trace was created first via /api/v1/traces
413Observation data too largeReduce data size (< 1MB recommended)
429Rate limit exceededImplement exponential backoff retry

Next Steps