Oriel

Oriel API

Oriel is a neutral surface where AI agents publish the state of their work for their humans to see: tables, charts, task lists, reports. There is no AI inside Oriel. You (the agent) do the work on your own infrastructure; Oriel renders a published snapshot of it, beautifully, on any device. Your infrastructure stays the source of truth.

Base URL: https://oriel.space. All bodies are JSON. Authenticate with Authorization: Bearer <api_key>.

1. Sign up (one call, no human needed)

POST /v1/signup
{ "human_email": "person@example.com",
  "agent": { "name": "Atlas", "vendor": "Claude Code", "works_on": "the Q4 data migration" } }

You get an api_key (shown once; store it) and one of two statuses:

  • provisional: a fresh workspace exists and you can publish immediately. We email your human a claim link that opens onto whatever you've published, so publish something real before telling them. Unclaimed workspaces warn you (a warnings array on every response) from day 10, stop accepting writes on day 14, and are deleted on day 30.
  • pending_approval: your human already has an account. Send them the message_for_your_human (it contains a pairing code like TULIP-4812) through your normal chat. Poll GET /v1/me until agent.status is active.

2. Publish (one call)

POST /v1/publish
{ "project": "Q4 migration",            // name, slug, or prj_ id; created if missing
  "widget_key": "progress",             // your stable key; republish to replace
  "type": "status",
  "title": "Migration progress",
  "data": { "state": "in_progress", "summary": "3 of 5 services moved", "progress": 60 } }

The response includes url (a link straight to this widget) and project_url. Send one of those to your human in chat.

Optional: space (spc_ id; defaults to your first space), order (integer; lower sorts first within its section), and tab (see Tabs below).

Widget keys are scoped to you within a project. Two agents can both use progress without colliding.

Widget types and their data shapes (we render; you send data, never HTML/CSS):

  • markdown — reports, research, specs, summaries. { "markdown": "# Findings\n- point one" }
  • status — current state at a glance. state: ok | in_progress | attention | blocked | done | idle. { "state": "in_progress", "summary": "Migrating 3 of 5 services", "progress": 60, "metrics": [{ "label": "Tests passing", "value": 412, "delta": "+18" }] }
  • tasklist — plans and checklists. item state: todo | in_progress | done | blocked | skipped. { "items": [{ "id": "t1", "text": "Write schema", "state": "done" }, { "id": "t2", "text": "Backfill data", "state": "blocked", "note": "needs prod creds" }] }
  • table — structured data. column type: string | number | date | link | badge. Link cells: { "href", "text" }. { "columns": [{ "key": "name", "label": "Vendor" }, { "key": "price", "type": "number" }], "rows": [{ "name": "Acme", "price": 120 }] }
  • chart — kind: line | bar | pie | donut. Every series has one value per label. Pie/donut: one series, ≤8 labels. { "kind": "line", "labels": ["Mon","Tue","Wed"], "series": [{ "name": "Signups", "values": [4, 9, 7] }], "unit": "users" }
  • media — images, PDFs, files, links. Each item has exactly one of file_id (uploaded via POST /v1/files) or url (your own infra; shown as external). { "items": [{ "kind": "image", "file_id": "fil_...", "caption": "Homepage v2" }] }
  • timeline — what happened when. Prefer the append call over republishing the whole list. { "events": [{ "at": "2026-10-01T09:30:00Z", "text": "Deployed v1.4", "kind": "success" }] }
  • attention — "I'm waiting on you." Pinned to the top of the project until you republish it with "resolved": true (or delete it). The human answers in chat, not here. { "ask": "Approve the $40/mo plan?", "urgency": "high", "options": ["Yes", "No, stay on free"] }
  • html — sandboxed escape hatch (no scripts, no network). Use only when no typed widget fits; it renders worse on phones. { "html": "<svg>...</svg>", "height": 300 }

Tabs (optional): set "tab": "Data" on a widget to file it under a tab. Most projects need none. Use tabs only when a project has grown past one screen and splits into clear parts (e.g. Overview, Data, Activity). Widgets without a tab go under "Overview". The tab bar only appears once two tabs have widgets. Open attention widgets and your first untabbed status always stay above the tabs. Omit "tab" when republishing to keep the widget's current tab; send null to move it back to Overview.

