Skip to content

Twin Brain

Read-model endpoints for the AI Twin Brain Simulator: your twin's neural map (neurons, synapses, regions), its wiki-style memory graph, live activity, and an optional wallet-billed AI narration.

All graph data is computed server-side from what your twin has already learned. These endpoints never modify the twin.

Endpoints

MethodPathDescriptionAuth
GET/agents/twins/resolveResolve a subject to its twinJWT + Entitlement
GET/agents/twins/{twinId}/brain/graphBrain graph (nodes, edges, clusters, overlay)JWT + Entitlement
GET/agents/twins/{twinId}/brain/memory-graphWiki-style memory graphJWT + Entitlement
GET/agents/twins/{twinId}/brain/memories/{memoryId}Memory detail with provenance and evidenceJWT + Entitlement
GET/agents/twins/{twinId}/brain/activityLive activity levels for the brainJWT + Entitlement
GET/agents/twins/{twinId}/brain/chain-impact/{chainId}How a chain has shaped the brainJWT + Entitlement
POST/agents/twins/{twinId}/brain/sampleConsume the one-time free brain previewJWT + Entitlement
GET/agents/twins/{twinId}/brain/sample-statusWhether the one-time preview is still availableJWT + Entitlement
POST/agents/twins/{twinId}/brain/narrateAI narration of a brain region (wallet-billed)JWT + Entitlement
GET/public/twins/{username}/brainPublic brain showcase (opt-in only)None (30/min)

Plan access

FeatureEndpointsFreeBitterChainerArchitect
Brain Simulator (ai.twin.brain_simulator)resolve, graph, activity, chain-impact
Memory Graph (ai.twin.memory_graph)memory-graph, memories/
One-time preview (ai.twin.brain_sample)sample, sample-status✓ (once)
AI Narration (ai.twin.brain_narrate)narrate5/day25/day100/day

Calling an endpoint your plan does not include returns 403 with a message naming the missing feature key. See the feature guide for the full plan matrix.


GET /agents/twins/resolve

Description

Resolves a subject (you, a workspace, a team, or an agent) to its twin so you can call the brain endpoints. Twins are system-managed — this is how you discover the twinId.

Request

  • Headers: Authorization: Bearer <token>
  • Query params:
ParameterTypeRequiredDescription
subjectTypestringYesOne of chainer, workspace, team, agent
subjectIdstring (max 64)YesEntity id, or me when subjectType=chainer

You can only resolve your own personal twin, and twins that belong to your account (workspace twins additionally require membership in that workspace). Anything else returns 404.

Response

  • 200 OK
    json
    {
      "data": {
        "twinId": "3f7c2a90-5b1e-4d2a-8c4e-1a9d6b3f5e72",
        "name": "My Twin",
        "scopeKind": "chainer",
        "lifecycleState": "active"
      }
    }

Code Example

bash
curl "https://api.chainabit.com/api/v1/agents/twins/resolve?subjectType=chainer&subjectId=me" \
  -H "Authorization: Bearer $TOKEN"

GET /agents/twins/{twinId}/brain/graph

Description

Returns the strongest window of your twin's brain graph: neurons (nodes), synapses (edges), kind clusters, and optionally a productivity overlay that maps your chainies to regions and your chains to synapses. The strongest connections are returned first; truncated tells you when the full brain is larger than the returned window.

Request

  • Headers: Authorization: Bearer <token>
  • Path params: twinId (UUID)
  • Query params:
ParameterTypeRequiredDescription
maxNodesintegerNoNode budget. Default 150, maximum 300 (higher values are clamped)
kindsstring (CSV)NoNode kinds to include: topic, person, tool, trait, memory — e.g. kinds=topic,tool
overlaystringNoproductivity to include the regions/synapses overlay

