intakeScribe abhix-aiLog inSign up
API reference

Integrate intakeScribe

A REST API for AI medical intake. Authenticate with an API key from your dashboard (header X-API-Key). All requests and responses are JSON.

Integration optionsREST APIJS SDKWidgetHosted pageVoiceMCP serverStreamingWebhooksStatus codes

Integration options

Every intake is driven by the same configurable form template. Choose the integration style that fits your stack — you can mix and match:

REST API
Your own UI, your own backend. Full control — authenticate with a secret key and call the JSON API directly.
JS SDK
Official TypeScript client. Browser-safe publishable keys for custom frontends; secret keys for Node.js backends.
Embeddable widget
One <script> tag. Our prebuilt chat + voice UI appears as a floating bubble or inline in any container.
Hosted page
Zero integration. Share a link. Patients open it and complete the intake; results arrive via webhook or API.
Voice
$0 extra. Browser speech-to-text + text-to-speech on-device. Built into the widget and hosted page.
MCP server
Let AI agents (Claude, etc.) automate intakeScribe: create forms, run intakes, read results.

REST API — Quickstart

Use a secret key (isk_...) from your dashboard. The pattern: define a template once, start a session, loop POST /sessions/:id/turn until completed, then fetch the structured intake.

# 1. Define a template (once)
curl -X POST https://intakescribe-api-production.up.railway.app/templates \
  -H "X-API-Key: $INTAKESCRIBE_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "New Patient Intake",
        "fields": [
          { "id": "full_name", "prompt": "the patient's full name", "type": "text" },
          { "id": "dob", "prompt": "date of birth", "type": "date" },
          { "id": "reason", "prompt": "reason for visit", "type": "text" } ] }'

# 2. Start a session
curl -X POST https://intakescribe-api-production.up.railway.app/sessions \
  -H "X-API-Key: $INTAKESCRIBE_KEY" -H "Content-Type: application/json" \
  -d '{ "template_id": "tmpl_..." }'

# 3. Send a turn (repeat until completed)
curl -X POST https://intakescribe-api-production.up.railway.app/sessions/SESSION_ID/turn \
  -H "X-API-Key: $INTAKESCRIBE_KEY" -H "Content-Type: application/json" \
  -d '{ "message": "Jane Doe" }'

# 4. Fetch the completed intake
curl https://intakescribe-api-production.up.railway.app/intakes/INTAKE_ID -H "X-API-Key: $INTAKESCRIBE_KEY"
Template fields. Each field needs an id, a prompt, and a type. Supported types: text, integer, number, boolean, date, email, phone, choice, multi_choice. Optional per-field keys: required, help_text, confirm, min, max, pattern, and choices (required for choice / multi_choice).

JS SDK

Use the official TypeScript client in the browser or on the server. The API surface is identical — only the key type changes. The SDK is a thin wrapper over the REST API, so if you'd rather not add a dependency you can call the endpoints directly with fetch (see the REST API quickstart) — that path always works, no install required.

Heads up: the @intakescribe/sdk npm package is not published yet (coming soon). For now, either use the REST/fetch path above, or install the SDK from the repo:
# (npm package "@intakescribe/sdk" coming soon — not on npm yet)
# Build the SDK from the repo and link it locally:
git clone https://github.com/Abhisg5/intakeScribe.git
cd intakeScribe/sdk
npm install && npm run build
npm link          # then `npm link @intakescribe/sdk` in your project

# Prefer zero dependencies? Skip the SDK and call the REST API with fetch
# (see the REST API quickstart above).

Browser — publishable key

Publishable keys (pk_...) are safe to ship in client-side bundles. They are origin-restricted (configure allowed origins in the dashboard) and never expose completed intake data — completed records are only accessible with your secret key or via your completion webhook.

import { createClient } from "@intakescribe/sdk";

const client = createClient({
  publishableKey: "pk_live_...",          // browser-safe; origin-restricted
  baseUrl: "https://intakescribe-api-production.up.railway.app",
});

// Start a session — returns the first question
const session = await client.startSession("tmpl_...");
console.log(session.message);

// Send answers turn by turn until the intake is complete
let result = await client.sendTurn(session.session_id, "Jane Doe");
while (!result.completed) {
  const userInput = await yourUI.prompt(result.message);
  result = await client.sendTurn(session.session_id, userInput);
}
// Done. On the publishable (browser) path the completed record is NOT returned
// here for PHI safety — result.intake_id is undefined. Receive the finished
// intake on your server via the completion webhook, or fetch it with your
// secret key. (The server/secret-key path does return result.intake_id.)

// Or stream the bot reply token by token:
await client.streamTurn(session.session_id, "Jane Doe", (delta) => {
  appendToChat(delta);
});

Node.js — secret key

Secret keys (isk_...) have access to the full data plane including completed intakes. Keep them server-side only.

import { createClient } from "@intakescribe/sdk";

const client = createClient({
  secretKey: process.env.INTAKESCRIBE_KEY,   // isk_... — server-side only
  baseUrl: "https://intakescribe-api-production.up.railway.app",
});

// Same API surface as the browser client
const session = await client.startSession("tmpl_...");
const result  = await client.sendTurn(session.session_id, "Jane Doe");
// On the secret-key path, result.intake_id is populated once completed —
// fetch the structured record with GET /intakes/:id.
Custom voice UI: The SDK is also the path for building your own voice interface. Bring your own STT/TTS pipeline and feed transcribed text to sendTurn or streamTurn — the API is the same.

Embeddable widget

