{"openapi":"3.1.0","info":{"title":"Potto Japanese Agent API","version":"1.0.0","description":"Authoritative JLPT-graded Japanese learning dataset: 16,885 original, independently curated learnables (2,375 kanji, 6,051 vocabulary, 935 grammar points, JLPT N5–N1; senses, pitch accent, etymology, and keigo registers cross-verified against multiple authoritative references, citations included per entry), plus 6,945 Japanese-history entities, 1,362 lessons, 96 journeys, 20 worlds.\n\nPricing is JPY-first: 1 point = ¥1 (fixed peg). Live per-call prices are in the GET /api/v1 discovery document. Keyless requests receive a bundled evaluation sample (marked \"sample\": true) — full access requires an API key: https://potto.app/developers\n\nAn MCP (Model Context Protocol) endpoint with the same six tools is served at POST /mcp (stateless Streamable HTTP).","contact":{"url":"https://potto.app/developers"}},"servers":[{"url":"https://potto.app","description":"Production (Firebase Hosting rewrite)"},{"url":"https://us-central1-pottojapan.cloudfunctions.net/agentApi","description":"Direct function URL"}],"security":[{"apiKey":[]},{"bearerKey":[]},{}],"components":{"securitySchemes":{"bearerKey":{"type":"http","scheme":"bearer","description":"API key (potto_ak_…) as a bearer token. Create one at the Developer page."},"apiKey":{"type":"apiKey","in":"header","name":"X-API-Key"}},"schemas":{"Error":{"type":"object","properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"required":{"type":"integer","nullable":true},"balance":{"type":"integer","nullable":true},"top_up_url":{"type":"string","nullable":true}}}}},"LearnableSummary":{"type":"object","description":"Compact learnable summary returned by search and related expansion.","properties":{"id":{"type":"string"},"label":{"type":"string","nullable":true},"type":{"type":"string","description":"kanji | vocabulary | grammar | character | radical, or a history entity type (person, event, era, …)."},"domain":{"type":"string","enum":["language","history"]},"jlpt_level":{"type":"integer","nullable":true,"description":"5 = N5 (beginner) … 1 = N1 (advanced)."},"is_common_word":{"type":"boolean","nullable":true},"canonical_name":{"type":"string","nullable":true,"description":"History entities only."},"pattern":{"type":"string","nullable":true,"description":"Grammar points only."},"meaning":{"type":"string","nullable":true},"writing_forms":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string"},"script":{"type":"string"}}}},"tags":{"type":"array","items":{"type":"string"}}}},"Billing":{"type":"object","description":"Appended to every keyed (billed) response.","properties":{"points_charged":{"type":"integer","description":"Points charged for this call. 1 point = ¥1 (fixed peg)."},"currency":{"type":"string","enum":["JPY"]},"invoice_id":{"type":"string","nullable":true},"free_tier":{"type":"boolean","nullable":true}}}}},"paths":{"/api/v1":{"get":{"operationId":"getDiscovery","summary":"Discovery document: endpoints, auth, live pricing, mode.","security":[{}],"responses":{"200":{"description":"Service metadata and live JPY pricing.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"data":{"type":"object"}}}}}},"401":{"description":"Missing/unknown key (INVALID_API_KEY) or revoked/expired key (REVOKED_API_KEY).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"INSUFFICIENT_POINTS — the key owner's balance cannot cover this call. The error carries required, balance, and top_up_url.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED (per-key burst limit) or QUOTA_EXCEEDED (daily fair-use call cap; resets 00:00 UTC — contact us for higher caps).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/learnables":{"get":{"operationId":"searchLearnables","summary":"Search learnables by Japanese or English surface forms.","description":"Token match over the curated search surface. Results are compact summaries; fetch the full entry with getLearnable.","parameters":[{"name":"query","in":"query","required":true,"schema":{"type":"string"},"description":"Japanese surfaces (満点, まんてん) or English tokens."},{"name":"type","in":"query","schema":{"type":"string","enum":["kanji","vocabulary","grammar","character","radical"]}},{"name":"jlpt_level","in":"query","schema":{"type":"integer","minimum":1,"maximum":5}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":20},"description":"Default 10, hard cap 20 — callers can ask for less, never more."}],"responses":{"200":{"description":"Ranked summaries (exact surface matches first).","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"data":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/LearnableSummary"}},"total_candidates":{"type":"integer"},"limit":{"type":"integer"},"billing":{"$ref":"#/components/schemas/Billing"}}}}}}}},"401":{"description":"Missing/unknown key (INVALID_API_KEY) or revoked/expired key (REVOKED_API_KEY).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"INSUFFICIENT_POINTS — the key owner's balance cannot cover this call. The error carries required, balance, and top_up_url.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED (per-key burst limit) or QUOTA_EXCEEDED (daily fair-use call cap; resets 00:00 UTC — contact us for higher caps).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/learnables/{id}":{"get":{"operationId":"getLearnable","summary":"Full learnable entry (senses, readings, pitch accent, etymology, kanji properties, cited sources).","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"locale","in":"query","required":false,"schema":{"type":"string","minLength":2,"maxLength":2},"description":"Two-letter language code. English (\"en\") is canonical; any other locale is generated on demand (billed at the localized-read price on a cache miss, plain read price on a hit) and cached for everyone after."}],"responses":{"200":{"description":"The full learnable document (internal fields stripped, sources included).","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"data":{"type":"object"}}}}}},"401":{"description":"Missing/unknown key (INVALID_API_KEY) or revoked/expired key (REVOKED_API_KEY).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"INSUFFICIENT_POINTS — the key owner's balance cannot cover this call. The error carries required, balance, and top_up_url.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED (per-key burst limit) or QUOTA_EXCEEDED (daily fair-use call cap; resets 00:00 UTC — contact us for higher caps).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/learnables/{id}/related":{"get":{"operationId":"getRelatedLearnables","summary":"One-hop expansion of a learnable's typed concept relationships.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":20}}],"responses":{"200":{"description":"Related learnable summaries with relationship types (precedes, causes, part-of, …).","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"data":{"type":"object"}}}}}},"401":{"description":"Missing/unknown key (INVALID_API_KEY) or revoked/expired key (REVOKED_API_KEY).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"INSUFFICIENT_POINTS — the key owner's balance cannot cover this call. The error carries required, balance, and top_up_url.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED (per-key burst limit) or QUOTA_EXCEEDED (daily fair-use call cap; resets 00:00 UTC — contact us for higher caps).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/lessons/{id}":{"get":{"operationId":"getLesson","summary":"One of 1,362 structured lessons: teaching markdown, questions, learnable refs.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"render","in":"query","schema":{"type":"string","enum":["raw","plain","resolved"]},"description":"Potto [[learn-id|text]] link handling; resolved also returns a learnable_refs id→display map."},{"name":"locale","in":"query","required":false,"schema":{"type":"string","minLength":2,"maxLength":2},"description":"Two-letter language code. English (\"en\") is canonical; any other locale is generated on demand (billed at the localized-read price on a cache miss, plain read price on a hit) and cached for everyone after."}],"responses":{"200":{"description":"The lesson document.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"data":{"type":"object"}}}}}},"401":{"description":"Missing/unknown key (INVALID_API_KEY) or revoked/expired key (REVOKED_API_KEY).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"INSUFFICIENT_POINTS — the key owner's balance cannot cover this call. The error carries required, balance, and top_up_url.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED (per-key burst limit) or QUOTA_EXCEEDED (daily fair-use call cap; resets 00:00 UTC — contact us for higher caps).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/journeys/{id}":{"get":{"operationId":"getJourney","summary":"One of 96 curated journeys: a lesson graph with branches and outcomes.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The journey document.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"data":{"type":"object"}}}}}},"401":{"description":"Missing/unknown key (INVALID_API_KEY) or revoked/expired key (REVOKED_API_KEY).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"INSUFFICIENT_POINTS — the key owner's balance cannot cover this call. The error carries required, balance, and top_up_url.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED (per-key burst limit) or QUOTA_EXCEEDED (daily fair-use call cap; resets 00:00 UTC — contact us for higher caps).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/worlds":{"get":{"operationId":"listWorlds","summary":"Catalog entry point: the 20 themed worlds with JLPT ranges and teaching domains.","responses":{"200":{"description":"All worlds.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"data":{"type":"object"}}}}}},"401":{"description":"Missing/unknown key (INVALID_API_KEY) or revoked/expired key (REVOKED_API_KEY).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"INSUFFICIENT_POINTS — the key owner's balance cannot cover this call. The error carries required, balance, and top_up_url.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED (per-key burst limit) or QUOTA_EXCEEDED (daily fair-use call cap; resets 00:00 UTC — contact us for higher caps).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}