Argosvix
Docs menuREST API

REST API Reference

Bypass the dashboard and operate Argosvix data directly over HTTP. Usable from any framework or curl.


Base URL

https://ingest.argosvix.com

All endpoints live under this origin (same target as the SDK). Distinct from the dashboard origin (dashboard.argosvix.com). CORS is explicitly allow-listed.


Authentication

Every endpoint accepts one of:

  • Bearer token — Same argk_... API key the SDK uses, sent as Authorization: Bearer argk_.... Standard for server-to-server.
  • Session cookie — When called from a logged-in dashboard.argosvix.com browser session. Mutating methods enforce Origin / Referer CSRF checks.

Status codes:

CodeMeaning
200OK
201Created
204OK, no body (delete etc.)
400Validation failure
401Missing / invalid Bearer or session
402Plan gate — the feature requires a higher plan (reason: "plan_required")
403CSRF reject, permission denied
404Resource not found, or hidden by ownership scope
409Duplicate — the resource already exists
429Rate limit
500Server-side transient error (retry)
503Service temporarily unavailable — retry shortly; if it persists, contact support

API key scopes (least privilege)

API keys can carry permission scopes. There are three:

  • read — read-only (GET endpoints and /v1/query/*)
  • write:records — record ingestion (/v1/ingest, /v1/traces, and other records writes)
  • write — everything else (creating / modifying alerts, prompts, evals, gates, etc.)

Keys issued by npx @argosvix/cli init default to read + write:records — least privilege, so a key embedded in your app cannot change alert settings or perform destructive operations even if it leaks. If you need operational access (e.g. running operations via MCP), issue a separate key with broader scopes from the dashboard. Keys without explicit scopes keep full access for backward compatibility.

Quotas and retention:

  • Record quota is 50,000 records/month on Free and 1,000,000/month on Pro / Team (per seat on Team). Once exceeded, ingestion returns 429 (with a Retry-After header) until the start of the next month. Read endpoints keep working.
  • Retention is 30 days on Free and 90 days on Pro / Team. Older records are deleted automatically.
  • Endpoints with their own rate limits (webhook test, proposal endpoints) note them in their sections.

Ingesting records

POST /v1/ingest — send call records

Send LLM call records over plain HTTP without the SDK. Use this from languages the SDKs don't cover (Go / Ruby / Rust, etc.). Up to 100 records per request; body capped at 1 MiB (413 beyond that).

Required fields: id (unique per call; duplicate ids are rejected, not overwritten), provider (openai / anthropic / gemini / mistral), model, timestamp (ISO 8601), and non-negative numbers for promptTokens / completionTokens / totalTokens / costUsd / latencyMs.

Optional fields: tags (Record<string, string>), error, errorDetails, traceId / spanId / parentSpanId (for trace linking), sessionId.

curl -X POST \
  -H "Authorization: Bearer $ARGOSVIX_API_KEY" \
  -H "Content-Type: application/json" \
  https://ingest.argosvix.com/v1/ingest \
  -d '{
    "records": [{
      "id": "call_20260706_0001",
      "provider": "openai",
      "model": "gpt-4o-mini",
      "timestamp": "2026-07-06T00:00:00.000Z",
      "promptTokens": 42,
      "completionTokens": 18,
      "totalTokens": 60,
      "costUsd": 0.0012,
      "latencyMs": 234,
      "tags": {"service": "checkout"}
    }]
  }'

Responses use partial success: records that fail validation come back in rejected with a reason, and the rest are ingested:

{"accepted": 1, "rejected": [{"id": "call_x", "reason": "missing id"}]}

OpenTelemetry trace ingestion (OTLP/HTTP)

POST /v1/traces — Ingest OTLP/HTTP spans

Send traces over OTLP/HTTP (JSON encoding) and Argosvix ingests the spans that follow the GenAI semantic conventions as call records. Point any OTel instrumentation — LangGraph, OpenLLMetry, Traceloop — straight at this endpoint without rewriting your code.

Authentication is the same Bearer API key as every other endpoint. Both http/json and http/protobuf encodings are accepted, so exporters work with their default settings.

Example exporter configuration:

OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/json
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://ingest.argosvix.com/v1/traces
OTEL_EXPORTER_OTLP_TRACES_HEADERS=Authorization=Bearer%20argk_xxx

Span mapping

Only spans that represent an LLM call (those carrying gen_ai.* attributes) are ingested. Both the newer and older attribute names are accepted.

Ingested valueAttributes (first match wins)
Providergen_ai.provider.name or gen_ai.system
Modelgen_ai.request.model or gen_ai.response.model
Input tokensgen_ai.usage.input_tokens or gen_ai.usage.prompt_tokens
Output tokensgen_ai.usage.output_tokens or gen_ai.usage.completion_tokens
Costgen_ai.usage.cost (otherwise derived from per-model pricing)

The provider is normalized to one of openai / anthropic / gemini / mistral. Spans that don't map to one of these, or that carry no gen_ai.* attributes (HTTP, DB, and so on), are dropped and counted as rejected. traceId and spanId are preserved, so you can follow the nested execution in the trace detail view after ingestion. Re-sending the same span is deduplicated by traceId-spanId, so retries are not double-counted.

Response contract

Because OTel exporters retry on any non-2xx response, business-level rejections are still returned with HTTP 200.

SituationStatusBody
All spans accepted200{}
Some rejected200{"partialSuccess":{"rejectedSpans":"2","errorMessage":"..."}}
Monthly quota exceeded200every span counted in rejectedSpans (nothing ingested until next month)
Malformed protobuf400
Malformed JSON400
Body too large (over 1 MiB)413
curl -X POST \
  -H "Authorization: Bearer $ARGOSVIX_API_KEY" \
  -H "Content-Type: application/json" \
  https://ingest.argosvix.com/v1/traces \
  -d '{
    "resourceSpans": [{
      "scopeSpans": [{
        "spans": [{
          "traceId": "5b8aa5a2d2c872e8321cf37308d69df2",
          "spanId": "051581bf3cb55c13",
          "name": "chat",
          "startTimeUnixNano": "1700000000000000000",
          "endTimeUnixNano": "1700000002400000000",
          "attributes": [
            {"key": "gen_ai.system", "value": {"stringValue": "openai"}},
            {"key": "gen_ai.request.model", "value": {"stringValue": "gpt-5.5"}},
            {"key": "gen_ai.usage.input_tokens", "value": {"intValue": "100"}},
            {"key": "gen_ai.usage.output_tokens", "value": {"intValue": "50"}}
          ]
        }]
      }]
    }]
  }'

Observability (call records)

POST /v1/query/calls — Recent calls

Filter by time range / provider / model / count.

Request body:

FieldTypeDescription
startTimeISO 8601Window start (UTC)
endTimeISO 8601Window end (UTC)
providerstringopenai / anthropic / gemini / mistral
modelstringPartial match
limitnumber1–200, default 50
sortBystringtimestamp / provider / model / total_tokens / cost_usd / latency_ms / error
sortOrderstringasc / desc (default desc)
curl -X POST \
  -H "Authorization: Bearer $ARGOSVIX_API_KEY" \
  -H "Content-Type: application/json" \
  https://ingest.argosvix.com/v1/query/calls \
  -d '{"provider":"anthropic","limit":10}'

Response (a records array; every record has the same shape, absent values are null):

{
  "records": [{
    "id": "call_20260706_0001",
    "provider": "openai",
    "model": "gpt-4o-mini",
    "promptTokens": 42,
    "completionTokens": 18,
    "totalTokens": 60,
    "costUsd": 0.0012,
    "latencyMs": 234,
    "timestamp": "2026-07-06T00:00:00.000Z",
    "tags": {"service": "checkout"},
    "error": null,
    "errorDetails": null,
    "traceId": null,
    "spanId": null,
    "parentSpanId": null,
    "sessionId": null,
    "toolCalls": null,
    "piiRedacted": false,
    "redactionMetadata": null,
    "requestMeta": null
  }]
}

limit caps at 200. For larger pulls, page through time windows with startTime / endTime, or use the MCP export_calls tool (CSV / JSON export).

POST /v1/query/aggregate — Aggregate

Group by provider / model / day / tag, returning cost / latency / tokens / count.

curl -X POST \
  -H "Authorization: Bearer $ARGOSVIX_API_KEY" \
  -H "Content-Type: application/json" \
  https://ingest.argosvix.com/v1/query/aggregate \
  -d '{"groupBy":"model","metric":"cost","startTime":"2026-06-01T00:00:00Z"}'

Response:

{
  "groups": [
    {"key": "gpt-4o", "value": 1.7031, "count": 68, "lastSeen": "2026-07-02T04:45:16.000Z"},
    {"key": "gpt-4o-mini", "value": 0.0214, "count": 312, "lastSeen": "2026-07-05T22:10:03.000Z"}
  ],
  "total": {"value": 1.7245, "count": 380}
}

POST /v1/query/percentiles — Percentiles

Returns p50 / p95 / p99 for latency or cost in one call.

curl -X POST \
  -H "Authorization: Bearer $ARGOSVIX_API_KEY" \
  -H "Content-Type: application/json" \
  https://ingest.argosvix.com/v1/query/percentiles \
  -d '{"metric":"latency"}'

Response:

{"metric": "latency", "p50": 950, "p95": 2400, "p99": 4200, "p999": 6005, "max": 6005, "count": 246}

GET /v1/query/calls/:id — Single call

Returns one record by call id. Other accounts' ids return 404.

GET /v1/query/trace/:id — Trace detail

Returns all spans for a trace (capped at 50). errorDetails and requestMeta are structurally stripped.


Account & budget

GET /v1/account — Identity snapshot

Returns plan, this month's usage, retention — non-sensitive identity. No subscription details. Bearer-only.

curl -H "Authorization: Bearer $ARGOSVIX_API_KEY" \
  https://ingest.argosvix.com/v1/account

GET /v1/account/llm-feature-budget — Budget state

Returns this month's budget, spent, remaining, and period (YYYY-MM). Available on Free and Pro+.

{
  "budgetUsd": 5,
  "spentUsd": 0.42,
  "remainingUsd": 4.58,
  "periodStart": "2026-06",
  "defaultBudgetUsd": 5,
  "minBudgetUsd": 5,
  "maxBudgetUsd": 500
}

PATCH /v1/account/llm-feature-budget — Change budget (Pro+)

Set monthly budget between $5–$500, in $0.01 increments.

curl -X PATCH \
  -H "Authorization: Bearer $ARGOSVIX_API_KEY" \
  -H "Content-Type: application/json" \
  https://ingest.argosvix.com/v1/account/llm-feature-budget \
  -d '{"budgetUsd":30}'

Alerts

GET /v1/alerts — List

Returns configured alerts.

POST /v1/alerts — Create

{
  "name": "monthly cost",
  "alertType": "monthly_budget",
  "thresholdValue": 100,
  "windowMinutes": 1440,
  "channelKinds": ["webhook"],
  "channelTargets": { "webhook": { "url": "https://..." } },
  "enabled": true
}

alertType is one of cost_threshold / monthly_budget / error_rate / latency_degradation / anomaly_cost / anomaly_latency / anomaly_error_rate / eval_score (quality SLO; requires evalCriterionId) / guardian_findings (delivers new inbox findings to external channels; threshold and window are unused — pass 0 and 60).

GET /v1/alerts/:id — Single alert

Returns config + recent firing events.

PATCH /v1/alerts/:id — Update

Update name / thresholdValue / windowMinutes / channelKinds / channelTargets. alertType is immutable (drop and recreate to change it).

DELETE /v1/alerts/:id — Delete

Related firing events cascade.

POST /v1/alerts/test-webhook — Test send (Pro+)

Probe a webhook URL before registering. HTTPS only, with SSRF guards (private / loopback / cloud metadata IPs rejected). Rate-limited to 5 / min.

curl -X POST \
  -H "Authorization: Bearer $ARGOSVIX_API_KEY" \
  -H "Content-Type: application/json" \
  https://ingest.argosvix.com/v1/alerts/test-webhook \
  -d '{"url":"https://example.com/hook","alertName":"test"}'

Response:

{
  "ok": true,
  "delivered": true,
  "message": "test webhook delivered (= 2xx response within 5s)"
}

GET /v1/alerts/events — Firing history

Returns the account's alert firing events, newest first. Filter to a single alert with alertId.

Query parameterDescription
limit1-100 (default 20)
alertIdRestrict to one alert's firings
beforeTriggeredAt + beforeIdKeyset cursor: pass the last event's triggeredAt and id from the previous page (always both together; one alone is a 400)

Each event includes a snapshot of the conditions at trigger time (thresholdValue / windowMinutes / alertType). Events that fired before snapshots were introduced fall back to the rule's current values and are marked thresholdIsSnapshot: false.

{
  "events": [
    {
      "id": "6f30f1b2-...",
      "alertId": "71845ee2-...",
      "alertName": "Cost overrun",
      "alertType": "cost_threshold",
      "triggeredAt": "2026-06-12T02:15:01.683Z",
      "observedValue": 0.005,
      "thresholdValue": 0.000001,
      "thresholdIsSnapshot": true,
      "windowMinutes": 60,
      "channelsSent": ["email"],
      "acknowledgedAt": null,
      "acknowledgedBy": null
    }
  ]
}

POST /v1/alerts/events/:eventId/acknowledge — Acknowledge

Mark a firing event as handled. Orthogonal to silence; idempotent.


Prompt management

GET /v1/prompts — List

Optional query params: label, name.

POST /v1/prompts — Create (Pro+)

{
  "name": "customer_support",
  "version": "v1",
  "template": "Hello {{user}}",
  "variables": { "user": "world" },
  "labels": ["production"],
  "description": "primary cs prompt"
}

A prompt's name + version must be unique within your account. Duplicates return 409.

GET /v1/prompts/:id — Single

PATCH /v1/prompts/:id — Partial update (Pro+)

Updates template / variables / labels / description. name and version are immutable (use POST /v1/prompts/:id/rename).

POST /v1/prompts/:id/rename — Rename (Pro+)

Change name and version. Useful for typo fixes.

curl -X POST \
  -H "Authorization: Bearer $ARGOSVIX_API_KEY" \
  -H "Content-Type: application/json" \
  https://ingest.argosvix.com/v1/prompts/7/rename \
  -d '{"name":"customer_support","version":"v2"}'

DELETE /v1/prompts/:id — Delete (Pro+)

⚠ Deleting a version detaches it from past eval runs, so those runs lose their link to the prompt (provenance is lost). For logical sunset, prefer PATCH to set labels to sunset.


Eval criteria

GET /v1/eval-criteria — List

Global defaults + your account's custom criteria.

POST /v1/eval-criteria — Create (Pro+)

{
  "name": "helpfulness",
  "rubric": "Score how helpful the answer is to the user.",
  "scaleMin": 1,
  "scaleMax": 5,
  "scope": "call"
}

Optional scope: call (default — one score per call) or trajectory (one score per multi-turn agent run, grouping all calls that share a trace_id into a single transcript; llm_judge type only). Trajectory scores are returned under trajectoryScores in GET /v1/eval-runs/:id.

PATCH /v1/eval-criteria/:id — Full replace (Pro+)

All 4 fields required: name / rubric / scaleMin / scaleMax. scope is preserved if omitted. Global defaults return 404.

DELETE /v1/eval-criteria/:id — Delete (Pro+)

⚠ All scores recorded against the criterion are deleted too, breaking historical comparison. Prefer PATCH if you only want to rename.

POST /v1/eval-criteria/propose — LLM-judge proposes evaluation criteria (Pro+)

Asks an LLM judge (gpt-4o-mini) to propose evaluation criteria tailored to your use case from useCaseHint and optional sampleCallIds. Proposal-only — nothing is created automatically (you apply it via POST /v1/eval-criteria separately). Sample decrypt failures are reported in partialFailures.

{
  "useCaseHint": "Customer support bot for e-commerce (returns + refund policy)",
  "sampleCallIds": ["call_abc123", "call_xyz789"],
  "maxCriteria": 5
}
  • useCaseHint: required, 1-500 chars
  • sampleCallIds: optional, max 5, account-scoped
  • maxCriteria: optional, 1-10 (default 5)
  • Rate limit: 30 requests per 60-second fixed window (429 + Retry-After header; enforced account-wide)

Returns:

{
  "criteria": [
    {
      "name": "tone_appropriateness",
      "rubric": "Is the bot's tone polite and professional?",
      "scaleMin": 1,
      "scaleMax": 5,
      "reasoning": "Tone is critical in customer support."
    }
  ],
  "partialFailures": [],
  "budgetSpentUsd": 0.002,
  "proposedRawCount": 5,
  "droppedCount": 0
}

Audit: emits eval.propose_criteria to the audit log.


Eval runs

GET /v1/eval-runs — List

Returns history with summary (scoredCount / failedCount / mean scores).

GET /v1/eval-runs/:id — Single

Returns run detail with per (criterion × call) scores.

POST /v1/eval-runs — Start a run (Pro+)

{
  "name": "weekly review",
  "recentCount": 50,
  "label": "production",
  "promptRegistryId": 7,
  "idempotencyKey": "uuid-1234-..."
}

With idempotencyKey, retries within 60 min return the existing run (dedupe).


Annotations

GET /v1/annotations — List

?callId=xxx lists for one call; ?label=xxx cross-call by label.

POST /v1/annotations — Create

{
  "callId": "call-xyz",
  "annotationText": "good response",
  "label": "approved",
  "qualityScore": 5
}

At least one of annotationText / label / qualityScore required.

GET /v1/annotations/:id — Single

PATCH /v1/annotations/:id — Partial update

Updates annotationText / label / qualityScore. callId is immutable.

DELETE /v1/annotations/:id — Delete


Safety assessments

GET /v1/safety-assessments — List

?call_id=xxx returns all classifier results for that call. Without it, returns the most recent across the account.

POST /v1/safety-assessments/scan-batch — On-demand bulk safety classify (Pro+)

Complements the automatic background scan (every 15 minutes) with an immediate "scan now" trigger over up to 100 unclassified calls in your account via OpenAI Moderation. The results are recorded as on-demand runs (distinct from scheduled scans), and duplicate assessments are prevented.

{
  "maxRecords": 50
}
  • maxRecords: optional, 1-100 (default 50), integer
  • Rate limit: 30 requests per 60-second fixed window (429 + Retry-After header; enforced account-wide)
  • Via OpenAI Moderation (cost ≈ $0); backend enforces plan + budget gates.

Returns:

{
  "scanned": 50,
  "assessed": 48,
  "flagged": 2,
  "failures": 0,
  "skipped": 2
}

Audit: emits safety.scan_batch_run to the audit log.

GET /v1/safety-assessments/:id — Single

Returns labels, score, reasoning, classifier_id, source.


Error responses

{
  "error": "invalid model (length 1-128 required)"
}

For 403 / 409 / 503 etc., the error field carries a specific cause. Clients should branch on status code + error string.


SDK & MCP server

Instead of calling REST directly, you can use the SDK or MCP server.

  • SDK Reference — Just wrap() the client and calls are auto-recorded.
  • MCP Server — Operate from chat in Claude Desktop / Cursor / Codex CLI.

Support

hello@argosvix.com

Last updated: 2026-07-09