Response

  • 200 OK
    json
    {
      "data": {
        "nodes": [
          { "id": "n-typescript", "kind": "topic", "label": "TypeScript", "weight": 4.6, "degree": 2, "lastSeen": "2026-06-10T09:12:41.000Z" },
          { "id": "n-testing", "kind": "topic", "label": "Testing", "weight": 2.9, "degree": 1, "lastSeen": "2026-06-09T18:02:10.000Z" },
          { "id": "n-deep-focus", "kind": "trait", "label": "Deep focus", "weight": 1.7, "degree": 1, "lastSeen": "2026-06-08T07:45:00.000Z" }
        ],
        "edges": [
          { "src": "n-typescript", "dst": "n-testing", "weight": 2.9, "releaseProb": 0.74, "sourceCount": 12 },
          { "src": "n-typescript", "dst": "n-deep-focus", "weight": 1.7, "releaseProb": 0.52, "sourceCount": 5 }
        ],
        "clusters": [
          { "kind": "topic", "label": "topic", "nodeCount": 2 },
          { "kind": "trait", "label": "trait", "nodeCount": 1 }
        ],
        "stats": { "nodeCount": 3, "edgeCount": 2, "totalNodes": 184, "totalEdges": 512 },
        "overlay": {
          "regions": [
            { "chainyId": "5c8e1f2a-9b4d-4c6e-8a3f-7d2b6e9c1a45", "title": "Product Launch", "colorHex": "#7C5CFF", "bitCount": 42, "completedBitCount": 31 }
          ],
          "synapses": [
            { "chainId": "9a1d4f6b-3c2e-4b7a-8d5c-6e9f2b1a7c34", "title": "Daily writing", "colorHex": "#19B26B", "protocolType": "daily", "currentStreak": 9, "totalCompletions": 124, "signedPeriodCount": 87, "chainyIds": ["5c8e1f2a-9b4d-4c6e-8a3f-7d2b6e9c1a45"] }
          ]
        },
        "truncated": true,
        "generatedAt": "2026-06-10T09:30:00.000Z"
      }
    }

weight and degree are computed from the returned edges, so they describe the window you received. overlay is present only when requested.

Code Example

Use data.twinId from GET /agents/twins/resolve as $TWIN_ID.

bash
curl "https://api.chainabit.com/api/v1/agents/twins/$TWIN_ID/brain/graph?maxNodes=150&kinds=topic,tool,trait&overlay=productivity" \
  -H "Authorization: Bearer $TOKEN"

GET /agents/twins/{twinId}/brain/memory-graph

Description

Returns the wiki-style memory map: the most relevant memories as nodes, consolidation links between them, and tag clusters for grouping. Memory content is excerpted to 240 characters — use the memory detail endpoint for the full entry.

Request

  • Headers: Authorization: Bearer <token>
  • Path params: twinId (UUID)
  • Query params:
ParameterTypeRequiredDescription
maxNodesintegerNoMemory budget. Default 100, maximum 200 (higher values are clamped)

Response

  • 200 OK
    json
    {
      "data": {
        "nodes": [
          {
            "id": "b2e6c1d4-7a3f-4e8b-9c5d-2f1a8e6b4c90",
            "memoryType": "preference",
            "excerpt": "Prefers concise bullet-point summaries over long prose.",
            "importance": 8,
            "relevanceScore": 0.91,
            "tags": ["writing", "style"],
            "isConsolidated": false,
            "flaggedWrongCount": 0,
            "createdAt": "2026-05-28T10:15:00.000Z"
          },
          {
            "id": "d4f8a2b6-1c5e-4a9d-8b3f-6e2c9a5d7b18",
            "memoryType": "preference",
            "excerpt": "Likes short summaries.",
            "importance": 5,
            "relevanceScore": 0.62,
            "tags": ["writing"],
            "isConsolidated": true,
            "flaggedWrongCount": 0,
            "createdAt": "2026-04-12T08:00:00.000Z"
          }
        ],
        "consolidationEdges": [
          { "fromId": "d4f8a2b6-1c5e-4a9d-8b3f-6e2c9a5d7b18", "toId": "b2e6c1d4-7a3f-4e8b-9c5d-2f1a8e6b4c90" }
        ],
        "tagClusters": [
          { "tag": "writing", "memoryIds": ["b2e6c1d4-7a3f-4e8b-9c5d-2f1a8e6b4c90", "d4f8a2b6-1c5e-4a9d-8b3f-6e2c9a5d7b18"] },
          { "tag": "style", "memoryIds": ["b2e6c1d4-7a3f-4e8b-9c5d-2f1a8e6b4c90"] }
        ],
        "stats": { "nodeCount": 2, "consolidationEdgeCount": 1, "tagCount": 2, "totalMemories": 57 },
        "truncated": true,
        "generatedAt": "2026-06-10T09:30:00.000Z"
      }
    }

