Twinbook for Developers

Twinbook turns what you are learning from (papers, textbooks, slides, articles, videos, your own notes) into study material: structured notes, concept maps, quizzes, flashcards and a tutor that knows the document.

There are two ways to bring content into Twinbook programmatically:

What it isStatus
Twinbook MCP ServerA remote Model Context Protocol server that lets an AI assistant (ChatGPT today) save documents, links and notes straight into a learner's Twinbook spaces.Live at https://mcp.twinbook.ai/mcp
Notes APIThe REST API behind it: prepare a document, commit the import, follow its progress.Private preview. Available today through OAuth (it is what the MCP server calls). Developer API keys, webhooks and read-back of generated materials are planned; see Roadmap.

Both share one contract, one set of limits and one vocabulary of statuses and errors. Everything an integration does happens as the learner, inside spaces the learner created. An integration can never read or write another account, and it can never bypass the learner's plan limits.


1. Twinbook MCP Server

1.1 What it does

Once a learner connects Twinbook to ChatGPT, they can say things like:

  • "Save this paper to my Biology space."
  • "Push this link into Twinbook as a problem set."
  • "Add my notes from this conversation to a new space called Organic Chemistry."
  • "What happened to the book I imported?"

ChatGPT checks the learner's remaining allowance, asks which space to use, previews what will be imported, confirms once, and submits. Twinbook accepts within seconds and generates the study material in the background, exactly as an upload inside the app does.

