---
title: "API &amp; MCP integrations — InCharacter"
canonical_url: "https://www.incharacter.io/docs/integrations"
last_updated: "2026-10-05T18:35:52.829Z"
meta:
  description: "InCharacter integration reference: authenticated V2 conversations, explicit membership, read-only MCP, character exports and provisioned world APIs."
  "og:description": "InCharacter integration reference: authenticated V2 conversations, explicit membership, read-only MCP, character exports and provisioned world APIs."
  "og:title": "API & MCP integrations — InCharacter"
---

Developer documentation

# **API & MCP integrations**

Connect to an InCharacter conversation, read a character through MCP, or carry a compiled character into your own application.

This page describes the current interfaces and their access requirements. Hosted conversation is in early access; broad launch qualification is still in progress. An implemented endpoint is not a promise of public access or a production SLA.

## Choose an integration Have a conversationAuthenticated REST · V2 hosted turns · current Studio path`POST /api/conversations/turn` Read a character with MCPPublic read-only identity tools · JSON-RPC over HTTP`POST /api/mcp/:identityId` Create or export a characterStudio account · ownership checks · downloadable package`GET /api/characters/:id/export` Connect a worldSeparately provisioned server integration · exact world scope`POST /api/sim/conversation-membership` The REST examples below use the InCharacter application origin, `https://www.incharacter.io`. All IDs in examples are placeholders to replace with your own authorized records. ## Authentication and access The conversation and Studio APIs use your signed-in InCharacter session cookies. Start in [Sign in](https://www.incharacter.io/login), then use your own compiled character from the [Library](https://www.incharacter.io/studio). Conversation and membership also require early access. The server derives the human participant from the verified session and checks character ownership. Browser conversation and membership writes require same-origin JSON. A third-party website cannot call these routes using permissive CORS. These routes do not implement a generic API-key or bearer-token login, delegated OAuth integration, or a public server-to-server conversation credential. The public MCP relay has no caller credential and exposes read-only tools. Separately provisioned world and editorial servers use their own credentials. Those credentials belong on the authorized server; they are not customer API keys and must never be placed in a browser, mobile app or shared MCP configuration. To build an external hosted-chat product, arrange the integration and authentication boundary with InCharacter first. A character URL, provider key, subscription or supplied owner ID does not grant that access. ## Hosted conversation`POST /api/conversations/turn` is the authenticated product endpoint used by Studio. It invokes the existing V2 turn engine. It requires a V2 character with ready runtime dependencies and does not fall back to a legacy character or demo handler. ### Request Send exactly four fields: UUIDs for `conversation_ref`, `character_identity_id` and `client_message_id`, plus nonblank `text` of 1–4,000 characters. Unknown fields reject. Keep the body within 32 KiB. Model, history, state, owner and runtime selectors are not client inputs. Same-origin JavaScript · one message, explicit resume```
// Run only in your authenticated InCharacter-origin application.
// Pick an owned, compiled identity_id from GET /api/characters.
const characterIdentityId = "YOUR_CHARACTER_IDENTITY_UUID";
const conversationRef = crypto.randomUUID(); // keep for this conversation
const pending = {
  conversation_ref: conversationRef,
  character_identity_id: characterIdentityId,
  client_message_id: crypto.randomUUID(), // create once for this message
  text: "Hello. What would you like to talk about?"
};
// Persist pending BEFORE sending. Keep all four fields unchanged on resume.
async function submitSameMessage() {
  const response = await fetch("/api/conversations/turn", {
    method: "POST",
    credentials: "same-origin",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(pending)
  });
  if (!response.ok) {
    throw new Error(\`HTTP ${response.status}; keep pending for review/resume\`);
  }
  return response.json();
}
const result = await submitSameMessage();
// Inspect status below. Do not automatically retry or generate a replacement ID.
``` Use one conversation UUID for the conversation and one new message UUID for each new message. Persist the full request before sending. A timeout, lost connection or pending result must be resumed with that exact request, including unchanged text. Do not send concurrent messages for the same conversation; resolve the current message before advancing. ### Response and completion The JSON response contains only `status` and `reply`. Spoken or silent behavior is returned after the coordinated turn has committed. Private state, internal prompts, provider payloads and operational receipts are not returned.`REPLIED` A committed spoken reply. Display reply.text and optionally the observer-facing nonverbal cue.`SILENT` A committed silence. reply.kind is silence; there is no text field. An optional nonverbal cue can be displayed. Silence does not end membership.`NO_REPLY` Terminal outcome with reply: null. Do not invent a response or silently resubmit this message as a new turn.`PENDING` The outcome is not yet confirmed. Keep the exact request and resume it to check completion; do not advance the conversation.`PREPARATION_PENDING` Preparation is not ready. Keep the exact request and resume after the prerequisite is resolved; do not create a new message ID. Illustrative committed reply```
{
  "status": "REPLIED",
  "reply": { "kind": "speak", "text": "Let's start with your day.", "nonverbal": null }
}
``` Illustrative committed silence```
{
  "status": "SILENT",
  "reply": { "kind": "silence", "nonverbal": null }
}
````nonverbal` is a nullable, observer-facing display cue, not a measurement of private affect. Render all returned text as text, not executable HTML. ### Waiting, limits and errors The endpoint returns a completed JSON response; token streaming, SSE and WebSocket conversation responses are not implemented. Preparation, provider work and finalization contribute to the wait. There is no guaranteed response-time SLA or public phase-timing field. The current turn limit is 20 requests per account per 60 seconds. It is an in-memory, per-process limit, not a distributed quota. Resume requests also count. Use deliberate, bounded resume rather than a tight polling loop.<dl><dt>`401`</dt><dd>The user session is absent or invalid. Sign in again; keep an uncertain message's original IDs.</dd><dt>`403`</dt><dd>Early access is missing, or a browser write is not same-origin. A provider key or server token is not a substitute.</dd><dt>`404`</dt><dd>The selected character is not available to this account.</dd><dt>`413 / 415 / 422`</dt><dd>Check request size, application/json and the exact request fields. Fix validation before a new submission.</dd><dt>`429`</dt><dd>Wait before resuming. Turn and membership budgets are separate; neither endpoint supplies a guaranteed Retry-After interval.</dd><dt>`502`</dt><dd>The turn could not be confirmed. Work may already exist. Resume the exact same body; do not switch endpoint or generate a replacement ID.</dd><dt>`503`</dt><dd>Configuration or access verification is unavailable. Preserve the request and resolve availability before resuming.</dd></dl>An HTTP 200 with a pending status is not a committed reply. There is no separate public turn-status GET route; checking a pending turn uses the same POST body. Conversation results use `Cache-Control: private, no-store`. ## Explicit conversation membership`POST /api/conversations/membership` uses the same session, early-access and same-origin JSON rules. It supports `ensure`, `read`, `join` and `leave`. A normal first hosted turn establishes its initial membership; a separate ensure call is optional. Current IC-hosted chat uses `one_to_one`: a new turn requires exactly the authenticated human and selected owned character. Reusing a conversation reference with a non-member never silently adds that character. After an explicit leave the roster can be incomplete, but it cannot accept a new turn until the required pair is present. Membership request shapes · replace IDs and use the actual current revision```
// Each object is a separate POST /api/conversations/membership body.
// Use the same authenticated session and JSON headers as the turn request.
{
  "action": "ensure",
  "conversation_ref": "YOUR_CONVERSATION_UUID",
  "character_identity_id": "YOUR_CHARACTER_IDENTITY_UUID"
}
// From the response, retain scope_id and revision.
{
  "action": "read",
  "scope_id": "RETURNED_SCOPE_UUID",
  "revision": null
}
// Explicit change: retain this exact body for idempotent resume.
{
  "action": "leave",
  "scope_id": "RETURNED_SCOPE_UUID",
  "operation_ref": "YOUR_UNIQUE_OPERATION_REFERENCE",
  "expected_revision": 1,
  "participant": { "kind": "character", "identity_id": "YOUR_CHARACTER_IDENTITY_UUID" }
}
// To join, use action: "join", a new operation_ref and the current revision.
// To target the authenticated human, participant is { "kind": "person" }.
``` A response contains `scope_id`, `conversation_ref`, `mode`, `revision` and `participants`. A participant includes `participant_ref`, `kind` and nullable `identity_id`. Read with `revision: null` for current membership, or a positive revision number for an immutable historical snapshot. Join and leave require a unique `operation_ref` (1–256 characters) and the current positive `expected_revision`. The server applies changes atomically. Retrying the exact operation returns its original result; reusing its reference for different intent conflicts. Joining an existing member or leaving an absent member can be a recorded no-op. Every begun turn and source retain their original membership revision and runtime binding. Later membership changes affect subsequent turns. Membership itself grants neither knowledge of earlier events nor proof that a participant perceived them. A conflicting or stale operation returns 409: read current membership, review the intent, then use a new operation reference for a new decision. Do not rewrite an uncertain existing operation. Membership has a separate per-process limit of 60 requests per account per 60 seconds and a 64 KiB body limit. Bodies are strict JSON; unknown fields reject. ## Read-only MCP The InCharacter relay accepts JSON-RPC POST requests at `/api/mcp/:identityId` and the alias `/api/mcp/proxy/:identityId`. The configured MCP host also maps `mcp.incharacter.io/:identityId` to the same relay. Prefer the main-site path when integrating directly with IC. The per-character **Carry out** page can also return an upstream Aurora MCP URL. Use the returned URL as-is; that is a distinct upstream connection, not the IC conversation endpoint. The Carry out URL display is subscription-gated, while the IC read-only relay itself does not authenticate callers or enforce that subscription gate. Discover tools · replace the identity UUID```
curl https://www.incharacter.io/api/mcp/YOUR_CHARACTER_IDENTITY_UUID \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  --data '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
``` A normal MCP client starts with `initialize`, followed by `notifications/initialized`, and discovers the deployed tool schemas with `tools/list`. The IC relay also allows `ping` and `resources/list`; resources are returned empty. - `get_latest_core` returns version and Core identifiers, not the complete character payload. - `get_core` retrieves the allowed identity snapshot. Use the argument schema returned by `tools/list`. - `list_expressions` lists available expression records. - `resolve_expression` resolves an allowed expression; raw output is rejected. JSON-RPC tool call · get_latest_core```
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": { "name": "get_latest_core", "arguments": {} }
}
``` Discovery lists only the allowed tools that the upstream deployment actually exposes. The relay checks requests and projects responses. Denied methods/tools return 403; an invalid or unsafe upstream response returns 502. Check JSON-RPC `error` and tool `isError` as well as HTTP status. This relay does not generate hosted replies, commit conversation memory, run state changes or expose a `chat_as_identity` tool/prompt. It does not allow `prompts/get`. Use the authenticated conversation route for the current V2 product flow. Do not assume a public character URL is a secret or grants private runtime access. ## Characters, creation and exports The simplest onboarding is [New character](https://www.incharacter.io/studio/new) → compile → open the character → Conversation. Use Carry out for export and connection details. The APIs below are the session-authenticated Studio interfaces; they do not add external bearer authentication. Character and export endpoints | **Endpoint** | **Access and purpose** |
| --- | --- | | GET`/api/characters` | Session List your characters. Use identity_id for conversation requests; id is the ownership record, and aurora_session_id is the interview. | | GET`/api/characters/:id/core` | Session + ownership Review the compiled character. Accepts identity or interview ID; this authoring view is not the conversation response. | | GET`/api/characters/:id/resume` | Session + ownership Resume character creation. Here :id is the ownership record ID. Returns seed, interview, compiling, done or expired phase. | | GET`/api/characters/:id/memories` | Session + ownership Read your character's own readable memories and eligible inherited memories. This is not a hosted chat transcript endpoint. | | GET`/api/characters/:id/deploy` | Session + ownership Read Carry out details. Returns exportPath; mcpUrl is returned only when subscribed (otherwise locked: true). An uncompiled character returns 409. | | GET`/api/characters/:id/export` | Session + ownership Download an application/zip package for identity_id, using its recorded live version or latest if absent. No subscription gate in this handler. | | POST`/api/characters/:id/fork` | Session + ownership Request a divergent character: { name?, inherit_memory? }. The upstream fork feature must be enabled. This creates data; a failed multi-service operation can need reconciliation. | | DELETE`/api/characters/:id` | Session + ownership For a compiled character, schedule Aurora identity and descendant-fork deletion after 48 hours; the character stays frozen and visible until purge or cancellation. An uncompiled draft is removed immediately from your library. Accepts ownership, identity or interview ID. Not a connectivity check. | | GET`/api/characters/:id/deletion` | Session + ownership Read deletion status and the owning root. A draft has no scheduled Aurora deletion. Unavailable status is not proof that a character is active. | | GET`/api/characters/:id/deletion/preview` | Session + ownership Preview the compiled identity, descendant forks and affected data before requesting deletion. | | POST`/api/characters/:id/deletion/cancel` | Session + ownership Cancel on the root while its deletion is still scheduled. A marked descendant cannot cancel separately; once purging starts it cannot be cancelled. | | POST`/api/characters/:id/report` | Session Submit a moderation report: { reason, note? }. reason is 1–80 characters; note is at most 2,000. Ownership is not required to report. | ### Portable export The export endpoint returns a ZIP containing identity material and a manifest, including `core.json` and `identity.md`. Treat it as a character package, not a copy of hosted conversation history or a runnable V2 service. Exporting a file does not provision an Aurora instance or reproduce the hosted authority, membership, memory and finalization workflow in another model. The export handler uses the recorded `live_version`, falling back to `latest` only when absent. The hosted conversation independently resolves its authorized runtime Core. Do not assume those selectors always identify the same version.<details><summary>Character interview API</summary>

Character interview endpoints | **Endpoint** | **Access and purpose** |
| --- | --- | | POST`/api/characters/interview/start` | Session + early access Start an interview and create an ownership record. Required: offeringSummary (10–600 characters), antiIdentityTags (1–10 items), moodTags (1–3 items). Optional: name, audienceDescription, antiIdentityNote, mbti, language, audienceRelationshipType (peers or public). | | POST`/api/characters/interview/turn` | Session + early access + ownership Answer an interview question: { sessionId, answer }. answer is 1–2,000 characters after trimming. This is character creation, not runtime conversation. | | POST`/api/characters/interview/finalize` | Session + early access + ownership Submit { sessionId } after the interview completes. Finalizes and starts asynchronous synthesis. Do not blindly repeat finalize. | | POST`/api/characters/interview/process` | Session + early access + ownership Submit { sessionId } to re-enqueue synthesis for an already-finalized interview; this is the separate compile retry operation. | | GET`/api/characters/interview/status?sessionId=:sessionId` | Session + ownership Read { ready, interview, core }. A ready result can update the ownership record. Compilation readiness is separate from hosted runtime readiness. | Start returns the interview's session ID. Use the returned question/options for each interview turn, finalize when complete, then read status for the resulting identity ID. This creates data and can invoke synthesis. Do not use creation or compile retries as health checks.</details>

<details><summary>Account, billing and public content endpoints</summary>

Supporting product endpoints | **Endpoint** | **Access and purpose** |
| --- | --- | | GET`/api/me` | Session Read the current account and early_access flag. May reconcile access or record the account on the waitlist; not a strictly non-writing health probe. | | GET`/api/billing/status` | Session Read subscription details for the signed-in account. | | POST`/api/billing/checkout` | Session Start the product's subscription checkout. Use the Studio billing flow for plan selection. | | POST`/api/billing/portal` | Session Open the signed-in customer's billing portal. | | GET / POST / DELETE`/api/settings/ai/keys` | Session Manage the account's stored provider keys in Settings. These are not InCharacter access tokens. Hosted V2 conversation uses its server-selected provider and does not accept a client key or model override. | | POST`/api/waitlist` | Public Request early access through the site's waitlist form. This is onboarding, not a conversation credential. | | GET`/api/blog` | Public List published posts. | | GET`/api/blog/:slug` | Public Read a published post by slug. Editorial MCP is a separate operator-only surface. | These support the product account and onboarding experience. Subscription checkout, settings changes and waitlist submission have their own side effects; they are not runtime integration probes.</details>

## Provisioned world integrations NerveTown and other explicitly provisioned hosts have a separate server integration surface. Availability requires a configured server credential, exact owner/world/run bindings, participant-to-person joins and the relevant runtime enrollment. There is no public token issuance or self-service world registration endpoint in IC today. NerveTown decides membership, join/leave timing, speaker selection and world sequencing. IC records and enforces versioned membership and validates the host's requested speaker through the existing source/execution adapter. It does not choose the next speaker or implement a second group orchestrator. Provisioned world integration endpoints | **Endpoint** | **Access and purpose** |
| --- | --- | | POST`/api/sim/conversation-membership` | Provisioned world server Ensure/read/join/leave membership; begin/read a frozen turn and bind an existing world runtime. The host supplies the requested actor and speaker; IC validates exact world/person bindings. | | POST`/api/sim/typed-appraisal-source` | Provisioned world server Admit the existing world-owned source against the frozen membership and enrolled speaker. This does not create an arbitrary public group-chat endpoint. | | POST`/api/episodes/readable` | Provisioned world server Store source-backed readable continuity using a stable externalEpisodeRef. Memory feature gates apply; a successful HTTP response may report stored: false. | | POST`/api/episodes/recall` | Provisioned world server Recall readable memories using identityId, queryEmbedding and optional n, asOf, runId. Preserve world/run scope; do not infer access from a character name. | | POST`/api/episodes/ingest` | Provisioned world server Scoped episode pressure ingestion, separate from readable memory and hosted replies. Requires stable externalEpisodeRef; typed live sources are excluded from batch folding. | | POST`/api/fork-ownership` | Provisioned world server Record an existing fork's IC ownership from the origin's owner. This does not create a character or grant a public token. | | POST`/api/adopt-ownership` | Provisioned operator Reconcile ownership for an existing identity created outside IC. A controlled repair operation, not a self-service claim-by-ID API. | ### World membership contract The world membership route uses `world_hosted` mode. Ensure supplies `owner_id`, a host-local `conversation_ref`, a `world_scope` with `deployment_ref`, `nt_world_id`, `ic_world_id` and `run_id`, plus explicit participant references. The server resolves canonical identities from existing exact bindings; the host cannot invent them in the roster. Besides ensure/read/join/leave, the route accepts `begin`, `read_turn` and `bind_runtime`. Begin freezes the requested actor, speaker, text and membership revision; runtime binding names an existing `world_instance_id`. Use the agreed source-adapter contract for turn references and enrollment. This is not a multi-participant mode flag for `/api/conversations/turn`.<details><summary>Operational surface directory — provisioned use only</summary>

Most world operations authenticate a server-held shared credential and then enforce their own scope and readiness checks. Producer, archive and development routes have additional or distinct gates. This directory identifies existing operations; it is not authorization to call them or a generic public API contract. Obtain the matching versioned payload contract during integration setup. Internal and provisioned operational endpoints | **Endpoint** | **Access and purpose** |
| --- | --- | | GET`/api/sim/authority-preflight` | World server Read capability checks for a provisioned seeding workflow. | | POST`/api/sim/seed · /validate · /generate-day · /encode-event · /day-close · /prime-worldview` | World server Configured simulation/seeding lifecycle; each abbreviated path is under /api/sim/. These can generate content or write domain data. | | GET / POST`/api/sim/intentions` | World server Read/record world intentions; POST /api/sim/intentions-resolve resolves them. | | GET`/api/sim/events` | World server Read simulation events for the provisioned workflow. | | POST`/api/sim/structural-referral · /typed-appraisal-barrier · /typed-appraisal-recover · /runtime-recovery` | World server + exact runtime scope Source, synchronization and recovery operations under /api/sim/. Not direct public state writers. | | POST`/api/sim/private-manifestation · /affect-tick` | World server + runtime guards Configured internal runtime consumers under /api/sim/. Private material is not a player-facing integration response. | | POST`/api/affect/drain · /api/affect/render` | World server Internal affect consumption and rendering adapters; not substitutes for hosted turn admission. | | POST`/api/sim/current-perception` | Separately configured producer Accept a bounded notice receipt from its configured producer. Membership alone does not prove perception. | | POST`/api/sim/native-applicability-read` | Configured development worker Worker-scoped native applicability read; availability is gated and not a general integration API. | | POST`/api/sim/archived-run · /archived-operational-inference · /archived-frozen-history` | World/archive operator Archive-oriented operations under /api/sim/, with route-specific authorization. Not the current hosted chat path. | | POST`/api/sim/execute-murder · /teardown` | Simulation operator Scenario-specific and destructive simulation operations under /api/sim/. Excluded from general onboarding. | | POST`/api/mcp/blog` | Editorial operator JSON-RPC Blog MCP: blog_list, blog_get, blog_create, blog_update, blog_publish, blog_unpublish, blog_delete. Uses a separate server-held editorial credential; no character runtime access. |</details>

Billing webhooks, debug endpoints, development/replay harnesses and source-specific candidate routes are internal operations. They are not supported plug-and-play integrations. The editorial Blog MCP is independent of character MCP and hosted conversations. ## Current availability and limits - The hosted product path is V2-only and one human plus one character. A committed reply is the success boundary; an HTTP connection alone does not prove runtime readiness. - No public hosted-turn MCP tool, standalone client SDK, embed widget, outbound conversation webhook, token stream or transcript-hydration API is implemented in this IC release. - External app authentication and broader launch/access-control qualification remain separate work. The current per-process limiter is not a distributed production quota. - Historical `/api/demo/*`, `/api/characters/:id/test` and `/api/direct-single-call-dev` return 410. They are not fallback paths; Studio uses `/api/conversations/turn`. For a new integration, start by choosing whether you need hosted conversation, read-only identity access, a portable export or an explicitly provisioned world. Use [Studio Docs](https://www.incharacter.io/studio/docs) with your account, or [request early access](https://www.incharacter.io/#waitlist) to get started.