Let the agent do it.
Your assistant chooses the right tool from plain language. No integration backend to build.
- Claude Desktop, Claude Code, Cursor
- Upload, check, and ask
- Readable answers by default
Developers
Connect once. Your software can ask a Mind Twin how a teammate will think, feel, say, and act, then keep that understanding current from the same workspace.
YouAsk Maya's Mind Twin what would make her reject this rollout.
{ target: "Maya Chen" }Start with what you are building
Both paths create a Mind Twin when there isn't one, upload, check readiness, and ask. Choose who should make the call.
Your assistant chooses the right tool from plain language. No integration backend to build.
Your server controls every request and receives structured JSON. No SDK required.
Before your first call
Get your key, choose who you need, then connect your software.
Use the MindTwins app to set up a shared team workspace and invite each person whose Mind Twin you need. Invitees receive their Mind Twin when they join. For someone you only meet with, your integration can create a private Mind Twin of them instead. See Create a Mind Twin.
Open Settings › Connectors in the MindTwins app. Use the provisioned twin:chat key for your own Mind Twin. For teammates, ask your workspace administrator to enable Mind Twin Teams and provide a key with twin:upload. Keep the key on your server.
Call list_twins in MCP or GET /twins in REST. Use the returned display name as target, or the person's email if names collide. Listing is not an access guarantee. Check GET /me/status with the intended target before uploading.
REST chat examples use a teammate named Maya Chen and require a Teams key. Creation and MCP examples use a private Mind Twin of Jordan Lee on twin:chat. To work with your own Mind Twin, omit target.
For someone you meet with
Their perspective, private to you. Start here when there is no Mind Twin you can reach.
twin:chatNo credits to createOnly you can see or ask it. It is never linked or merged with anyone's own Mind Twin.
needs_meetingBefore the first uploadGive it a meeting the person speaks in. Then check status before you ask.
list_twins Look first. If you already made a Mind Twin of this person, add the meeting to that one.
create_mind_twin Create one only when it is missing. Keep the returned twin_id for target.
upload_transcript Add a meeting the person speaks in. Use their full name or twin_id as target.
get_status Check the same target until it is ready.
chat_with_twin Ask the same target a question.
name is required, 1 to 200 characters after trimming. email is optional, up to 320 characters; description is optional, up to 500. Include only details you know. idempotency_key is optional, 1 to 200 characters. REST also accepts Idempotency-Key; the body field wins when both are present. Reusing the same key with the same name, email, and description within 24 hours returns the first successful answer. MCP generates a key when you omit it. Creating is free and needs only twin:chat, with a separate limit of 10 per hour per person.
{
"name": "create_mind_twin",
"arguments": {
"name": "Jordan Lee",
"description": "Product lead",
"idempotency_key": "create-jordan-2026-10-29"
}
}POST https://app.mindtwins.com/api/twin-mcp/twins
Authorization: Bearer <your-key>
Content-Type: application/json
{
"name": "Jordan Lee",
"description": "Product lead",
"idempotency_key": "create-jordan-2026-10-29"
}{
"content": [
{
"type": "text",
"text": "Created a Mind Twin of Jordan Lee. Only you can see it or ask it.\nIt has nothing to learn from yet: add a meeting Jordan Lee speaks in with upload_transcript (target \"Jordan Lee\").\nIts twin_id: example-created-id"
}
]
}{
"twin_id": "example-created-id",
"mind_id": "example-created-mind_id",
"name": "Jordan Lee",
"email": null,
"ownership": "created",
"ready": false,
"status": "needs_meeting",
"visible_to": "only_you"
}Read the code, then the sentence. ALREADY_CREATED also returns twin_id; add the meeting to that Mind Twin. For IN_PROGRESS, wait before retrying the same request. For RATE_LIMITED, wait for Retry-After in seconds.
| Status | Code | Exact error sentence |
|---|---|---|
400 | VALIDATION | A Mind Twin needs the person's name, so nothing was created. |
400 | TOO_LONG | That name, email or description is too long, so nothing was created. |
400 | BAD_IDEMPOTENCY_KEY | An idempotency_key must be 1 to 200 characters, so nothing was done. |
403 | CREATION_OFF | Creating Mind Twins is turned off for your workspace, so nothing was created. |
409 | IS_YOU | That's you. Your own Mind Twin already learns from your meetings, so nothing was created. |
409 | ALREADY_CREATED | You already created a Mind Twin with this name, so nothing new was created. Add the meeting to that one. |
409 | IN_PROGRESS | The same request is still running, so nothing new was done. Try again in a moment. |
429 | RATE_LIMITED | You've made 10 Mind Twins this hour, so nothing was created. Try again later. |
502 | INTERNAL | Failed to create the Mind Twin |
A Mind Twin you created stays only yours, even if that person later joins MindTwins.
Prepare the source
A Mind Twin needs to know which words belong to its person.
WEBVTT
00:00:01.000 --> 00:00:05.000
<v Maya Chen>I want to test one region before we commit to a full rollout.</v>
00:00:05.000 --> 00:00:09.000
<v Jordan Lee>What would you need to see before expanding?</v>
00:00:09.000 --> 00:00:14.000
<v Maya Chen>Four weeks of steady adoption and a clear way to stop if it slips.</v>Speaker 1: I want to test one region first.
Meet participant 2: What would you need to see?In plain text, Name: text accepts letters, digits, spaces, and . ' - in the name, or a plain email. For other names, including + or _, use [Name]: text. Bracket labels work in plain text and on a WEBVTT cue line. A name containing ] and sound cues such as [Music] are not speaker labels. SRT does not preserve bracket labels or voice tags; use agent-json for those names. Match the person you are feeding; for your own meetings, read speaker_name fresh from whoami or GET /me.
[Alex+Rivera]: Let's start with one region.
[Jordan Lee]: We can review the results first.Recommended request budget: under 4,000,000 bytes for the complete UTF-8 JSON body. This is a conservative client budget, not an exact service maximum. Count the encoded body, including JSON escaping and metadata. Oversized requests can return a plain-text 413 FUNCTION_PAYLOAD_TOO_LARGE before the API runs. The MCP transcript schema limit does not override HTTP or downstream limits. REST has a larger application cap of 52,428,800 bytes for transcript plus segment text and 54,525,952 bytes for the raw JSON body; the public hosting limit applies first, so these are not usable upload sizes at this URL.
The complete integration
Create once if needed. Then three actions take a meeting to a decision-ready answer.
A Mind Twin of someone you meet with is free to create, private to you, and starts empty. Skip this when the person already has one you can reach.
create_mind_twinSend a dated transcript. VTT, SRT, text, and agent JSON all work.
upload_transcriptLearning runs in the background. Poll periodically until the status is ready.
get_statusAsk about a past decision or a new situation. Get the answer and the reasoning around it.
chat_with_twinBuilt in Re-uploading a transcript already built returns 200. Queued duplicates can also return 200. A 409 ALREADY_PROCESSING means nothing more to do.
Path 01 · MCP
One hosted server gives an MCP-capable assistant everything it needs to keep a Mind Twin current and ask it questions.
The command below runs through npx, which comes with Node.js.
brew install nodenodejs.orgDownload sudo apt install nodejsnode --version
npx --versionOpen Settings › Connectors in the MindTwins app. Copy your provisioned twin:chat key. For teammates, follow Teams setup.
The installer connects every supported MCP client it detects.
If the tools do not appear, quit and reopen the client. Then ask which mind-twin tools it sees.
npx -y add-mcp mcp-remote --name mind-twin \
--args=https://mcp.mindtwins.com/mcp \
--args=--header --args="Authorization: Bearer dev_YOUR_TWIN_CHAT_KEY" \
--all -g -yclaude mcp add --transport http mind-twin https://mcp.mindtwins.com/mcp \
--header "Authorization: Bearer dev_YOUR_TWIN_CHAT_KEY"Some clients load new servers only when they open. If the mind-twin tools do not appear, fully quit and reopen the client, then ask which mind-twin tools it can see.
https://mcp.mindtwins.com/mcpmind-twinAgent integrations
The universal installer is the supported fast path. It detects compatible clients, adds the hosted server, and includes your Bearer header.
Call in. Result out.
Ask one consequential question and request JSON when your agent needs structure.
{
"name": "chat_with_twin",
"arguments": {
"message": "What would make you reject this rollout?",
"target": "Jordan Lee",
"format": "json"
}
}{
"content": [
{
"type": "text",
"text": "{\n \"answer\": \"I would reject a company-wide rollout without a contained pilot.\",\n \"think\": \"The upside is clear. The implementation risk is not.\",\n \"feel\": \"Interested, but exposed if adoption fails.\",\n \"say\": \"Show me one region working first.\",\n \"act\": \"Approve a 30-day pilot with a stop point.\"\n}"
}
]
}Good to knowformat defaults to text. Pass json for the structured answer, think, feel, say, and act object inside content[0].text. Missing TFSA fields may be omitted. MCP chat does not remember earlier messages. Include the relevant conversation context in each message.
MCP tool results use a content array containing text. Upload and status return readable text. Chat with format: json puts a JSON string inside content[0].text; parse that string to read the answer fields. These examples show the tool result, without the outer JSON-RPC envelope. Tool failures normally set isError: true; inspect it before reading content as success.
OpenAPI 3.1 reference. Response examples use illustrative values.
upload_transcriptUpload field referenceLimits below are validated by the MCP tool. REST names show what the MCP client sends.
| MCP field | REST field | Type and limit | Meaning |
|---|---|---|---|
transcript | transcript | string · required · 1 to 10,000,000 characters | One meeting in VTT, SRT, plain text, or agent JSON. The character limit is the MCP schema limit; the encoded request budget still applies. |
date | date | ISO-8601 string · required | The real meeting start time with its UTC offset, or YYYY-MM-DD when the time zone is unknown. Use the recording app's meeting time or a date supplied in the transcript or by the user. Never use file timestamps. Preserve the exact string on retries. |
title | title | string · optional · 1 to 500 characters | The meeting's actual title, when known. |
format | format | enum · optional | vtt, srt, text, or agent-json. Omit to pass VTT or plain text through; set srt or agent-json explicitly. There is no auto or json format value. |
target | target | string · optional · up to 200 characters | Use a full name, email, @handle when available, or twin_id. A teammate needs twin:upload; a Mind Twin you created needs only twin:chat. MCP trims whitespace and treats blank as omitted; REST rejects an empty string. Omit for your own Mind Twin. Partial names are refused on upload. |
participants | participants | string[] · optional · up to 100 names | Each name is trimmed and limited to 200 characters. Blank names are dropped. Keep the same order on retries. |
source | source | enum · optional | The recording app: fathom, zoom, gong, google_meet, fireflies, microsoft_teams, manual, sybill, affinity_notetaker, granola, raw_import. Omit or use manual for pasted text; use raw_import for another app or an export. Never guess. |
duration | duration | number · optional · zero or greater | Meeting duration in seconds, not minutes or milliseconds. Helps space lines when the transcript has no timestamps. |
self_speaker | selfSpeaker | string · optional · up to 200 characters | The exact label for the person this Mind Twin is of, including one you created. With VTT, text, or omitted format, also include it in participants. With SRT or agent-json, it is checked against transcript speakers and participants. A mismatch returns SELF_NOT_A_SPEAKER and saves nothing. Omit it to match by name. |
meeting_ref | meetingRef | string · optional · reserved for automated delivery | Leave out of ordinary custom uploads. If an automated request supplies a meeting ID, preserve it exactly; MCP trims it and forwards at most 128 characters. It is not transcript content or a deduplication key and is ignored for a Mind Twin you created. |
For a retry, resend the same transcript, exact date string, title, format, target, source, duration, and participants in the same order. Changing meeting details can save a second copy. Deduplication compares generated transcript content within the selected Mind Twin. A completed duplicate returns 200; queued or processing duplicates can also return 200 in workspaces with a build queue. ALREADY_PROCESSING means the same transcript is in progress: check status instead of uploading again. BUILD_IN_PROGRESS means this upload was not saved: wait for the current build, then retry. If an upload reply is lost, check status first and preserve the original payload for any retry.
| Tool | What it does | Inputs |
|---|---|---|
whoami | Shows your identity, the name your Mind Twin listens for as you (speaker_name when available), and whether it can answer right now. ready and status use chat's own readiness check: ready, building, or failed. The engine read has a 1.5-second cap; a slow or failed read falls back to the last stored status. | None. |
list_twins | Lists your own, team-visible, and creator-only Mind Twins by display name. Created entries have ownership: created and a meetings count. Use get_status with the intended target to verify access and build readiness. | None. |
chat_with_twin | Asks a Mind Twin. Include conversation context in each message; prior messages are not remembered. | message (required, 1 to 800,000 characters), target, format (text by default or json). |
upload_transcript | Feeds a Mind Twin a dated transcript. | transcript (required, up to 10,000,000 characters), date (required, real ISO-8601 meeting date), title (up to 500 characters), format (vtt, srt, text, or agent-json; omit for VTT/text passthrough), target, participants, source, duration (seconds), self_speaker. See the field reference below. |
get_status | Checks whether a Mind Twin is ready. | target (optional). |
list_snapshots | Lists a Mind Twin's snapshots with id, name, status, and counts. Results are paginated. | target (up to 200 characters), cursor (1 to 500 characters), limit (integer 1 to 100; default 25). |
snapshot_detail | Shows one snapshot's summaries and counts. Raw psychometric records are withheld; the profile can include derived scores and confidence labels. | snapshot_id (required, 1 to 200 characters), target, detail (concise or detailed; default detailed). |
create_mind_twin | Creates a Mind Twin of someone you meet with. Private to you, free to create, and starts empty; give it a meeting next. | name (required, 1 to 200 characters), email, description, idempotency_key. |
delete_raw | Permanently removes one transcript from recall while keeping the snapshot counts and Mind Twin; no rebuild. Preview without confirm, then confirm with the expected_count. Irreversible. | snapshot_id (required, 1 to 200 characters), confirm (omit for preview; true to delete), expected_count (nonnegative integer, required with confirm), idempotency_key (1 to 200 characters), target. |
Path 02 · REST API
Use ordinary HTTPS from any server language. Every authenticated request carries the same Bearer key.
200 · application/json
answer is the complete response. say is the short spoken line. Any empty Think, Feel, Say, or Act field is omitted.
// npm install undici
import { request } from "undici";
const { statusCode, headers, body } = await request(
"https://app.mindtwins.com/api/twin-mcp/chat",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.MINDTWINS_KEY}`,
"Content-Type": "application/json",
},
headersTimeout: 600_000,
bodyTimeout: 600_000,
signal: AbortSignal.timeout(600_000),
body: JSON.stringify({
message: "What would make you reject this rollout?",
target: "Maya Chen",
}),
},
);
if (statusCode < 200 || statusCode >= 300) {
await body.dump();
throw new Error(`MindTwins request failed: ${statusCode}`);
}
if (!String(headers["content-type"]).includes("application/json")) {
await body.dump();
throw new Error("Expected a JSON response");
}
const mindTwin = await body.json();{
"answer": "I would reject a company-wide rollout without a contained pilot.",
"think": "The upside is clear. The implementation risk is not.",
"feel": "Interested, but exposed if adoption fails.",
"say": "Show me one region working first.",
"act": "Approve a 30-day pilot with a stop point."
}https://app.mindtwins.com/api/twin-mcpMind Twin Teams
Use target to choose a teammate's Mind Twin. Omit target for your own Mind Twin. To reach a teammate or a Mind Twin you created, use its full name, email, @handle when available, or twin_id. Read operations also accept a first or last name alone. Upload and delete_raw require a full name or another exact identifier: a partial name returns 404 TARGET_NEEDS_FULL_NAME and nothing is saved or deleted. If a name is ambiguous, use an email or twin_id.
| Person | Target | Scope | Access |
|---|---|---|---|
| Your own Mind Twin | Omit target | twin:chat | You, inside the key's workspace |
| A teammate's Mind Twin | Their full name, email, @handle, or twin_id | twin:upload | Team-visible in the same workspace; owner boundaries stay on |
| A Mind Twin you created | Its full name, email, or twin_id | twin:chat | Only you; never linked to anyone's own Mind Twin |
| Identifier | Reading | Upload or delete_raw |
|---|---|---|
| Full name, email, @handle when available, or twin_id | Accepted when reachable | Accepted when reachable |
| First or last name alone | Accepted when unambiguous | 404 TARGET_NEEDS_FULL_NAME; nothing saved or deleted |
{ "target": "Maya Chen" }Complete reference
Scan the map first. Open an endpoint only when you need the exact fields. This reference covers integration actions; automated installer checks are outside this workflow.
create_mind_twinname required, 1 to 200 characters · email, description, and idempotency_key optional · needs only twin:chat
chat_with_twinmessage required, 1 to 800,000 characters · target optional · format defaults to text; pass json for structured TFSA
upload_transcripttranscript and the real meeting date required · title, format, and target optional · limit 10,000,000 characters at the schema level; request-body limits still apply
get_statusNo fields for your own Mind Twin · target optional · needs_upload and ready + ingesting are expected conditions
| Intent | MCP tool | REST endpoint | Returns |
|---|---|---|---|
| Ask a Mind Twin | chat_with_twin | POST/chat | Answer + TFSA |
| Add a meeting | upload_transcript | POST/upload | Processing state |
| Check readiness | get_status | GET/me/status | Status + progress |
| Check service | Not exposed | GET/health | Service status |
| Identify yourself | whoami | GET/me | Identity + own Mind Twin readiness |
| Find a Mind Twin | list_twins | GET/twins | Mind Twins with name, email, ownership, ready, status |
| Browse snapshots | list_snapshots | GET/snapshots | snapshots, next_cursor, total |
| Inspect a snapshot | snapshot_detail | GET/snapshots/{id} | Snapshot summaries and counts |
| Create a Mind Twin | create_mind_twin | POST/twins | twin_id + visible_to |
| Remove raw material | delete_raw | DELETE/snapshots/{id}/raw | Dry run or confirmed deletion |
OpenAPI 3.1 reference. Response examples use illustrative values.
/chatAsk a Mind TwinSend one question. Omit target for your own Mind Twin. To reach a teammate or a Mind Twin you created, use its full name, email, @handle when available, or twin_id. Read operations also accept a first or last name alone. Upload and delete_raw require a full name or another exact identifier: a partial name returns 404 TARGET_NEEDS_FULL_NAME and nothing is saved or deleted. If a name is ambiguous, use an email or twin_id. The REST response has answer, think, feel, say, and act; empty parts are omitted. format is MCP-only.
messagestring · required · 1 to 800,000 charactersThe question to answer.
targetstring · optional · 1 to 200 charactersA full name, email, @handle when available, or twin_id Requires twin:upload for a teammate.
POST https://app.mindtwins.com/api/twin-mcp/chat
Authorization: Bearer $MINDTWINS_KEY
Content-Type: application/json
{
"message": "What would make you reject this rollout?",
"target": "Maya Chen"
}{
"answer": "I would reject a company-wide rollout without a contained pilot.",
"think": "The upside is clear. The implementation risk is not.",
"feel": "Interested, but exposed if adoption fails.",
"say": "Show me one region working first.",
"act": "Approve a 30-day pilot with a stop point."
}REST chat may add answered_by when you supply target: { you: true } for your own Mind Twin, { name } for a teammate, or { name, ownership: 'created' } for one you created. Treat it as optional. MCP's text format shows whose Mind Twin answered; its json format keeps only answer, think, feel, say, and act.
{
"answer": "Start with one region.",
"answered_by": {
"name": "Maya Chen"
}
}REST chat accepts history: up to 20 earlier turns, each with role (user or assistant) and content. User content allows 800,000 UTF-16 code units; assistant content allows 100,000. Send the turns you want considered on each request. There is no conversation ID that restores earlier turns.
Use context for attached text: one string or up to 10 strings. Each item allows 100,000 UTF-16 code units and, after trimming and normalization, at most 51,200 UTF-8 bytes. Context is allowed on the current question and earlier user turns, never assistant turns. Extract text from PDFs or documents before sending it. The prepared current message plus context must fit 1,470,000 characters after escaping; a punctuation-heavy message can hit this limit before 800,000 input characters. The complete JSON body must also fit the hosting limit.
Requests accept Unicode text and have no language selector or published per-language guarantee. Test representative transcripts and questions in the languages your integration needs before rollout.
{
"message": "How should we respond?",
"context": [
"Jordan Lee: Can we expand the pilot next week?"
],
"history": [
{
"role": "user",
"content": "What matters most for this launch?"
},
{
"role": "assistant",
"content": "A clear rollback plan."
}
]
}/uploadAdd a meetingRecommended request budget: under 4,000,000 bytes for the complete UTF-8 JSON body. This is a conservative client budget, not an exact service maximum. Count the encoded body, including JSON escaping and metadata. Oversized requests can return a plain-text 413 FUNCTION_PAYLOAD_TOO_LARGE before the API runs. The MCP transcript schema limit does not override HTTP or downstream limits. REST has a larger application cap of 52,428,800 bytes for transcript plus segment text and 54,525,952 bytes for the raw JSON body; the public hosting limit applies first, so these are not usable upload sizes at this URL.
The MCP transcript limit is 10,000,000 characters at the schema level; request-body limits still apply.
transcriptstring · requiredVTT, SRT, plain text, or agent JSON.
dateISO-8601 · requiredThe real meeting date or timestamp. Invalid dates return 400 INVALID_DATE. Use the real meeting date; confirm each transcript separately.
titlestring · optional · max 500A human-readable meeting name.
formatenum · optionalvtt, srt, text, or agent-json. Omit for VTT/text passthrough; set srt or agent-json explicitly.
targetstring · optional · 1 to 200 charactersA full name, email, @handle when available, or twin_id Requires twin:upload for a teammate.
participantsstring[] · optional · up to 100 namesMeeting speaker names. Preserve their order on retry.
durationnumber · optional · zero or greaterMeeting duration in seconds.
sourceenum · optionalfathom, zoom, gong, google_meet, fireflies, microsoft_teams, manual, sybill, affinity_notetaker, granola, raw_import. Omit for manual.
selfSpeakerstring · optional · max 200 charactersExact owner speaker label. MCP spells this self_speaker. With VTT/text, also include it in participants. See the upload field reference.
Use the real meeting date in ISO 8601, from January 1, 2000 through 24 hours ahead of server time. Confirm the date for every transcript.
REST-only segments accepts up to 10,000 objects: text (required string), speaker (optional full name), startTime and endTime (optional nonnegative seconds). transcript and date remain required. When segments is supplied, it takes precedence over format normalization. Keep transcript and segments consistent; their combined text contributes to the size limit.
For format: agent-json, transcript is a string containing JSON, not a nested object. Serialize either an array of segments or an object with a segments array. Each segment uses text, speaker, startTime, and endTime; times are nonnegative seconds. Blank or non-string text is skipped, invalid timestamps are ignored, and only the first 10,000 usable segments are retained. Malformed JSON or no usable text returns 400 INVALID_TRANSCRIPT.
{
"date": "2026-10-07",
"format": "agent-json",
"transcript": "{\"segments\":[{\"speaker\":\"Maya Chen\",\"text\":\"Start with a small pilot.\",\"startTime\":0,\"endTime\":4},{\"speaker\":\"Jordan Lee\",\"text\":\"We will review the results first.\",\"startTime\":4,\"endTime\":8}]}"
}For a retry, resend the same transcript, exact date string, title, format, target, source, duration, and participants in the same order. Changing meeting details can save a second copy. Deduplication compares generated transcript content within the selected Mind Twin. A completed duplicate returns 200; queued or processing duplicates can also return 200 in workspaces with a build queue. ALREADY_PROCESSING means the same transcript is in progress: check status instead of uploading again. BUILD_IN_PROGRESS means this upload was not saved: wait for the current build, then retry. If an upload reply is lost, check status first and preserve the original payload for any retry.
A successful upload returns success: true and data.status: processing. Optional data.duplicate: true means the same transcript is already there; it is not a new upload. data.own: true identifies a target that resolved to your own Mind Twin. A Mind Twin you created instead returns data.ownership: created and data.name; it never uses own. These flags do not mean the latest meeting is ready to quote: check status. meeting_ref (MCP) and meetingRef (REST) are reserved for automated meeting delivery. Ordinary custom uploads leave them out; they are not transcript content or part of deduplication, and are ignored for a Mind Twin you created.
POST https://app.mindtwins.com/api/twin-mcp/upload
Authorization: Bearer $MINDTWINS_KEY
Content-Type: application/json
{
"date": "2026-10-07",
"title": "Rollout planning",
"format": "vtt",
"transcript": "WEBVTT\n\n00:00:01.000 --> 00:00:05.000\n<v Maya Chen>I want to test one region before we commit to a full rollout.</v>\n\n00:00:05.000 --> 00:00:09.000\n<v Jordan Lee>What would you need to see before expanding?</v>\n\n00:00:09.000 --> 00:00:14.000\n<v Maya Chen>Four weeks of steady adoption and a clear way to stop if it slips.</v>",
"target": "Maya Chen"
}{
"success": true,
"data": {
"status": "processing"
}
}/me/statusCheck readinessUse ?target=Maya%20Chen for a teammate. Recommended client strategy: check status every 15 seconds, then every 30 to 60 seconds for longer builds. These intervals are guidance, not server requirements. Build time varies; use status rather than a fixed delay. No completion webhooks are available. GET /me/status returns status: ready, building, or failed, plus progress and ingesting. Read them with the optional flags. needs_upload: true means upload a transcript instead of polling an empty Mind Twin. ready + hydrating: true means it can answer from previously learned material, but the latest upload is not available yet. ready + ingesting: true means a transcript is being folded in. latest_upload_failed: true means the newest upload could not be learned. On your own Mind Twin or one you created, latest_failed_upload can identify it by date, filename, optional snapshot_id, and optional reason. queued_uploads counts uploads still waiting in line. Treat absent flags as false; low or zero early progress alone does not mean failure.
| latest_failed_upload.reason | Your next move |
|---|---|
no_speakers | Label who said each line, then upload the corrected conversation. |
owner_not_a_speaker | For your own Mind Twin, label your turns with the exact speaker_name from a fresh GET /me. For another person, match their name in the meeting. |
too_short | Upload a longer, substantive conversation. |
build_error | Try again later; if it persists, use a different conversation. |
The field or reason can be absent when unavailable. A missing reason is unknown, not success.
GET https://app.mindtwins.com/api/twin-mcp/me/status?target=Maya%20Chen
Authorization: Bearer $MINDTWINS_KEY{
"status": "building",
"progress": 42,
"ingesting": false
}/healthCheck service livenessNo key required. Use it for a simple service liveness check.
GET https://app.mindtwins.com/api/twin-mcp/health{
"status": "ok",
"service": "twin-mcp",
"timestamp": "2026-10-08T14:35:56.581Z"
}/meIdentify yourselfShows your identity, the name your Mind Twin listens for as you (speaker_name when available), and whether it can answer right now. ready and status use chat's own readiness check: ready, building, or failed. The engine read has a 1.5-second cap; a slow or failed read falls back to the last stored status. Returns 404 NO_TWIN if you have no Mind Twin.
None.
All of these endpoints require the same Bearer key.
{
"twin_id": "example-twin_id",
"mind_id": "example-mind_id",
"workspace_id": "example-workspace_id",
"user_id": "example-user_id",
"name": "Maya Chen",
"email": "maya\u0040example.com",
"handle": "maya",
"ready": true,
"status": "ready",
"speaker_name": "Maya Chen"
}/twinsFind a Mind TwinList the workspace directory with name, email, ownership (self, team, or created), ready, and status. Created entries can have email: null, no handle, and a meetings count. The optional top-level team.name is the workspace name. Listing does not guarantee target permission; use GET /me/status to check access and build readiness.
None. The response contains twins: [...].
All of these endpoints require the same Bearer key.
{
"twins": [
{
"twin_id": "example-twin_id",
"mind_id": "example-mind_id",
"name": "Maya Chen",
"email": "maya\u0040example.com",
"handle": "maya",
"ready": true,
"status": "ready",
"ownership": "self"
},
{
"twin_id": "example-created-id",
"mind_id": "example-created-mind_id",
"name": "Jordan Lee",
"email": null,
"ready": false,
"status": "needs_meeting",
"ownership": "created",
"meetings": 0
}
],
"team": {
"name": "Acme"
}
}/snapshotsBrowse snapshotsBrowse a Mind Twin's snapshots. The response contains snapshots, next_cursor, and total.
Query: target, cursor, limit (1 to 100; default 25).
All of these endpoints require the same Bearer key.
{
"snapshots": [
{
"snapshot_id": "example-snapshot",
"date": "2026-10-07",
"filename": "Rollout planning",
"status": "completed",
"counts": {
"data_points": 120,
"key_topics": 4,
"findings": 8
}
},
{
"snapshot_id": "example-failed-snapshot",
"date": "2026-10-06",
"filename": "Earlier planning",
"status": "failed",
"counts": null
}
],
"next_cursor": null,
"total": 2
}/snapshots/{id}Inspect a snapshotRead one snapshot's summaries and counts. Raw psychometric records are withheld; the profile can include derived scores and confidence labels.
Path: id. Query: detail (concise or detailed), target.
Findings and Mindsets are learned outputs associated with a snapshot. Browse snapshots, then inspect a snapshot by its ID. The API does not provide a combined per-person Findings or Mindsets view.
All of these endpoints require the same Bearer key.
{
"ready": true,
"snapshot_id": "example-snapshot",
"date": "2026-10-07",
"filename": "Rollout planning",
"status": "completed",
"counts": {
"data_points": 120,
"key_topics": 4,
"findings": 8
},
"summary": {
"disposition": null,
"synthesis": [
{
"ticker": "SYN-01",
"label": "Decision style",
"value": "Prefers evidence before expanding a rollout."
}
],
"categories": [
{
"index": "01",
"title": "Decision-making",
"items": [
{
"subIndex": "01.1",
"label": "Trade-offs",
"value": "Balances speed with a clear rollback plan."
}
]
}
]
},
"profile": {
"twin": "Maya Chen",
"proceedings": 1,
"findings": 8,
"points": 120,
"domainSummary": null,
"coreProfile": null,
"buckets": []
}
}/snapshots/{id}/rawRemove raw materialPreview removal without confirm, then permanently remove raw material with confirm: true and the dry run's expected_count. A stale count returns 409 PRECONDITION_FAILED. The snapshot counts and Mind Twin remain; no rebuild.
Path: id. JSON body: confirm, dry_run, expected_count, idempotency_key, target. dry_run: true overrides confirm. Confirmation requires the latest expected_count. The idempotency cache lasts 10 minutes per server instance; do not rely on it across retries to a different instance. Reconcile a lost response with a fresh dry run.
delete_raw removes one transcript from the Mind Twin's recall store and browse list. It does not delete the snapshot or Mind Twin, erase learned outputs, or trigger a rebuild. The public integration API has no Mind Twin deletion, snapshot deletion, or export endpoint.
All of these endpoints require the same Bearer key.
{
"dry_run": true,
"snapshot_id": "example-snapshot",
"found": true,
"would_delete": {
"mind_sync_rows": 1
},
"expected_count": 1,
"message": "This is a dry run. No data has been removed."
}/twinsCreate a Mind TwinCreate a private Mind Twin of someone you meet with. Only you can see or ask it; it starts empty and costs no credits. A Mind Twin you created stays only yours, even if that person later joins MindTwins.
name is required, 1 to 200 characters after trimming. email is optional, up to 320 characters; description is optional, up to 500. Include only details you know. idempotency_key is optional, 1 to 200 characters. REST also accepts Idempotency-Key; the body field wins when both are present. Reusing the same key with the same name, email, and description within 24 hours returns the first successful answer. MCP generates a key when you omit it. Creating is free and needs only twin:chat, with a separate limit of 10 per hour per person.
All of these endpoints require the same Bearer key.
{
"twin_id": "example-created-id",
"mind_id": "example-created-mind_id",
"name": "Jordan Lee",
"email": null,
"ownership": "created",
"ready": false,
"status": "needs_meeting",
"visible_to": "only_you"
}Authentication and boundaries
Every authenticated request is tied to one caller and one workspace. The request never supplies a workspace id.
twin:chatEveryoneEvery action on the key owner's Mind Twin, including snapshot browsing and delete_raw. Creating one, and every action on the Mind Twins you create.
twin:uploadMind Twin TeamsA teammate target requires twin:upload, team visibility in the same workspace, and permission under any target allowlist attached to the key. The directory can show members that this key cannot target. A directory entry is not proof of access; verify the intended target with GET /me/status before uploading. A Mind Twin you created needs no extra scope: twin:chat alone reaches it for chat, upload, status, snapshots, and delete_raw. Only you, its creator, can reach it, subject to the key's target allowlist.
Provisioned for you Keys are created in the app. There is no public create-key endpoint.
Keep it server-side. Never ship a dev_ key in browser or mobile code.
Rotate on exposure. Regenerate in Settings › Connectors. The old key stops working immediately.
Use the narrow path. Drop target when you only need the key owner's Mind Twin.
A valid key starts with dev_ and is 41 characters in total, including dev_.
Limits and timing
Direct REST chat can take up to 9 minutes; allow 10 minutes in your HTTP client and proxy. MCP has a separate 290-second downstream request budget and a 300-second hosting limit. A longer MCP client timeout cannot extend that server budget. After a timeout, the server may still be working; avoid an immediate retry loop.
| Key environment | Per minute | Per hour | Per day |
|---|---|---|---|
| Development | 60 | 1,000 | 10,000 |
| Production | 300 | 10,000 | 100,000 |
These request limits apply per key. A per-key 429 response does not include a Retry-After header; read the retryAfter ISO timestamp in its JSON body. If it is absent or invalid, use bounded exponential backoff with jitter. Do not immediately retry. Creating Mind Twins has a separate limit of 10 per hour per person; its 429 RATE_LIMITED response includes Retry-After in seconds. A 403 IP_BLOCKED response also uses Retry-After.
On a 429, X-RateLimit-Limit-Minute, X-RateLimit-Remaining-Minute, X-RateLimit-Limit-Hour, X-RateLimit-Remaining-Hour, X-RateLimit-Limit-Day, and X-RateLimit-Remaining-Day contain request counts. X-RateLimit-Reset-Minute is an ISO timestamp. Hour and day reset headers are not returned. Successful integration responses do not guarantee these headers. Use retryAfter in the body for the window that actually blocked the request.
In Settings › Connectors, create or reuse a separate key for each app to see its usage on its own row. REST chat can send X-MindTwins-App: <slug> to label the app that asked. The slug must match ^[a-z0-9][a-z0-9-]{0,39}$; invalid values are ignored, never rejected. This header is attribution only: it never changes authentication, scopes, the Mind Twin, the key, or the workspace. The hosted MCP server sends it for you when your key was set up for a specific app.
Recommended client strategy: check status every 15 seconds, then every 30 to 60 seconds for longer builds. These intervals are guidance, not server requirements. Build time varies; use status rather than a fixed delay. No completion webhooks are available.
MCP chat does not remember earlier messages. Include the relevant conversation context in each message.
Findings and Mindsets are learned outputs associated with a snapshot. Browse snapshots, then inspect a snapshot by its ID. The API does not provide a combined per-person Findings or Mindsets view.
Snapshot lists use snapshot_id, date, filename, status, and counts; counts can be null while processing, after failure, or when no engine snapshot is linked. A failed row is not learned. not_learned means the transcript was saved without being learned. In concise detail, summary and profile are null; they can also be null for an unlinked recall entry. Pass snapshot_id unchanged to detail or deletion, including an rc: prefix if present. next_cursor is opaque; stop when it is absent or null. In detail, ready: false means this snapshot is not ready to read. Findings appear in summary.categories and Mindsets in profile.buckets. MCP renders readable Markdown, limited to 9 finding categories, 6 items per category, and 12 mindset attributes; use REST detail for the structured sections.
delete_raw removes one transcript from the Mind Twin's recall store and browse list. It does not delete the snapshot or Mind Twin, erase learned outputs, or trigger a rebuild. The public integration API has no Mind Twin deletion, snapshot deletion, or export endpoint.
Errors and retries
Application errors return JSON with error; code and message are usually present. Check Content-Type first: hosting-layer errors can be plain text.
{
"error": "Missing or invalid Authorization header",
"message": "Provide a developer API key: Authorization: Bearer dev_xxx"
}MCP may refuse a call before REST runs: INVALID_SNAPSHOT_ID means the snapshot ID is invalid; copy it unchanged from list_snapshots. KEY_FORMAT means an invalid key shape; KEY_RECENTLY_REJECTED means the key was recently refused; KEY_CHECKS_PAUSED means new key checks are briefly paused. Fix the key or wait as directed instead of looping. OUTCOME_UNKNOWN means an upload or deletion may have completed despite a lost reply. For upload, preserve the exact payload. For deletion, run a fresh dry run without confirm to check whether the transcript remains. These are MCP-side conditions, not additional REST response codes.
| Status | Code | Meaning | Your next move |
|---|---|---|---|
400 | BAD_REQUEST | Invalid snapshot ID or deletion confirmation | Copy the ID from the list and use the latest dry-run expected_count. |
500 | none | Authentication or rate-limit service failed | Retry with bounded backoff; contact support if it persists. |
502 | UPSTREAM | Snapshot browse or raw deletion failed | Back off; reconcile a deletion with a fresh dry run before retrying confirmation. |
400 | none ("error": "Validation error") | Missing or invalid field | Fix the request; message or details identifies the invalid field. |
400 | INVALID_DATE | The server rejected the upload date | Send the real meeting date. |
400 | INVALID_TRANSCRIPT | Empty or unrecognized transcript | Send a supported format and match format to the content. |
400 | NO_ORG | The Mind Twin is not fully provisioned for uploads | Contact support to complete setup. |
400 | SELF_NOT_A_SPEAKER | The owner label does not match a speaker | Correct self_speaker (MCP) or selfSpeaker (REST); for VTT/text, include it in participants. Nothing was saved. |
400 | INVALID_CURSOR | The snapshot page cursor is invalid | Restart browsing without cursor. |
401 | none | Missing, malformed, or revoked key | Re-copy or regenerate the key. |
403 | none ("Insufficient scope") or INSUFFICIENT_SCOPE | The key lacks the scope. For target resolution, this now applies only when your own partial name or @handle matches a teammate more strongly. | Use your full name or email, or omit target for your own Mind Twin. |
403 | NO_CALLER | The key is not tied to a user | Check the key owner in the app. |
403 | IP_BLOCKED | Too many failed key attempts from your IP | Wait for the Retry-After header (seconds), then fix the key. |
404 | NOT_FOUND | No Mind Twin for your account in this workspace | Set one up in the MindTwins app. |
404 | NO_TWIN | GET /me found no Mind Twin for your account | Set one up in the MindTwins app. |
404 | TARGET_NOT_FOUND | target matches no Mind Twin you can reach. The same response covers nobody, a teammate on a chat-only key, someone else's private Mind Twin, or another workspace. | Check the exact identifier and key permissions. Use GET /twins as a directory, then verify access. |
404 | TARGET_NEEDS_FULL_NAME | An upload or raw deletion used a partial name | Use the full name, email, @handle, or twin_id. Nothing was saved or deleted. |
409 | NEEDS_MEETING | A Mind Twin you created has not been given a meeting | Upload a meeting the person speaks in, then check status. |
409 | STILL_LEARNING | The first meeting or core is still being learned | Wait a few minutes and check again. This is not a fatal failure. |
422 | TRANSCRIPT_FAILED_BEFORE | This transcript already failed twice | Use a different conversation instead of uploading it again. |
404 | MIND_NOT_FOUND | Upload target has no mind to feed yet | Set it up in the app first. |
409 | ALREADY_PROCESSING | Same transcript is already processing | Nothing to do. Do not upload it again. |
409 | BUILD_IN_PROGRESS | Mind Twin is busy with another build | Retry after it finishes. |
409 | ORPHANED_ARTIFACT | A previous upload did not finish building; this upload was not saved | Retry the same upload; contact support if it repeats. |
409 | PRECONDITION_FAILED | Raw deletion count changed since the dry run | Run the dry run again before confirming. |
413 | CONTENT_TOO_LARGE / FUNCTION_PAYLOAD_TOO_LARGE | Message, transcript, or encoded request body too large | Chat: up to 800,000 characters. Keep the complete upload request under the recommended 4,000,000-byte budget. Hosting-layer errors may be plain text. |
422 | AMBIGUOUS_TARGET | Name matches more than one person | Use their email. |
422 | LOW_QUALITY_CONTENT | Not enough meaningful conversation to learn from | Use a longer, substantive transcript. Aim for a few hundred words; this is guidance, not a fixed minimum. |
423 | TWIN_PAUSED | The owner paused the Mind Twin | Resume your Mind Twin in the app, or wait for the teammate to resume theirs. |
429 | none | Rate limit | Wait until the retryAfter body field (an ISO timestamp), then back off. |
502 / 503 / 504 | INTERNAL / UNAVAILABLE / TIMEOUT | Transient service or upstream failure | Back off for read requests. For an upload or deletion with a lost response, reconcile the outcome before retrying. |
Run the copied install command again, confirm the Bearer key is present, then restart the MCP client.
That is expected for a fresh Mind Twin. In MCP, a blank or zero learned-transcript count is not a live meter. Check periodically. A Mind Twin with nothing uploaded returns 200 building with progress 0 and needs_upload: true. Investigate failed builds and requests that exceed your integration's waiting budget.
A Mind Twin with nothing uploaded returns 200 building, progress 0, and needs_upload: true. Upload a transcript to start building. A 404 NOT_FOUND means this account has no Mind Twin in the workspace; set one up in the MindTwins app.
Keep chatting. The Mind Twin remains ready while the new transcript lands; recall of it sharpens shortly.
Ask for the real meeting date. Never guess or default to today. Confirm each transcript separately.
For a retry, resend the same transcript, exact date string, title, format, target, source, duration, and participants in the same order. Changing meeting details can save a second copy. Deduplication compares generated transcript content within the selected Mind Twin. A completed duplicate returns 200; queued or processing duplicates can also return 200 in workspaces with a build queue. ALREADY_PROCESSING means the same transcript is in progress: check status instead of uploading again. BUILD_IN_PROGRESS means this upload was not saved: wait for the current build, then retry. If an upload reply is lost, check status first and preserve the original payload for any retry.
409 STILL_LEARNING means its first meeting is being learned or its core is still forming. Wait a few minutes and check again; do not treat it as fatal.
409 NEEDS_MEETING means a Mind Twin you created has no meeting yet. Upload a meeting the person speaks in, then check status.
Read latest_failed_upload.reason when present and use the failure-reason table. The field or reason can be absent when unavailable; do not invent a diagnosis.
422 TRANSCRIPT_FAILED_BEFORE means this transcript failed twice. Use a different conversation.
A valid provisioned key starts with dev_ and is 41 characters in total, including dev_. Re-copy it from Settings › Connectors.
Documentation
Open the Markdown reference or download OpenAPI. Copy docs uses the same content.
Use mcp.mindtwins.com/mcp for MCP clients and app.mindtwins.com/api/twin-mcp for REST requests. These are different transports for the same Mind Twin integration; keep the hostname and path together.
Documentation revision: . This date versions the guide, not the API. REST paths are currently unversioned.
One key. One workspace.
Open Settings › Connectors, copy your provisioned key, and choose the path your software needs.