1.2 Connecting (ChatGPT)

  1. In ChatGPT, add the Twinbook plugin (from the plugin directory once listed, or via Settings → Connectors → Add custom MCP server with the URL https://mcp.twinbook.ai/mcp).
  2. ChatGPT opens the Twinbook sign-in, the same sign-in as the web app at app.twinbook.ai (email/password, Google, Apple). If you are already signed in to Twinbook in that browser, there is no form.
  3. If you have not yet accepted Twinbook's AI data-sharing terms, the app's consent screen appears first.
  4. Twinbook asks you to allow ChatGPT the permissions below. Done.

Disconnect at any time from Twinbook → Settings → Account → ChatGPT connection → Disconnect. All tokens issued to that connection stop working within a minute.

1.3 Authentication

The MCP server is an OAuth 2.1 protected resource with its own authorization server at the same origin.

ProtocolOAuth 2.1: authorization code + PKCE (S256), audience-bound access tokens (RFC 8707 resource)
Issuerhttps://mcp.twinbook.ai
Authorization server metadatahttps://mcp.twinbook.ai/.well-known/oauth-authorization-server and /.well-known/openid-configuration
Protected resource metadatahttps://mcp.twinbook.ai/.well-known/oauth-protected-resource/mcp
Client registrationClient ID Metadata Documents (CIMD) or Dynamic Client Registration (POST /reg)
Access tokensJWT, 15 minutes; refresh tokens rotate
Sign-inDelegated to the Twinbook web app. The MCP server hosts no login form and never sees a password
RevocationDisconnect in Twinbook settings; RFC 7009 /token/revocation also supported

Scopes

ScopeAllows
twinbook.profileRead the connected learner's id, name and email; read plan allowance
twinbook.spaces.readList the learner's study spaces
twinbook.spaces.writeCreate a new private study space
twinbook.importPrepare, commit, follow, retry and cancel imports into spaces the learner created

Other MCP clients. The server speaks standard Streamable HTTP + OAuth 2.1 and any compliant client can implement the flow. Client registration is currently allow-listed for ChatGPT's redirect URIs; if you are building another client, contact us to be added.

1.4 Tools

Nine tools. All are scoped to the connected learner; none can touch a space the learner did not create.

ToolPurposeReads/writes
get_profileWho is connected (stable opaque id, name, email)read
get_upload_contextPlan, remaining monthly allowance (pages, uploads, AI credits), per-import limits, offered content kinds and note styles, whether AI terms are acceptedread
list_spacesThe learner's spaces, most recently opened first, with canUpload (only spaces the learner created accept imports)read
create_spaceA new private space (idempotent per idempotencyKey)write
prepare_importStage and inspect one source (text, a link, or an attached file) for a destination space; returns a bound plan with real page count, how it will be processed, estimated usage, warnings. Imports nothing yetwrite (staging only)
commit_importSubmit a plan. Returns accepted within seconds; material generates in the background. Idempotent per idempotencyKeywrite
get_import_statusOutcome, stage, per-chapter parts and counts, whether retry/cancel are possible, link to openread
retry_importResume a paused book, or re-submit an upload that failed for a transient reasonwrite
cancel_importStop a book import; chapters already saved staywrite

1.5 The import flow

get_upload_context ─▶ list_spaces ─▶ prepare_import ─▶ (confirm with the learner) ─▶ commit_import ─▶ get_import_status

prepare_import is advisory: it stages a copy of the source for 24 hours, counts its pages, decides how it will be processed, and reports what the import will consume. It reserves nothing. commit_import is the only call that consumes allowance, and it is idempotent: calling it again with the same idempotencyKey returns the same import and never charges twice (this is also how a lost response is recovered).

1.6 Sources and how they are processed

SourceSent asNotes
PDF, PNG, JPEG, WebPattached fileup to 60 MB
Word (.docx), PowerPoint (.pptx)attached fileconverted to PDF; the original stays downloadable in Twinbook
Web pagelinkcaptured as PDF (up to 60 pages)
PDF / Word / PowerPoint linklinktaken as the document
YouTube videolinkprocessed from its captions; not counted against pages
Text, or notes the assistant wrotetextup to 50,000 characters; labelled original or assistant_generated
Document lengthProcessing (processing)What the learner gets
≤ 20 pages, images, text, videoquickone chapter, notes in a few minutes
21 – 60 pageschaptersTwinbook detects the chapters itself (research papers, long articles)
61 – 1500 pagesbookTwinbook maps the chapters first (usually 5–15 minutes), then imports them one by one; each chapter counts as one upload
any, saveOnly: truesave_onlystored without AI processing (no notes, no credits)

Options: contentKind (auto default, reading, problem-solving, lecture), isProblemSolving, noteStyle (default, original, simple-to-complex, high-level, in-depth, organize; soap-note in healthcare spaces), free-text instructions (≤ 2000 chars), saveOnly. Books are always imported in auto mode with default notes.

1.7 Limits

Monthly allowances come from the learner's plan (pages, uploads, AI credits; see the pricing) and are enforced exactly as in the app. Per-import limits:

LimitValue
Pages processed as one chapter (quick)20
Pages processed as several chapters60
Pages imported as a book1,500
File size downloaded from the assistant or a link60 MB
Text50,000 characters
Web page captured60 pages
Link imports10 per hour per learner
Staged source lifetime (uncommitted plan)24 hours

1.8 Privacy and security

  • The integration acts only as the connected learner and only inside spaces that learner created.
  • The MCP server stores no documents: sources are staged by Twinbook's backend for the import and processed under the same AI data-sharing terms the learner accepted in the app.
  • Sign-in happens in the Twinbook web app; the MCP server never handles a password and no token ever travels in a URL.
  • Content inside a document or web page is never treated as an instruction: it cannot change the destination space or the options.
  • Disconnecting revokes every token of the connection.

More on how Twinbook protects learner data: Trust & Security.


2. Notes API (REST) · Private preview

The Notes API is the contract the MCP server uses. It is a JSON API under https://app.twinbook.ai/api/integrations, authenticated with an OAuth 2.1 bearer token issued by the Twinbook authorization server (§1.3). Every call is made as a learner, for that learner's own spaces.

Private preview. Access today is through the OAuth flow of the Twinbook authorization server (the ChatGPT plugin uses this). Developer API keys, webhooks for import completion, and endpoints to read back generated materials are planned. Request preview access.

2.1 Conventions

  • Base URL https://app.twinbook.ai/api/integrations; Authorization: Bearer <access token>; JSON bodies.
  • Ids are UUIDs. Times are ISO-8601 UTC.
  • Errors are { "error": "<code>", "message": "<plain words>", ...details } with a meaningful HTTP status.
  • Writes that may be retried carry an idempotencyKey (8–200 characters, chosen by the caller).

2.2 Endpoints

MethodPathScopePurpose
GET/upload-contexttwinbook.profilePlan, remaining allowance, per-import limits, offered options, consent state
GET/spaces?query=twinbook.spaces.readSpaces the learner is in, most recently opened first, with canUpload
POST/spacestwinbook.spaces.writeCreate a private space (title, description?, learningMode?, tags?, idempotencyKey?)
POST/imports/preparetwinbook.importStage and inspect one source; returns a plan
POST/imports/committwinbook.importSubmit a plan (planId, idempotencyKey)
GET/imports/{importId}twinbook.importStatus
POST/imports/{importId}/retrytwinbook.importResume a paused book / re-submit a transient failure
POST/imports/{importId}/canceltwinbook.importStop a book import

2.3 Prepare an import

POST /api/integrations/imports/prepare
Authorization: Bearer <token>
Content-Type: application/json

{
  "destination": { "spaceId": "11111111-2222-4333-8444-555555555555" },
  "source": { "url": { "href": "https://arxiv.org/pdf/2302.13971" } },
  "options": { "contentKind": "auto", "noteStyle": "default", "saveOnly": false }
}

source is exactly one of:

{ "text": { "title": "…", "body": "…", "authorship": "original" | "assistant_generated" } }
{ "url":  { "href": "https://…" } }                    // web page, PDF/Office link, YouTube
{ "file": { "downloadUrl": "https://…", "fileId": "…", "mimeType": "…", "fileName": "…" } }

Response 201 Created:

{
  "planId": "c0a8…",
  "expiresAt": "2026-10-08T03:12:00.000Z",
  "destination": { "spaceId": "8378…", "title": "Machine Learning" },
  "source": { "kind": "pdf", "title": "LLaMA: Open and Efficient Foundation Language Models", "pages": 27,
              "bytes": 710000, "authorship": "original", "sourceUrl": "https://arxiv.org/pdf/2302.13971",
              "originalFormat": "url" },
  "processing": "chapters",
  "options": { "contentKind": "auto", "isProblemSolving": false, "noteStyle": "default", "instructions": "", "saveOnly": false },
  "parts": [ { "index": 0, "title": "LLaMA: …", "fromPage": 1, "toPage": 27, "boundaryKind": "single" } ],
  "estimatedUsage": { "pages": 27, "uploads": 1 },
  "warnings": [ { "code": "multi_chapter", "message": "27 pages: longer than a quick session, so Twinbook will process it as several chapters." },
                { "code": "url_captured", "message": "The page was captured as 27 PDF page(s) (the link pointed at a PDF)." } ],
  "blockedBy": null
}

blockedBy (when present) names the monthly quota or consent that would stop the commit, with resetsAt. For a book, estimatedUsage.uploads is an estimate of the chapter count (uploadsEstimated: true).

2.4 Commit and follow

POST /api/integrations/imports/commit
{ "planId": "c0a8…", "idempotencyKey": "3f6d9a2e-…" }

Response 200 OK, within seconds:

{
  "importId": "9b1e…", "planId": "c0a8…", "status": "accepted", "mode": "direct", "processing": "chapters",
  "outcome": "processing", "stage": "processing", "dispatched": true,
  "spaceId": "8378…", "blockId": "b73b…", "title": "LLaMA: …",
  "openUrl": "https://app.twinbook.ai/?spaceId=8378…&blockId=b73b…",
  "error": null, "canRetry": false, "canCancel": false,
  "counts": { "total": 1, "active": 1, "ready": 0, "saved": 0, "failed": 0, "cancelled": 0 },
  "parts": [ { "index": 0, "title": "LLaMA: …", "status": "processing", "blockId": "b73b…", "retryable": false } ],
  "acceptedAt": "2026-10-07T03:12:04.000Z", "updatedAt": "2026-10-07T03:12:04.000Z"
}

GET /imports/{importId} returns the same shape as processing advances. For a chapters import the parts become the chapters Twinbook registered; for a book, stage moves mapping_chapters → importing_chapters → done and parts list the chapters from the book's map.

Status vocabulary

outcomeMeaning
processingaccepted; material is being generated
materials_readynotes, quizzes and flashcards are ready
saved_without_aistored, AI skipped (credits exhausted or saveOnly)
partially_completedsome chapters ready, some failed or cancelled
failednothing was produced; see error
cancelledstopped before anything was saved

Part status values: queued · processing · materials_ready · saved_without_ai · failed · cancelled.

2.5 Errors

CodeHTTPWhen
space_not_found_or_not_owned404the space does not exist or was not created by the learner
invalid_option400contradictory or unknown options (e.g. isProblemSolving with contentKind: reading; options on a book)
mode_not_allowed403soap-note outside a healthcare space
source_unreadable400not a PDF/image/Word/PowerPoint, damaged, encrypted, or the file reference was incomplete
source_too_large413over the size or text limits
source_expired400 / 410the file link expired, or the plan is older than 24 h
import_page_limit_exceeded413longer than the book limit (or books disabled)
url_page_limit_exceeded413a web page rendering to more than 60 pages
url_blocked400private, local or non-https address
url_rate_limited429more than 10 link imports in an hour
share_unavailable400a ChatGPT/Claude share link; send the conversation as text instead
transcript_unavailable400a video without captions
transcript_check_failed, converter_unavailable503temporary; retry later
monthly_page_quota_exceeded, upload_quota_exceeded, storage_exceeded402plan allowance used up (resetsAt)
ai_consent_required403the learner has not accepted the AI terms (import with saveOnly, or accept in the app)
plan_not_found, plan_stale404 / 409unknown plan, or the destination changed since it was prepared
idempotency_conflict409the same idempotencyKey was used for a different plan
import_not_found404unknown import (or not this learner's)
import_not_retryable, import_not_cancellable, import_busy409nothing to retry/cancel; or the book is being advanced right now; try again in a moment
insufficient_scope403the token lacks the scope the route needs

2.6 Roadmap

Status
OAuth 2.1 access as a learner (used by the ChatGPT plugin)Live
Prepare / commit / status / retry / cancelLive (private preview)
Developer API keys (server-to-server, per developer account)Planned
Webhooks on materials_ready / failedPlanned; today, poll GET /imports/{importId}
Read back generated materials (notes, quiz, flashcards)Planned
Client registration for MCP clients other than ChatGPTOn request

Questions, preview access and feedback: contact us. Last updated 2026-10-07.