tagClusters are groupings, not pairwise edges — render them as hulls or clusters around the listed memories.

Code Example

bash
curl "https://api.chainabit.com/api/v1/agents/twins/$TWIN_ID/brain/memory-graph?maxNodes=100" \
  -H "Authorization: Bearer $TOKEN"

GET /agents/twins/{twinId}/brain/memories/

Description

Returns one memory in full — the wiki page for that memory: complete content, provenance (where it came from), consolidation relationships, and recent behavioural evidence recorded for the twin.

Request

  • Headers: Authorization: Bearer <token>
  • Path params: twinId (UUID), memoryId (UUID)

Response

  • 200 OK
    json
    {
      "data": {
        "id": "b2e6c1d4-7a3f-4e8b-9c5d-2f1a8e6b4c90",
        "memoryType": "preference",
        "content": "Prefers concise bullet-point summaries over long prose, especially in status updates.",
        "importance": 8,
        "relevanceScore": 0.91,
        "tags": ["writing", "style"],
        "isConsolidated": false,
        "flaggedWrongCount": 0,
        "createdAt": "2026-05-28T10:15:00.000Z",
        "source": "session",
        "sourceId": "a7c3e9f1-2b6d-4e8a-9c4f-5d1b8e3a6c72",
        "consolidatedInto": null,
        "consolidatedChildren": [
          { "id": "d4f8a2b6-1c5e-4a9d-8b3f-6e2c9a5d7b18", "excerpt": "Likes short summaries." }
        ],
        "evidence": [
          { "type": "chain_completed", "claim": "Keeps a daily writing habit", "confidence": 0.84, "recordedAt": "2026-06-09T20:00:00.000Z" }
        ]
      }
    }

To remove or correct a memory, use the forget and flag endpoints in the Twins API.

Code Example

Use a memory id from GET /agents/twins/{twinId}/brain/memory-graph's data.nodes[] as $MEMORY_ID.

bash
curl "https://api.chainabit.com/api/v1/agents/twins/$TWIN_ID/brain/memories/$MEMORY_ID" \
  -H "Authorization: Bearer $TOKEN"

GET /agents/twins/{twinId}/brain/activity

Description

Returns live activity for the brain over a recent window — this is what makes the brain "shine" while your twin produces. Activation levels are 0..1: a node or chain that was just active is near 1, one at the edge of the window is near 0.

Request

  • Headers: Authorization: Bearer <token>
  • Path params: twinId (UUID)
  • Query params:
ParameterTypeRequiredDescription
windowMinutesintegerNoActivity window. Default 60, minimum 5, maximum 1440 (out-of-range values are clamped)

Response

  • 200 OK
    json
    {
      "data": {
        "totals": { "runs": 14, "aiSessions": 6, "signedPeriods": 2 },
        "chainActivations": [
          { "chainId": "9a1d4f6b-3c2e-4b7a-8d5c-6e9f2b1a7c34", "level": 1, "lastRunAt": "2026-06-10T09:28:12.000Z", "running": true }
        ],
        "nodeActivations": [
          { "nodeId": "n-typescript", "level": 0.93 },
          { "nodeId": "n-testing", "level": 0.41 }
        ],
        "windowMinutes": 60,
        "cacheTtlSeconds": 30,
        "generatedAt": "2026-06-10T09:30:00.000Z"
      }
    }

cacheTtlSeconds is the snapshot freshness — polling faster than this returns the same data, so use it as your minimum refresh interval. signedPeriods counts your own signed chain periods within the window.

Code Example

bash
curl "https://api.chainabit.com/api/v1/agents/twins/$TWIN_ID/brain/activity?windowMinutes=60" \
  -H "Authorization: Bearer $TOKEN"

GET /agents/twins/{twinId}/brain/chain-impact/

Description