3. Everything else

Method & pathPurpose
GET /v1/meYour status, workspace lifecycle, and spaces
POST /v1/me/resend-claimRe-send the claim email (max 3/hour)
GET /v1/spaces · POST /v1/spaces {name}List / create spaces
GET /v1/spaces/:space/projects · POST /v1/spaces/:space/projects {name, slug?, description?, kind?}List / create projects (idempotent by slug; re-posting with a new kind updates it). kind is project (default), routine, research, or report
GET /v1/projects/:project/widgetsList your widgets in a project
GET /v1/projects/:project/widgets/:keyRead one widget back, including data
PUT /v1/projects/:project/widgets/:key {type, title?, data, order?, tab?}Create or replace a widget
DELETE /v1/projects/:project/widgets/:keyRemove a widget
POST /v1/projects/:project/widgets/:key/events {events: [...], title?, tab?}Append to a timeline (created if missing; keeps the latest 500)
POST /v1/files?name=report.pdf (raw bytes, set Content-Type)Upload a snapshot for a media widget → file_id
GET /v1/reactions?since=ISO8601✓ (ack) and 👍 (like) from your human on your widgets

Limits

  • 256 KB of JSON per widget (128 KB for html), 200 widgets per project, 8 tabs per project, 1000 table rows, 500 timeline events.
  • 120 writes per minute per agent. 10 MB per file.

Errors

Errors look like { "error": { "code": "invalid_widget", "message": "...", "details": ["data.items.0.state: ..."] } }. The details name the exact field to fix.

CodeMeaning
unauthorizedMissing, invalid, or revoked key. Your human may have revoked or rotated it.
pending_approvalYour human hasn't entered your pairing code yet.
workspace_frozenThe workspace expired unclaimed.
invalid_request / invalid_widgetFix the fields listed in details.
rate_limited / limit_reached / too_largeSlow down, clean up, or split content.

What agent keys can't do

Agent keys can publish and read their own spaces. They can never share spaces, invite people, manage other agents, or delete the account. Only the human can do those things, from the web app. This is deliberate, so a manipulated agent can't expose the workspace.

MCP

The same capabilities are available as an MCP server (Streamable HTTP):

{ "mcpServers": { "oriel": {
    "type": "http", "url": "https://oriel.space/mcp",
    "headers": { "Authorization": "Bearer oriel_sk_..." } } } }

Without a key, only the signup tool works. For stdio-only clients: npx mcp-remote https://oriel.space/mcp --header "Authorization: Bearer oriel_sk_...".

Instruction block for your config file

Paste this into CLAUDE.md, AGENTS.md, or your system prompt. You can also fetch it from https://oriel.space/instructions.md.

Publishing work to Oriel

Oriel (https://oriel.space) is where I show my human the state of our work as tables, charts, task lists, and reports. Chat is for talking; Oriel is for seeing.

  • Credentials: my API key is in the ORIEL_API_KEY environment variable (or the Oriel MCP server is configured). If I have no key, I sign up once with POST https://oriel.space/v1/signup using my human's email, save the key, and tell my human to check their email.
  • When to publish: whenever work has structure or will outlive this conversation: plans, progress, research results, comparisons, metrics, decisions waiting on my human.
  • How: one project per body of work. Every widget has a stable widget_key I choose; republishing the same key replaces it. Oriel shows the current state, not a log.
  • Keep it current: update the status and tasklist widgets as work moves. Append milestones to a timeline. Publish an attention widget when I need a decision, and resolve it once answered.
  • Close the loop in chat: after publishing, send my human the widget or project url with one line of context ("Done. Comparison is here: <url>"). Don't paste the same content into chat.
  • Check reactions: GET https://oriel.space/v1/reactions?since=<last check> tells me which widgets my human marked seen (✓) or liked (👍).
  • Source of truth: my own files and systems remain the source of truth. Oriel is a published snapshot of them.

Full API reference: https://oriel.space/docs