Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.voight.xyz/llms.txt

Use this file to discover all available pages before exploring further.

If you can’t or don’t want to add a dependency, Voight accepts raw HTTP POST from any runtime — curl, Python, Go, Rust, browsers, embedded devices, anything that speaks JSON over HTTPS.

Endpoint

POST https://voight-production.up.railway.app/v1/events

Authentication

Authorization: Bearer vk_YOUR_API_KEY
Content-Type: application/json
Generate your vk_ key at voight.xyz/dashboard/settings.

Minimal payload

curl https://voight-production.up.railway.app/v1/events \
  -H "authorization: Bearer $VOIGHT_KEY" \
  -H "content-type: application/json" \
  -d '{
    "agentId": "trading-bot.sol",
    "type": "tx",
    "toolExecuted": "jupiter.swap",
    "transaction": "4zK...9Fp"
  }'
Response:
{
  "id": "evt_clz3...",
  "accepted": true,
  "agentId": "cmoq...",
  "eventId": "evt_clz3..."
}
The server returns the resolved agentId (a CUID) — store it locally if you want subsequent events to skip label resolution.

Full payload schema

{
  "agentId": "trading-bot.sol",
  "timestamp": "2026-05-12T14:02:54.084Z",
  "type": "tx",
  "input": { "prompt": "...", "context": { } },
  "reasoning": "free-form trace",
  "toolsConsidered": ["jupiter.swap", "orca.swap"],
  "toolExecuted": "jupiter.swap",
  "transaction": "4zK...9Fp",
  "amount": { "token": "SOL", "value": 1.5 },
  "outcome": "success",
  "durationMs": 184,
  "errorMessage": "optional, when outcome=failed",
  "model": "claude-opus-4-7",
  "metadata": {
    "source": "my-trading-bot",
    "strategy": "mean-reversion",
    "tokensBreakdown": {
      "inputBase": 50,
      "cacheCreation": 100,
      "cacheRead": 850,
      "output": 200
    },
    "privacyLevel": "standard"
  }
}
All fields except agentId are optional. The server defaults type: 'decision' if omitted.

Privacy on the HTTP path

The HTTP path does not apply PII scrubbing — that’s a client-side operation that requires the SDK. If you’re calling HTTP directly, you’re responsible for scrubbing sensitive content before it leaves your machine. To make Voight respect privacy controls on HTTP events, include metadata.privacyLevel: 'minimal' | 'standard' | 'full' so the dashboard can chip them correctly. The scrubbing itself is your responsibility. For most use cases, the library mode SDK is the right call — import { scrubPii } from '@voightxyz/sdk' gives you the same scrubbing without adopting the hook handler.

Examples

Python

import requests
import os

def log_event(payload):
    response = requests.post(
        "https://voight-production.up.railway.app/v1/events",
        headers={
            "authorization": f"Bearer {os.environ['VOIGHT_KEY']}",
            "content-type": "application/json",
        },
        json=payload,
        timeout=5,
    )
    response.raise_for_status()
    return response.json()

log_event({
    "agentId": "py-bot",
    "type": "action",
    "toolExecuted": "fetch_market_data",
    "outcome": "success",
    "durationMs": 412,
})

Go

package main

import (
    "bytes"
    "encoding/json"
    "net/http"
    "os"
)

func logEvent(payload map[string]any) error {
    body, _ := json.Marshal(payload)
    req, _ := http.NewRequest("POST",
        "https://voight-production.up.railway.app/v1/events",
        bytes.NewBuffer(body))
    req.Header.Set("Authorization", "Bearer "+os.Getenv("VOIGHT_KEY"))
    req.Header.Set("Content-Type", "application/json")
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    return nil
}

Rust

use reqwest::Client;
use serde_json::json;

async fn log_event(payload: serde_json::Value) -> reqwest::Result<()> {
    let client = Client::new();
    let api_key = std::env::var("VOIGHT_KEY").unwrap();
    client.post("https://voight-production.up.railway.app/v1/events")
        .bearer_auth(&api_key)
        .json(&payload)
        .send()
        .await?
        .error_for_status()?;
    Ok(())
}

Browser / fetch

await fetch('https://voight-production.up.railway.app/v1/events', {
  method: 'POST',
  headers: {
    'authorization': `Bearer ${VOIGHT_KEY}`,
    'content-type': 'application/json',
  },
  body: JSON.stringify({
    agentId: 'browser-agent',
    reasoning: 'user clicked the swap button',
  }),
})
CORS is configured to allow https://voight.xyz and https://www.voight.xyz. For other origins, you’ll need server-side proxying.

Status codes

CodeMeaning
202 AcceptedEvent received and persisted (returns { id, accepted: true, agentId })
400 Bad RequestPayload failed Zod validation (response includes details)
401 UnauthorizedMissing or invalid API key
410 GoneThe agent has been soft-deleted, ingestion blocked
429 Too Many RequestsRate-limited (honor Retry-After header)
500 Server ErrorBackend issue — retry with exponential backoff

Rate limits

Per pricing tier:
TierEvents / month
Free10,000
Pro300,000
EnterpriseUnlimited
Quotas are advisory today, enforced server-side starting v1.0. Burst rate-limiting (per-second) is in place to prevent abuse.

Next

Library mode

For JS/TS — gives you retry, error types, and PII scrubbing for free.

POST /v1/events reference

Full schema definition with every field documented.