Skip to navigation

Embeds

Show a CloudRaker review, transcript or document inside your own app, in an iframe.
View as Markdown

An embed puts a CloudRaker view on your page. Your users review extracted values, correct them and approve them without a CloudRaker account. Your backend creates a short-lived embed session with your API key. Your page loads the session URL in an iframe.

The embed has three views:

ViewShowsModes
reviewThe results of an extraction run (exr_…), next to each source document and its citations.read or write
transcriptAn audio file with its speaker transcript.read
documentA PDF, the preview of an office file, or an image.read

In write mode, the person can edit values and approve a document. Each saved change and each approval sends a signed webhook and a message to your page.

How it works

1

Run an extraction

Call POST /v1/extract with the default unit per_document. Wait until the run status is processed.

2

Create a session on your backend

Call POST /v1/embeds with your API key. Send the run, the mode, the origins of the pages that show the iframe and, optionally, the person, a webhook and a theme. The response has a url.

3

Put the URL in an iframe

Send url to your page and set it as the src of an iframe. The session token is in the URL fragment. Treat the URL as a secret.

4

Receive the results

Listen for message events on your page to update your UI. Use the webhook (or GET /v1/runs/:id) as the record of the change.

5

Revoke the session

Call DELETE /v1/embeds/:id when the person closes the view. A session also ends by itself at expiresAt.

Create sessions on your backend only. Your API key must never reach the browser. Create a new session for each person and each visit. Do not share a session URL between people.

Create a session

curl -sX POST https://api.cloudraker.com/v1/embeds \
-H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"run": { "id": "exr_01K6ZQ8X4J3M2N7P5R9T1V0W2Y" },
"mode": "write",
"origins": ["https://emr.partner.example"],
"user": { "id": "clinician-4821", "name": "Dre Tremblay", "email": "[email protected]" },
"webhook": { "id": "whe_01K6ZQ9A1B2C3D4E5F6G7H8J9K" },
"theme": {
"mode": "light",
"colors": { "primary": "#0f766e", "primary-foreground": "#ffffff" },
"radius": "8px",
"font": "system"
},
"locale": "fr-ca",
"metadata": { "encounterId": "enc_77" },
"ttl": 3600
}'

The response is 201:

{
"object": "embed_session",
"id": "ems_01K6ZQB3C4D5E6F7G8H9J0K1M2",
"view": "review",
"mode": "write",
"url": "https://embed.cloudraker.com/embed/ems_01K6ZQB3C4D5E6F7G8H9J0K1M2#token=eyJhbGciOiJIUzI1NiJ9…",
"run": { "id": "exr_01K6ZQ8X4J3M2N7P5R9T1V0W2Y" },
"documents": ["file_01K6ZQ7W3H2G1F0E9D8C7B6A5Z"],
"expiresAt": "2026-09-29T15:04:05.000Z",
"createdAt": "2026-09-29T14:04:05.000Z"
}
FieldNotes
viewreview (default), transcript or document.
runreview only. An extraction run id (exr_…). The run must be processed.
documentsreview only. 1 to 20 file ids of the run, in the order to show them. The default is every finished document of the run, in run order.
filetranscript and document only. A ready file.
spacetranscript and document only. The space of file. The default is the files of /v1/files.
moderead (default) or write. transcript and document are always read.
origins1 to 5 exact origins of the pages that frame the iframe, such as https://app.example.com. No path, no wildcard, no trailing slash. Use https, or http://localhost for local work.
userThe person who uses the view. See Identity.
webhookwrite mode only. { "url": "…" } or { "id": "whe_…" }. See Webhooks.
themeSee Themes.
localeen (default) or fr-ca.
metadataYour own JSON, at most 10 KB. It comes back on every embed webhook.
ttlSession lifetime in seconds. Default 3600, minimum 60, maximum 28800.

A transcript session binds one file:

curl -sX POST https://api.cloudraker.com/v1/embeds \
-H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "view": "transcript", "file": { "id": "file_01K6ZQ7W3H2G1F0E9D8C7B6A5Z" }, "origins": ["https://emr.partner.example"] }'

Put the iframe on your page