Add one <script> tag to get our prebuilt chat + voice UI. By default it mounts as a floating bubble in the bottom-right corner; use data-target to embed it inline. Create a publishable key and configure allowed origins in the dashboard Integrate page.

<!-- Drop this anywhere in your page -->
<script
  src="https://intakescribe.abhix-ai.com/widget/v1.js"
  data-pk="pk_live_..."
  data-template="tmpl_...">
</script>

<!-- Inline (embedded in a container instead of a floating bubble): -->
<div id="intake-widget"></div>
<script
  src="https://intakescribe.abhix-ai.com/widget/v1.js"
  data-pk="pk_live_..."
  data-template="tmpl_..."
  data-target="#intake-widget">
</script>

<!-- Enable voice mode by default: -->
<script
  src="https://intakescribe.abhix-ai.com/widget/v1.js"
  data-pk="pk_live_..."
  data-template="tmpl_..."
  data-voice="1">
</script>
The script also exposes window.IntakeScribe.mount(cfg) for programmatic control.

Hosted intake page

No integration required. Go to the dashboard Integrate page, create a hosted link for your template, and share the URL with patients:

https://intakescribe.abhix-ai.com/i/<your-slug>

Patients open the link, complete the intake (chat or voice), and the results arrive on your server via your configured completion webhook — or you can poll GET /intakes/:id with your secret key. Hosted pages support voice out of the box; patients can switch between chat and voice at any time.

Voice

Patients can speak instead of type at no extra cost. The browser handles speech-to-text and text-to-speech entirely on-device using the Web Speech API — you only pay for the same LLM tokens as a text intake.

Voice is built into both the widget and hosted page. Enable it by default with data-voice="1" on the widget script tag (patients can always switch to chat). Patients without a supported browser fall back to the text chat automatically. Best support: Chrome and Edge desktop.

<!-- Widget with voice enabled by default -->
<script
  src="https://intakescribe.abhix-ai.com/widget/v1.js"
  data-pk="pk_live_..."
  data-template="tmpl_..."
  data-voice="1">
</script>
Custom voice UI: Use the JS SDK (sendTurn / streamTurn) with your own STT/TTS pipeline for full control over the voice experience. See the JS SDK section above.

MCP server (for AI agents)

The intakescribe-mcp package wraps the REST API as Model Context Protocol tools so an AI agent (Claude Desktop, Claude Code, or any MCP client) can automate intakeScribe — create forms, run intakes, and read results — without writing any glue code.

# (PyPI release coming soon.) Install directly from the repo:
pip install "intakescribe-mcp @ git+https://github.com/Abhisg5/intakeScribe.git#subdirectory=mcp"

# Or, from a local checkout of the mcp/ directory:
#   pip install -e .
# Either way you get the `intakescribe-mcp` console command (used in the config below).
MCP client config
{
  "mcpServers": {
    "intakescribe": {
      "command": "intakescribe-mcp",
      "env": {
        "INTAKESCRIBE_API_KEY": "isk_live_...",
        "INTAKESCRIBE_API_URL": "https://intakescribe-api-production.up.railway.app"
      }
    }
  }
}
Available tools
ToolDescription
list_templates()List all intake form templates for the account
create_template(name, fields, description?)Create a new intake form; returns the new template with its id
get_template(template_id)Inspect a template's full field schema
start_intake(template_id)Start a session; returns session_id + the opening question
send_patient_message(session_id, message)Send a patient answer; returns the next question, completed flag, and intake_id when done
get_session(session_id)Get a session's current status and full transcript
get_intake(intake_id)Retrieve the completed, structured intake (contains PHI)
get_usage()Check account usage totals
Security: The secret key (isk_...) grants full data-plane access including completed intakes which may contain PHI. Run the MCP server in your own infrastructure with the key supplied via environment — do not ship it to browsers or commit it to source control.

Streaming (SSE)

Use /turn/stream to stream the agent's reply token-by-token. Concatenate the deltas, then read the final done event.

POST https://intakescribe-api-production.up.railway.app/sessions/SESSION_ID/turn/stream
→ Content-Type: text/event-stream

event: message
data: {"delta": "What is "}

event: message
data: {"delta": "your date of birth?"}

event: done
data: {"session_id": "...", "status": "in_progress", "completed": false, "intake_id": null}

Webhooks

Set completion_webhook_url on a template. On completion we POST the signed payload. Verify the X-IntakeScribe-Signature header (HMAC-SHA256 of the raw body with your key's webhook secret).

Node
import crypto from "node:crypto";

function verify(rawBody, signature, secret) {
  const expected =
    "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

// Express handler (use the RAW body, not the parsed JSON):
app.post("/hook", express.raw({ type: "*/*" }), (req, res) => {
  const sig = req.header("X-IntakeScribe-Signature");
  if (!verify(req.body, sig, process.env.WEBHOOK_SECRET)) return res.status(401).end();
  const event = JSON.parse(req.body.toString());
  // event.payload is the completed intake
  res.json({ ok: true });
});
Python
import hmac, hashlib

def verify(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)

# FastAPI:
@app.post("/hook")
async def hook(request: Request):
    raw = await request.body()
    sig = request.headers.get("X-IntakeScribe-Signature", "")
    if not verify(raw, sig, WEBHOOK_SECRET):
        raise HTTPException(401)
    event = json.loads(raw)  # event["payload"] = completed intake
    return {"ok": True}

Status codes

CodeMeaning
200 / 201Success
401Missing or invalid API key
404Resource not found (or not yours)
409Session already completed
429Monthly token quota exceeded