Summarises how one of your chains has shaped the brain over the last 30 days: how many pieces of behavioural evidence the chain produced and when it last evolved the twin.

Request

  • Headers: Authorization: Bearer <token>
  • Path params: twinId (UUID), chainId (UUID)

Response

  • 200 OK
    json
    {
      "data": {
        "chainId": "9a1d4f6b-3c2e-4b7a-8d5c-6e9f2b1a7c34",
        "twinId": "3f7c2a90-5b1e-4d2a-8c4e-1a9d6b3f5e72",
        "strengthenedNodes": [],
        "evidenceCount": 6,
        "lastEvolvedAt": "2026-06-09T20:00:00.000Z"
      }
    }

strengthenedNodes entries have the shape { nodeId, label, kind, weightDelta? }; the array may be empty when the impact cannot be attributed to specific concepts — evidenceCount and lastEvolvedAt always summarise the chain's contribution.

Code Example

Use one of your own chain ids from the Chains API as $CHAIN_ID.

bash
curl "https://api.chainabit.com/api/v1/agents/twins/$TWIN_ID/brain/chain-impact/$CHAIN_ID" \
  -H "Authorization: Bearer $TOKEN"

POST /agents/twins/{twinId}/brain/sample

Description

Consumes the one-time free brain preview (Free plan). Returns a real brain graph — your actual data, capped at 80 nodes — exactly once per account. A second call returns 403 brain_sample_already_used. Check availability first with sample-status.

Request

  • Headers: Authorization: Bearer <token>
  • Path params: twinId (UUID)
  • Body: none

Response

  • 200 OK — same shape as GET .../brain/graph (without overlay), capped at 80 nodes:
    json
    {
      "data": {
        "nodes": [
          { "id": "n-typescript", "kind": "topic", "label": "TypeScript", "weight": 4.6, "degree": 2, "lastSeen": "2026-06-10T09:12:41.000Z" }
        ],
        "edges": [
          { "src": "n-typescript", "dst": "n-testing", "weight": 2.9, "releaseProb": 0.74, "sourceCount": 12 }
        ],
        "clusters": [{ "kind": "topic", "label": "topic", "nodeCount": 1 }],
        "stats": { "nodeCount": 1, "edgeCount": 1, "totalNodes": 184, "totalEdges": 512 },
        "truncated": true,
        "generatedAt": "2026-06-10T09:30:00.000Z"
      }
    }
  • 403 Forbidden — preview already used:
    json
    {
      "error": {
        "code": "brain_sample_already_used",
        "message": "The one-time brain preview has already been used on this account. Upgrade to unlock the full Brain Simulator."
      }
    }

Code Example

bash
curl -X POST "https://api.chainabit.com/api/v1/agents/twins/$TWIN_ID/brain/sample" \
  -H "Authorization: Bearer $TOKEN"

GET /agents/twins/{twinId}/brain/sample-status

Description

Tells you whether the one-time preview is still available on this account, so your UI can show the right call to action before posting to sample.

Request

  • Headers: Authorization: Bearer <token>
  • Path params: twinId (UUID)

Response

  • 200 OK
    json
    { "data": { "available": true } }

Code Example

bash
curl "https://api.chainabit.com/api/v1/agents/twins/$TWIN_ID/brain/sample-status" \
  -H "Authorization: Bearer $TOKEN"

POST /agents/twins/{twinId}/brain/narrate

Description

Generates a short AI narration of a region of your brain graph — a focus concept, a path of concepts, or an activation wave. This is the only billed endpoint in the Brain Simulator: usage is charged to your wallet, and each plan has a daily narration budget (Bitter 5/day, Chainer 25/day, Architect 100/day).

The narration is grounded in your graph only — node labels and their connections. Raw memories are never sent to the model.

Request

  • Headers: Authorization: Bearer <token>, Content-Type: application/json
  • Path params: twinId (UUID)
  • Body (provide at least one of focusNodeId, pathNodeIds, waveSeedId — if several are set, focusNodeId wins, then pathNodeIds, then waveSeedId):
