Expert Sign-Off API
When your AI reaches a decision that needs a licensed human, send it to Luminary. You get a ticket immediately, your AI keeps working, and a verified expert returns a signed verdict with an audit trail.
contains_phi: false. Keys are issued by our team during the pilot — contact us from the business page.Quickstart
Ask a licensed clinician in California to check an AI-drafted care plan:
curl https://theluminary.group/api/v1/signoffs \
-H "Authorization: Bearer lum_test_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: careplan-8812-v1" \
-d '{
"question": "Is this care plan clinically appropriate for the needs described?",
"context": "Synthetic member, 54, recently discharged ... [care plan text]",
"credential": "LCSW",
"jurisdiction": "CA",
"depth": "standard",
"tier": "standard",
"speed": "next_day",
"contains_phi": false,
"external_ref": "careplan-8812"
}'The response is the ticket: {"id": "so_...", "status": "open", "price_cents": 12900, "due_at": "...", ...}. Poll it, or receive a webhook when the expert signs.
Keys and the sandbox
Send your key as Authorization: Bearer <key>. There are two kinds:
lum_test_…— sandbox. A simulated reviewer answers within seconds and no credits are used. Include the word “reject” or “modify” in the question to test those verdicts.lum_live_…— real, verified experts. The price is charged from your prepaid credits when the request is created and refunded if you cancel before it’s claimed or it expires unanswered.
Create a sign-off
POST /api/v1/signoffs — returns 201 with the ticket. Send an Idempotency-Key header so a retried request is never created (or charged) twice.
| question | Required. What the expert should decide (10–4,000 characters). |
| context | The material to review: the AI’s draft, the facts, documents as text (up to 50,000 characters). |
| credential | Required. License needed: PSYCHOLOGIST, LCSW, LMFT, LPCC, MD, DO, NP, PA, RN, PHARMACIST, ATTORNEY, CPA, PE. |
| jurisdiction | Required. Two-letter US state the expert must be licensed in. |
| depth | quick (verdict + one line), standard (edits + rationale, default) or deep (documents, complex). |
| tier | standard (default), senior (10+ years or board certified) or tier_one (curated panel). |
| speed | two_days, next_day (default), four_hours, one_hour or live_15 (only when experts are on call). |
| contains_phi | Required, must be false during the preview. |
| external_ref | Optional. Your own id, echoed back. |
| metadata | Optional. Object of your own key/values, echoed back. |
Getting the answer
GET /api/v1/signoffs/:id returns the request with its status: open → claimed → answered, or cancelled / expired. Once answered, answer holds the signed result:
"answer": {
"verdict": "modify", // approve | reject | modify
"edited_text": "...", // the expert's corrected version (modify only)
"rationale": "...",
"signed_at": "2026-10-14T17:02:11Z",
"signer": {
"name": "Dr. Jane Smith",
"tier": "standard",
"license": { "kind": "LCSW", "state": "CA", "number": "LCSW 12345",
"expires_on": "2027-06-30", "verified_at": "..." }
},
"content_hash": "9f2c…", // SHA-256 over the request and the signed answer
"verify_url": "https://theluminary.group/verify/so_...?h=9f2c…",
"certificate_url": "https://theluminary.group/api/v1/signoffs/so_.../certificate",
"public_certificate_url": "https://theluminary.group/verify/so_.../certificate?h=9f2c…"
}The verify_url lets anyone confirm the sign-off is genuine without seeing its content — useful for your own audit trail. certificate_url returns a PDF certificate with the full record (send your API key); public_certificate_url is a shareable version without the question or answer.
Webhooks
We POST signoff.answered, signoff.expired and signoff.cancelled events to your webhook URL, with the full sign-off as data. Each delivery is signed:
Luminary-Signature: t=1760374931,v1=<hex HMAC-SHA256 of "<t>.<raw body>" using your webhook secret>// Node.js
import crypto from "crypto";
function verify(rawBody, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}Webhooks are best-effort; polling the request always gives the current state.
Other endpoints
POST /api/v1/estimate— price, deadline and how many experts are available, without creating anything (send credential, jurisdiction, depth, tier, speed).GET /api/v1/expertise— which credentials and states have verified experts now.GET /api/v1/signoffs— your 50 most recent requests (filter with?status=answered).POST /api/v1/signoffs/:id/cancel— withdraw a request nobody has claimed yet (refunded).GET /api/v1/account— your organization, key mode and credit balance.
Connect an AI assistant (MCP)
Luminary is also an MCP server, so an AI assistant can ask an expert directly — no code needed. Server URL: https://theluminary.group/api/mcp
Easiest: sign in. Add the URL as a custom connector in your AI app (in Claude: Settings → Connectors → Add custom connector). The app opens a Luminary sign-in page; choose which account to connect and click Allow. Everyone gets a free personal sandbox; live accounts are set up by our team during the pilot. Disconnect any time from your account page.
Claude Code (then run /mcp to sign in):
claude mcp add --transport http luminary https://theluminary.group/api/mcpOr use an API key (servers and scripts). Claude Code:
claude mcp add --transport http luminary https://theluminary.group/api/mcp \
--header "Authorization: Bearer lum_test_..."Claude Desktop, Cursor and other apps that take a JSON config:
{
"mcpServers": {
"luminary": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://theluminary.group/api/mcp",
"--header", "Authorization:${LUMINARY_AUTH}"],
"env": { "LUMINARY_AUTH": "Bearer lum_test_..." }
}
}
}The assistant gets these tools:
list_expertise— which licenses and states have verified experts.estimate_price— price, deadline and availability before asking.ask_expert— send the decision; returns a ticket right away.check_request— status, and the signed verdict once answered (can wait up to 25 seconds).list_requests,cancel_request,get_account.
With a live key, ask_expert spends credits, and the assistant is told to confirm the price with you first. Start with a test key: the sandbox answers in seconds and costs nothing.
Pricing
Priced per review, not per hour. Base price at standard tier, next business day:
| quick | Verdict plus one-line rationale (~10 min) | $49 |
| standard | Edits plus rationale (~25 min) | $129 |
| deep | Documents or complex context (~60 min) | $349 |
Tier: senior ×2, tier_one ×4. Speed: two_days ×0.8, four_hours ×1.5, one_hour ×2, live_15 ×3. Example: a senior standard review in four hours is $387.
Errors
Errors return JSON: {"error": {"type": "...", "message": "..."}}.
400 invalid_request— a field is missing or wrong; the message says which.401 unauthorized— missing or revoked key.402 insufficient_credits— add credits to your account.409 no_experts_available— no verified expert matches yet; try another tier or speed.422 phi_detected— the content looks like it contains patient identifiers.429 rate_limited— more than 120 requests a minute.