vrinda.dev / api
the rest api — same runner, your key in the body

byok · rest · metered receipts

the api.

The exact runner behind compare.html and run.html, exposed as a small REST API. POST a config and a prompt with your OpenRouter key, poll for status, and get back receipt-backed metered cost and a sandboxed artifact. One key, in the body, never stored.

Everything lives under /v1 on this same origin. No auth header, no account — your OpenRouter key rides in the request body as byok_key. POST /v1/runs with a full inline config; this example uses the leanest possible shape — one builder node, no eval:

curl -s https://vrinda.dev/v1/runs \
  -H 'content-type: application/json' \
  -d '{
    "config": {
      "schema": "orch-v0",
      "name": "solo-glm",
      "nodes": [
        { "id": "builder", "role": "builder", "model": "z-ai/glm-5.2",
          "instructions": "Build a complete, single-file HTML app for the request. Inline all CSS and JS." }
      ],
      "eval": { "type": "none", "max_retries": 0 }
    },
    "prompt": "Build a single-file HTML pomodoro timer with a circular progress ring.",
    "byok_key": "sk-or-...",
    "idempotency_key": "b1946ac9-4c9f-4b9a-8f1e-2d3c4b5a6d7e",
    "disclose": true
  }'

The server accepts immediately and returns 202 — it doesn't wait for the run to finish:

{ "run_id": "946aa650f4814e89b15c52f28572274d", "status": "queued" }

Runs follow a fixed node order — plan → build → check — with exactly one builder in the pipeline. The server injects its own approved model profile per node and rejects any temperature or max_tokens override you send. Set disclose: true to echo your config and prompt on the public board (see disclose below) — leave it false or omit it to keep them private. idempotency_key dedupes retried submits: reuse the same key and a retried POST returns the original run instead of starting a new one.

GET /v1/runs/{run_id} — poll this until status is done, failed, or cancelled. Here's a finished run:

{
  "run_id": "946aa650f4814e89b15c52f28572274d",
  "status": "done",
  "config_name": "solo-glm",
  "disclose": true,
  "node_statuses": [ { "id": "builder", "role": "builder", "status": "done" } ],
  "receipts": {
    "nodes": [
      { "role": "builder", "model": "z-ai/glm-5.2", "turn": 1,
        "prompt_tokens": 3120, "completion_tokens": 8140,
        "key_fingerprint": "a1b2c3d4", "usd": 0.011834,
        "gen_id": "gen-01H9Z8Q", "artifact_sha256": "60b346276a6e4c9e2f..." }
    ],
    "usd": 0.011834,
    "tokens": { "total": 11260 }
  },
  "timing": {
    "created_at": "2026-07-18T03:09:33Z",
    "started_at": "2026-07-18T03:09:34Z",
    "finished_at": "2026-07-18T03:10:22Z"
  },
  "config": { "schema": "orch-v0", "name": "solo-glm", "nodes": [ "..." ], "eval": { "type": "none", "max_retries": 0 } },
  "prompt": "Build a single-file HTML pomodoro timer with a circular progress ring."
}
  • status — one of queued, running, done, failed, cancelled.
  • node_statuses[] — per-node id / role / status for the live pipeline, in run order.
  • receipts — per-node metered cost straight from the provider's own generation receipts. usd is real metered spend billed to your OpenRouter account, not an estimate. key_fingerprint is sha256(your key)[:8]. artifact_sha256 is a content hash of that node's artifact.
  • receipts.usd is the run's total, summed across nodes. receipts.tokens.total is summed prompt and completion tokens across nodes.
  • timing holds created_at / started_at / finished_at, ISO-8601 UTC. Queue wait is started_at minus created_at; build time is finished_at minus started_at.
  • disclose — when true, the response also echoes config and prompt: the run is public-by-link, so anyone with the run_id sees your prompt, config, receipts, and artifact. When false or absent, config and prompt are omitted. Some models are free (catalog price 0) — a run entirely on free models meters at zero cost, and the site renders a free token there, never a dollar figure.

The final builder node's output is one HTML file, served from its own locked-down origin:

https://artifacts.vrinda.dev/runs/{run_id}/artifact.html

Every response carries a strict sandbox CSP — content-security-policy: sandbox allow-scripts — so the artifact can run its own JS but can't touch cookies, storage, or the parent page. The URL is unlisted (a random run id) but not access-controlled. Treat anything you build as shareable.

GET /v1/models returns the live catalog:

{
  "schema": "models-v0",
  "models": [
    { "slug": "z-ai/glm-5.2", "label": "glm-5.2", "tier": "workhorse",
      "in_usd_per_mtok": 0.413, "out_usd_per_mtok": 1.298, "fits_run_ceiling": true },
    { "slug": "tencent/hy3:free", "label": "hy3 · free", "tier": "free",
      "in_usd_per_mtok": 0, "out_usd_per_mtok": 0, "fits_run_ceiling": true }
  ],
  "presets": [ "..." ]
}

tier distinguishes free (catalog price 0, rate-limited by OpenRouter itself) from paid tiers like workhorse. in_usd_per_mtok and out_usd_per_mtok are USD per million tokens, input and output priced separately. fits_run_ceiling is whether one run on that model can stay under the $1.00 per-run ceiling. The catalog is the source of truth for pricing and updates without notice — read it here, don't hardcode it.

Bring-your-own-key. Hard ceilings, enforced server-side, checked before every call.

  • Rate limit. 12 runs / hour per address, rolling.
  • Spend ceilings. Per-run cap $1.00; rolling session cap $5.00 — runaway spend is refused.
  • Size caps. Prompt 8 KB (8192 bytes, UTF-8); per-stage instructions 2 KB (2048 bytes); the whole request wire size 40 KB (40960 bytes).
  • Time limit. 720 s wall-clock deadline per run.
  • One worker. A single FIFO queue. A run can fail alone and honestly — you see the reason, not a generic error.
  • Your key rides in the request body straight to the run, is forwarded to OpenRouter, and is never written to our queue, database, logs, or receipts. Receipts keep only sha256(key)[:8] so you can match a run to your key without us holding it.
  • You pay OpenRouter, not us. Every usd is metered from the provider's own generation receipts. Estimates are used only for a pre-call budget reservation.
  • No quality scores or winner predictions in the API — that judgment stays with you.
  • disclose: true is what makes a run's board entry public. The API itself doesn't grade or rank anything.
  • Costs in receipts are metered fact. Anything called an estimate is a pre-call reservation only, never what you're billed.