FieldTypeRequiredDescription
focusNodeIdstring (max 256)No*Narrate around a single node
pathNodeIdsstring[] (max 10 items, each max 256)No*Narrate a path of nodes in order
waveSeedIdstring (max 256)No*Narrate the activation wave seeded at this node
localestringNoen, tr, es, fr, ar, de, pt — default en
  • Example body:
    json
    { "focusNodeId": "n-typescript", "locale": "en" }

Response

  • 200 OK
    json
    {
      "data": {
        "narration": "You keep circling back to TypeScript — it is the hub your testing habits and deep-focus sessions connect through...",
        "model": "gemini-3.5-flash",
        "creditsCharged": 286
      }
    }
FieldTypeDescription
narrationstringThe generated narration text
modelstringThe model that produced the narration
creditsChargednumberBilled usage committed to your wallet for this narration, measured in model tokens
  • 400 Bad Requestnarrate_target_required when no target is provided
  • 402 Payment Requiredinsufficient_credit when your wallet cannot cover the narration (top up via Wallet)
  • 404 Not Foundnarrate_target_not_found when the node ids do not exist in this twin brain
  • 429 Too Many Requests — daily narration budget for your plan is exhausted; resets the next day

Code Example

bash
curl -X POST "https://api.chainabit.com/api/v1/agents/twins/$TWIN_ID/brain/narrate" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "pathNodeIds": ["n-typescript", "n-testing"], "locale": "en" }'

GET /public/twins/{username}/brain

Description

Public, unauthenticated showcase of a brain — available only when the owner has a public profile and has opted in via the publicBrainShowcase twin preference (off by default; see Twins API). The payload is reduced: top topic, tool, and trait nodes only (up to 60 nodes and 180 edges) — never memories, never people. Everything else returns 404.

Changes — including turning the showcase off — can take a few minutes to propagate.

Request

  • Path params: username (Chainer username)
  • Rate limit: 30/min

Response

  • 200 OK
    json
    {
      "data": {
        "username": "ada",
        "nodes": [
          { "id": "n-typescript", "kind": "topic", "label": "TypeScript", "weight": 4.6 },
          { "id": "n-vitepress", "kind": "tool", "label": "VitePress", "weight": 2.1 },
          { "id": "n-deep-focus", "kind": "trait", "label": "Deep focus", "weight": 1.7 }
        ],
        "edges": [
          { "src": "n-typescript", "dst": "n-vitepress", "weight": 2.1 }
        ],
        "stats": { "nodeCount": 3, "edgeCount": 1 }
      }
    }
  • 404 Not Found — the user does not exist, has a non-public profile, or has not opted in

Code Example

bash
curl "https://api.chainabit.com/api/v1/public/twins/ada/brain"

Errors

StatusCodeWhenWhat to do
400VALIDATION_ERRORMalformed parameters (non-UUID twinId, unknown kinds value, too many pathNodeIds, …)Fix the request
400narrate_target_requiredNarrate body has none of focusNodeId, pathNodeIds, waveSeedIdProvide a target
401UNAUTHORIZEDMissing or expired tokenRe-authenticate
402insufficient_creditWallet balance cannot cover the narrationTop up your wallet
403FORBIDDENYour plan does not include the feature — the message names the missing feature keyUpgrade your plan
403brain_sample_already_usedThe one-time free preview was already consumed on this accountUpgrade to unlock the full simulator
404NOT_FOUNDTwin, memory, or chain not found or not accessible to you; public brain not availableCheck the id / opt-in state
404narrate_target_not_foundNo matching nodes for the narration target in this twin brainUse node ids from the brain graph
429RATE_LIMIT_EXCEEDEDEndpoint rate limit hit, or the daily narration budget is exhaustedRetry later; narration budgets reset daily

Errors use the standard response envelope. See also the global error codes reference.

Notes

  • Replace https://api.chainabit.com/api/v1 with your base URL, e.g. https://api.chainabit.com/api/v1.
  • All twinId, memoryId, and chainId path parameters are UUIDs. Discover your twinId with GET /agents/twins/resolve.
  • Account, workspace, and identity scope are derived from your authentication token — request bodies never include them.
  • Simulating thinking and tracing decision paths happen in the app on top of the graph data — deterministic and free. Only narrate spends from your wallet.

Built with purpose.