<iframe
src="{session.url}"
title="CloudRaker review"
allow="clipboard-write"
referrerpolicy="no-referrer"
style="width: 100%; height: 720px; border: 0"
></iframe>
  • Your page sets the size. The embed fills the frame.
  • allow="clipboard-write" lets the person copy values.
  • The page must be one of the session origins. When a page from another origin frames the embed, the embed refuses to render and sends no message.
  • The embed removes the token from its address bar after it loads. A reload of the iframe keeps the session.

If you sandbox the iframe, use exactly these flags:

sandbox="allow-scripts allow-same-origin allow-forms allow-popups"

A sandbox without allow-same-origin gives the frame an opaque origin. The embed does not support that.

Messages to your page

The embed posts messages to window.parent. It sends them only to an origin in the session origins, never to *. It ignores all messages that your page sends to it.

type EmbedMessage = {
source: "cloudraker-embed";
version: 1;
trusted: false;
sessionId: string; // ems_…
type: "ready" | "document_saved" | "document_approved" | "error";
payload:
| { view: "review" | "transcript" | "document"; mode: "read" | "write" } // ready
| { fileId: string; rev: number; changedKeys: string[]; document: Document } // document_saved, document_approved
| { code: "session_expired" | "review_closed" | "conflict" | "rate_limited" | "load_failed" }; // error
};

document has the same shape as a document in GET /v1/runs/:id: fileId, name, value and citations.

Always check the origin and the session id before you use a message:

const embedOrigin = new URL(session.url).origin; // https://embed.cloudraker.com
window.addEventListener("message", (event) => {
if (event.origin !== embedOrigin) return;
const msg = event.data;
if (msg?.source !== "cloudraker-embed" || msg.sessionId !== session.id) return;
if (msg.type === "document_approved") closeReviewModal();
});

trusted: false means what it says. A message is a UI signal only. The browser can change it. Use the signed webhook, or GET /v1/runs/:id, as the record of a save or an approval.

In read mode, the embed posts only ready and error.

Webhooks

A write session sends two event types:

TypeFires when
embed.document_savedThe person saves a change to a document.
embed.document_approvedThe person approves a document, with or without a change.

A save with no change sends nothing. The events go to the session webhook and to every saved endpoint of your organization that subscribes to the type, or to all types. Each endpoint gets one delivery per event.

{
"eventId": "9b2f6c1e-4a7d-43b0-8c55-0e1f2a3b4c5d",
"type": "embed.document_approved",
"processingId": "exr_01K6ZQ8X4J3M2N7P5R9T1V0W2Y",
"occurredAt": "2026-09-29T14:12:30.000Z",
"data": {
"embedSessionId": "ems_01K6ZQB3C4D5E6F7G8H9J0K1M2",
"runId": "exr_01K6ZQ8X4J3M2N7P5R9T1V0W2Y",
"fileId": "file_01K6ZQ7W3H2G1F0E9D8C7B6A5Z",
"rev": 1,
"changedKeys": [],
"document": {
"fileId": "file_01K6ZQ7W3H2G1F0E9D8C7B6A5Z",
"name": "call.m4a",
"value": { "reason": "Toux", "diagnosis": "Bronchite aiguë" },
"citations": { "reason": [{ "fileId": "file_01K6ZQ7W3H2G1F0E9D8C7B6A5Z", "timecodeStart": 12.4, "timecodeEnd": 15.1, "timecode": 12.4, "text": "…", "confidence": 4 }] }
},
"user": { "id": "clinician-4821", "name": "Dre Tremblay", "verified": false },
"metadata": { "encounterId": "enc_77" }
}
}
  • document is the full document after the change, not a diff. changedKeys lists the fields that changed.
  • An edited field loses its citation.
  • user is absent when the session has no user. metadata is absent when the session has none.
  • eventId is stable across retries. A save and an approval at the same rev have different ids. Dedupe on eventId.
  • CloudRaker does not store an approval state. Record the approval on your side when you receive the event.

Verify every delivery. Embed events use the same signature as all other events: the x-rk1-signature ES256 JWT, checked against GET /v1/webhooks/jwks.json, with iss rakerone-process and a bodySha256 claim for the raw body. The webhooks page has the steps and sample verifiers. In short:

const { payload } = await jwtVerify(req.headers["x-rk1-signature"], JWKS, {
issuer: "rakerone-process",
algorithms: ["ES256"],
});
if (createHash("sha256").update(rawBody).digest("base64url") !== payload.bodySha256) {
throw new Error("body digest mismatch");
}

Embed delivery is best effort. Each event gets up to 5 attempts, about 0.5, 1, 2 and 4 seconds apart, with a 10-second timeout on each attempt. The change stays saved in CloudRaker when all attempts fail. GET /v1/runs/:id shows it, and your page also gets the message.

Save conflicts (rev)

Each document has a revision number, rev. It starts at 0. Each saved change adds 1.

The embed sends the rev it loaded with each save. If the document changed after the page loaded, the save fails. For example, a colleague edited it in the CloudRaker app, or in another session. The embed then:

  • shows the banner “This document changed after you opened it.” with a Reload button. Reload loads the current values and drops the local edits.
  • posts an error message with code conflict.

A stale approval fails in the same way. Events can arrive out of order. Use rev to keep the newest version of each document.

Themes

Send theme to match your app. All keys are optional. An unknown key or a value that does not match its pattern is a 400.

KeyValues
modelight (default) or dark. It is fixed for the session. The embed does not follow your page or the operating system.
colorsHex colors (#rgb, #rgba, #rrggbb or #rrggbbaa) by key.
radiusCorner radius, such as 8px or 0.5rem. At most 32px or 2rem.
fontinter (Inter) or system (the system font of the device). The default is Inter for text and Space Grotesk for headings.

The color keys are:

background, foreground, card, card-foreground, popover, popover-foreground, primary, primary-foreground, secondary, secondary-foreground, muted, muted-foreground, accent, accent-foreground, destructive, border, input, ring.

The patterns accept only plain values. Other CSS (url(, var(, ;, { and comments) always fails. The confidence rating colors do not change with the theme.

Identity

user is the person who uses the view, as your system knows them: { id, name, email? }.

  • CloudRaker does not verify it. It stores it as you send it.
  • The audit log names the person as ext:<id> with the given name, and marks the identity as not verified.
  • Webhooks carry { id, name, verified: false }.
  • The email stays with CloudRaker. No webhook, message or audit event contains it.

Revoke a session

curl -sX DELETE https://api.cloudraker.com/v1/embeds/ems_01K6ZQB3C4D5E6F7G8H9J0K1M2 \
-H "Authorization: Bearer $CLOUDRAKER_API_KEY"

The response is 204, also for a session that is already revoked. The iframe stops working on its next request. There is no route to list or read sessions, so store the id when you create a session.

Limits

LimitValue
Session lifetimettl default 1 hour, from 60 seconds to 8 hours. The session also ends when the run expires.
Editing windowwrite mode ends 7 days after the extraction run started. A write session never lasts past that time. After it, create a read session.
Minimum time leftA session with less than 60 seconds left is refused with 409 review_closed.
Documents1 to 20 per review session. For a larger run, send documents and create one session per group.
RunsExtraction runs (exr_…) with unit per_document only. Other units and other run types get 422 unsupported_run. A /process id gets 404 not_found.
Origins1 to 5 per session. Each origin must also be on the allowed list for your organization (see below).
Rate600 reads and 120 saves per minute per session. Then rate_limited.

Origin onboarding

The embed shows only on pages that CloudRaker allows to frame it. Before you go live, send the exact origins of your pages (for example https://emr.partner.example) to your account team. CloudRaker adds them to the allowed list. The origins of each session must be on that list.

On test environments, http://localhost and http://127.0.0.1 on any port are allowed.

Errors

StatuscodeWhen
400invalid_requestThe body fails validation. This includes a bad theme, a bad origin, a bad webhook URL and more than 20 documents.
401, 403unauthorized, forbiddenThe normal authentication errors.
404not_foundUnknown run, file, space or session.
409result_not_readyThe run is not processed yet.
409review_closedThe review data is gone, the run has no finished document, or less than 60 seconds remain.
409file_not_readytranscript and document: the file is not ready.
410run_expiredThe run expired.
422unsupported_runNot an extraction run, or the unit is not per_document.
422unknown_documentA documents id is not a finished document of the run.
422unsupported_fileThe file type does not fit the view.
503embed_unconfiguredEmbeds are not available now.

Where to go next