Maximem Synap Documentation Audit
One-line state: the prose is polished and the mental model is clear, but the reference layer is fractured — error types, retry fields, config models, webhook events, version pins, and regions each have two or more mutually contradictory definitions across pages, and two launched dashboard features (webhooks, analytics) have no surviving doc page.
1. SDK method pages tell you to catch exceptions that don't exist in the error hierarchy (critical)
Location: /sdk-reference/memories/create (and all sdk-reference method pages) vs /sdk/error-handling
Problem: The memories.create "Raises" section lists SynapAuthError, SynapValidationError, and SynapConflictError. The annotation notes this Synap*Error scheme is used across every method page (SynapAuthError 12×, SynapValidationError 6×). None of these names appear in the documented error hierarchy on /sdk/error-handling, which instead defines AuthenticationError, InvalidInputError, and has no conflict type at all.
Consequence: A developer (or coding agent) who writes except SynapAuthError from the reference pages will never catch anything — the SDK raises AuthenticationError. The exact exception you're told to handle is uncatchable, so error handling silently fails in production. An agent cannot reconcile this; it picks one scheme and the code breaks against the real SDK.
The fix: Pick one exception naming scheme and use it everywhere. If AuthenticationError/InvalidInputError is canonical (it matches the hierarchy and overview), rewrite every method page's "Raises" section to use those names, and either add a real ConflictError to the hierarchy or replace the conflict reference.
2. The retry-delay field has two different names across pages (critical)
Location: /sdk-reference/overview and /resources/faq (e.retry_after) vs /sdk/error-handling and /guides/production-checklist (e.retry_after_seconds)
Problem: The overview's RateLimitError example and the FAQ both read e.retry_after (print(f"Retry after {e.retry_after}s")). The error-handling page and production-checklist both read e.retry_after_seconds (await asyncio.sleep(e.retry_after_seconds)). Same exception, same field, two spellings.
Consequence: Only one attribute name is real. Whichever half of the docs is wrong, a developer copy-pasting from it gets an AttributeError the moment a rate limit actually fires — i.e., precisely when the retry path is exercised under load and hardest to test for.
The fix: Confirm the real attribute name on RateLimitError, then make all four pages agree. Add the field to the error reference so there's a single source of truth.
3. requirements.txt pins a version that does not exist (critical)
Location: /setup/installation vs /resources/changelog
Problem: The Installation page's requirements.txt snippet pins maximem-synap>=0.2.0. The changelog's newest released version is v0.1.2 (2025-01-15), and the Compatibility Matrix only lists the 0.1.x line.
Consequence: pip install -r requirements.txt cannot resolve >=0.2.0 against a package whose latest published release is 0.1.2 — the install fails at the very first step of the getting-started flow.
The fix: Change the pin to a version that exists (e.g. maximem-synap>=0.1.2,<0.2). If 0.2.0 is imminent, ship the changelog entry and Compatibility Matrix row before referencing it in install instructions.
4. The webhook setup page 404s while the feature is documented as shipped (critical)
Location: /dashboard/webhooks (verified HTTP 404) — linked from /sdk-reference/memories/create, /guides/production-checklist, /concepts/integration-overview, and /sdk-reference/memories/batch-create
Problem: https://docs.maximem.ai/dashboard/webhooks.md returns 404 (control /dashboard/overview.md returns 200), yet at least four pages link to it for setup, payloads, and signature verification — e.g. production-checklist's "See Dashboard Webhooks for setup instructions." The changelog (v0.1.0) announces a full "Webhook system… with HMAC-SHA256 signature verification."
Consequence: A developer trying to implement the documented HMAC-SHA256 verification — security-critical, since unverified webhooks are a forgery vector — hits a dead page from every entry point. There is no payload schema, no signature reference, no event catalog they can rely on.
The fix: Publish /dashboard/webhooks with payload schemas, the signature-verification algorithm, and the canonical event list. Until it exists, remove or redirect the four inbound links rather than pointing developers at a 404.
5. The dashboard analytics page also 404s and is linked from live pages (significant)
Location: /dashboard/monitoring-and-analytics (verified HTTP 404) — linked from /resources/faq and /concepts/memories-and-context
Problem: https://docs.maximem.ai/dashboard/monitoring-and-analytics.md returns 404 (verified via the same Chrome network check that confirms /dashboard/overview.md returns 200). The FAQ tells developers to "Use the Dashboard Analytics to monitor your usage in real time," and concepts/memories-and-context links the same path. The getting-started overview lists "monitor ingestion pipelines" as a Dashboard capability, but its dedicated page does not exist.
Consequence: A developer following the FAQ's instruction to monitor usage lands on a dead page. This is the second confirmed dead /dashboard/* page (alongside webhooks) — only /dashboard/overview actually resolves, so two of the three documented Dashboard features have no reference page despite live inbound links.
The fix: Publish /dashboard/monitoring-and-analytics, or remove/redirect the inbound links from the FAQ and memories-and-context until it exists. Audit all /dashboard/* links against the pages that actually render.
6. Two conflicting error taxonomies, several error types have no wire code, and auth messages don't map to codes (significant)
Location: /sdk-reference/errors vs /sdk/error-handling, with /setup/authentication
Problem: /sdk-reference/errors is the wire-level code list; /sdk/error-handling is a much larger SDK taxonomy. The latter documents InsufficientCreditsError, SessionExpiredError, AgentUnavailableError, NetworkTimeoutError, ListeningAlreadyActiveError, and ListeningNotActiveError — none of which have a documented wire code on the errors page. Conversely, INTERNAL_ERROR (500) on the errors page maps to no typed SDK exception. The gap shows up concretely in auth: the authentication page's troubleshooting table surfaces the strings AuthenticationError: No Synap API key found and AuthenticationError: Invalid credentials, while the wire-level errors page uses the codes UNAUTHORIZED and CREDENTIAL_INVALID — no page connects the message to the code to the exception.
Consequence: A developer handling insufficient credits, a 500, or an auth failure can't map between the HTTP-level code, the SDK exception they're supposed to catch, and the message they'll actually see logged. The pages can't be cross-referenced, so robust error handling requires guessing.
The fix: Maintain one error table keyed by (wire code ⇄ HTTP status ⇄ SDK exception ⇄ surfaced message). Give every SDK exception a wire code and every wire code an exception (including a typed exception for INTERNAL_ERROR), and list the auth message strings against their codes.
7. mode is required on one page and optional on two others (significant)
Location: /sdk-reference/errors (treats mode as "Required field is missing") vs /resources/faq and /index
Problem: The errors page's INVALID_INPUT example treats a missing mode as a required-field error. The FAQ says ingestion mode defaults to long-range — i.e. optional. The homepage's own memories.create example passes no mode at all (just document, document_type, user_id).
Consequence: If mode is genuinely required, the homepage quickstart example is broken and will 400 on the first call. If it's optional, the errors page is wrong. A developer can't know whether to pass it.
The fix: Decide whether mode is required or defaulted, then make the errors page, FAQ, and homepage example agree. If defaulted, document the default value on the memories.create reference.
8. document_type enum disagrees on which values are live (significant)
Location: /sdk-reference/memories/create vs /sdk-reference/errors
Problem: The memories.create page marks human-chat-conversation, support-ticket, knowledge-article, and note as (coming soon). The errors page's INVALID_INPUT enum lists those exact four as currently-valid values — and omits pdf, image, audio, and meeting-transcript, which the create page documents as available.
Consequence: A developer trying to ingest a support-ticket gets contradictory guidance: one page says it's not available yet, the other implies it's valid. Whichever they trust, they may build against a document_type the API rejects.
The fix: Generate the document_type enum from a single source and render it identically on both pages, with an explicit availability flag per value.
9. Duplicate document_id raises two different errors (significant)
Location: /sdk-reference/memories/create vs /resources/faq
Problem: The create page says a duplicate document_id raises SynapConflictError. The FAQ says the same case "is rejected as a duplicate (InvalidInputError)."
Consequence: A developer writing idempotent ingestion (catch-the-duplicate, continue) doesn't know which exception to catch. Catch the wrong one and duplicates crash the pipeline instead of being skipped.
The fix: Pick the real exception for duplicate IDs and state it identically on both pages (this also intersects with the Synap*Error naming problem in finding 1).
10. Webhook event lists don't match between changelog and production checklist (significant)
Location: /resources/changelog vs /guides/production-checklist
Problem: The changelog (v0.1.0) lists five webhook events: conversation.started, conversation.ended, context.retrieved, config.applied, compaction.completed. The production-checklist lists four: ingestion.failed, credential.expiring, config.applied, retention.cleanup. Only config.applied appears on both — that's a total of eight distinct event names across two pages.
Consequence: A developer building webhook handlers can't know which events actually fire. They'll subscribe to events that may not exist and miss events that do — and with the webhook reference page 404ing (finding 4), there's no authoritative list to settle it.
The fix: Publish one canonical event catalog (on the webhooks page) and have the changelog and checklist reference it rather than restating divergent lists.
11. The same SDKConfig timeout surface is documented as two different models (significant)
Location: /resources/performance-limits vs /guides/production-checklist
Problem: Performance-limits documents SDK timeouts as per-retrieval-mode values (fast 8000 ms / accurate 30000 ms), "configurable per call via SDKConfig.timeouts." Production-checklist documents the same SDKConfig timeouts as connect/read/write (5s / 30s / 10s). These are two incompatible mental models for one config object.
Consequence: A developer configuring timeouts can't tell whether SDKConfig.timeouts takes mode keys (fast/accurate) or transport keys (connect/read/write). The wrong shape either errors or is silently ignored, leaving production timeouts at unintended defaults.
The fix: Document the real structure of SDKConfig.timeouts once, with a concrete code example, and delete the competing model from the other page (or explain how the two layers compose if both exist).
12. Data residency names conflict on a compliance-critical fact (significant)
Location: /resources/security-trust vs /resources/faq
Problem: The security page's residency table lists the EU region as "EU West / Frankfurt". The FAQ says Synap Cloud is available in "US East and EU Central." Frankfurt is conventionally eu-central-1, so "EU West / Frankfurt" is internally questionable on top of contradicting the FAQ.
Consequence: Region/residency is exactly the fact compliance and procurement teams cite in DPAs and security reviews. Two different EU region labels in the docs undermine trust and can stall a GDPR data-residency sign-off until someone confirms which is true.
The fix: State the canonical region label once (location + cloud region code) and reference it from both pages. Reconcile the "EU West vs EU Central" naming against the actual Frankfurt region.
13. Default-retry behavior is self-contradictory within and across pages (significant)
Location: /sdk/error-handling and /guides/production-checklist
Problem: Two issues. (1) Error-handling prose says "all four transient error types are retried by default (NetworkTimeoutError, RateLimitError, ServiceUnavailableError, and AgentUnavailableError)," but the adjacent RetryPolicy example's retryable_errors list omits AgentUnavailableError and adds SynapTransientError instead. (2) Production-checklist says the default retry policy is "3 attempts," while the error-handling example sets max_attempts=5.
Consequence: A developer copying the example silently loses auto-retry on AgentUnavailableError despite the prose promising it, and can't tell whether the real default is 3 or 5 attempts — which directly changes failure behavior under load.
The fix: Make the prose and the code example list the identical set of retryable errors, and state one true default max_attempts. If SynapTransientError is a valid catch-all, document why it appears instead of the leaf type.
14. Rate-limit tiers don't match the plan names used elsewhere (significant)
Location: /sdk-reference/overview
Problem: The rate-limit table names tiers Free / Pro / Enterprise. Per the overview annotation, the plan names referenced for this product are Trial / Starter / Pro / Scale / Enterprise — only "Pro" and "Enterprise" overlap, and there's no "Free" plan in that list. Caveat: those plan names appear only in the auditor's annotation of this page; no pricing page was captured in the scraped evidence, so the exact plan-name set should be confirmed against the live pricing page before publishing this finding externally. The rate-limit table itself (Free/Pro/Enterprise) is directly verified.
Consequence: If the plan names are as annotated, a developer on a "Starter" or "Scale" plan can't find their request-per-minute limit in the rate-limit table at all, and someone on "Free" can't tell which plan that maps to. Capacity planning becomes guesswork.
The fix: Use the same plan names in the rate-limit table as on the pricing page, with a row per plan (including burst limits for each). Confirm the canonical plan-name set first.
15. "Recommended for all environments" includes a runtime the JS SDK says is unsupported (significant)
Location: /setup/authentication vs /setup/installation (JS SDK section)
Problem: The authentication page's "recommended for all environments" list includes AWS Lambda. The JS SDK section states that "AWS Lambda Node-only runtimes are not supported" because the JS SDK spawns the Python SDK as a subprocess and needs Python 3.11+ on the host.
Consequence: A JavaScript/TypeScript developer reads "recommended" for Lambda, ships to a Node-only Lambda, and the SDK fails at runtime because there's no Python on the host. The "recommended" claim is unqualified and contradicts the platform constraint.
The fix: Qualify the recommendation by SDK — note that AWS Lambda is supported only where a Python 3.11+ runtime is available, and is unsupported for the JS SDK on Node-only Lambda runtimes.
16. The OpenClaw plugin documents a parallel, contradictory credential scheme (significant)
Location: /plugins/openclaw vs /setup/authentication
Problem: Every core page uses synap_... keys, the SYNAP_API_KEY env var, and the dashboard at synap.maximem.ai. The OpenClaw plugin page instead instructs you to get a key starting with mx_... from app.maximem.ai, stored in MAXIMEM_API_KEY — with no explanation of how the two schemes relate, whether they're interchangeable, or which backend issues which key.
Consequence: A developer can't tell whether they need one credential or two, which dashboard to log into, or whether a synap_ key will authenticate the plugin. There's no stated mapping between SYNAP_API_KEY/synap.maximem.ai and MAXIMEM_API_KEY/app.maximem.ai, so onboarding stalls on an auth question the docs never answer. (Rated significant rather than critical: the plugin reads as a separate, partially-rebranded product — OpenClaw/Vity/moltbot — not the core SDK path, so this blocks plugin onboarding, not a production SDK call.)
The fix: Add an explicit note reconciling the two systems: which product uses which key prefix, env var, and console, and whether keys are shared. If the plugin is a distinct product, say so and link the relationship clearly.
17. The OpenClaw plugin's "Issues & bugs" link 404s and uses a third product name (significant)
Location: /plugins/openclaw
Problem: The plugin's support section links "Issues & bugs" to github.com/gauravmaximem/moltbot-memory-plugin-maximem/issues, which returns HTTP 404 ("Page not found · GitHub"). The repo lives under a personal-looking org (gauravmaximem) and carries a third product name, "moltbot," while the page's walkthrough actor is called "Vity" and the plugin is "OpenClaw."
Consequence: A developer hitting a bug has no working place to report it — the issues link is dead. The proliferation of unrebranded names (OpenClaw / Vity / moltbot / Maximem) makes it unclear which project they're even filing against.
The fix: Point the link at a real, official issues tracker, and rename the walkthrough consistently to the shipped product name.
18. "Verify installation" expected output doesn't match what the script prints (minor)
Location: /setup/installation
Problem: The verify-installation script prints [OK] SDK initialized successfully / [OK] Connected to Synap / [OK] SDK shut down cleanly. The "Expected output" block shows a different middle line — [OK] Connected to instance: inst_a1b2c3d4e5f67890 — which the script never emits.
Consequence: A developer comparing their terminal output to the documented expected output sees a mismatch on the connection line and may think their install failed when it succeeded.
The fix: Make the expected-output block match the actual print statements (or update the script to print the instance ID if that's the intended UX).
19. The FAQ's "Error Handling" link points to the wrong page (minor)
Location: /resources/faq
Problem: The FAQ text "See Error Handling for the full reference" links the words "Error Handling" to /sdk/configuration instead of /sdk/error-handling.
Consequence: A developer clicking through for the error reference lands on the configuration page and has to hunt for the right one — small friction, but it's the canonical cross-reference for error handling.
The fix: Repoint the link to /sdk/error-handling.
20. The Discord community link doesn't resolve to a real server invite (minor)
Location: /resources/changelog
Problem: The changelog tells readers to "join the #releases channel on Discord for real-time updates." Per the captured evidence, discord.gg/synap resolves to Discord's generic marketing homepage rather than a named server invite — i.e. a likely invalid/expired invite.
Consequence: A developer who wants release notifications or community help clicks through to Discord's landing page instead of the Synap server and has no way in.
The fix: Replace the link with a current, named invite to the actual server and verify it resolves to the server (not Discord's homepage).
21. GitHub benchmark and integration claims could not be located in the captured docs (minor)
Location: github.com/maximem-ai (repo description) vs the captured docs pages
Problem: The maximem_synap_sdk repo blurb advertises "92% LongMemEval, 93.2% on LOCOMO" and lists native integrations limited to Python-ecosystem frameworks (LangChain, LlamaIndex, CrewAI, Google ADK, AutoGen, OpenAI Agents, Semantic Kernel, Haystack, Pydantic AI). Per the auditor's annotation, the docs additionally claim TypeScript-native packages (Mastra, Vercel AI SDK, Claude Agent SDK) that the repo list omits, and the benchmark numbers do not appear in the docs. Caveat: those docs-side integration claims and the absence of the benchmark numbers are from annotation [17]; no scraped docs page showing the TS-native claims, and no full doc-set diff, is in the captured evidence — so this is framed as "could not locate in captured docs" rather than asserted absence.
Consequence: A developer evaluating Synap may see headline benchmark numbers and a TypeScript-native integration list on GitHub that they can't corroborate in the docs, and gets a potentially inconsistent answer on which frameworks are supported per language.
The fix: Verify whether the benchmarks and TS-native integrations are documented; if real, surface them in the docs with methodology and reconcile the integration list so docs and repo advertise the same supported frameworks per language. Confirm against the full doc set before treating this as a true gap.
What they do well
- An
llms-full.txtexists and is substantive — agents can index the full doc set, and most pages expose canonical.mdendpoints (the/dashboard/overview.md200 confirms it). - The conceptual layer is genuinely clear — the five-identifier mental model (Client/Instance/Customer/User/Conversation), the three-layer SDK/Cloud/Dashboard split, and the control-plane vs data-plane framing are well explained.
- Platform constraints are stated plainly where it counts — the JS-SDK-is-a-Python-subprocess limitation and its unsupported-runtime list are documented explicitly rather than discovered the hard way.
Top 3 recommendations
- Establish single sources of truth for the reference layer. Generate the error table (wire code ⇄ HTTP ⇄ exception ⇄ message), the
document_typeenum, the webhook event catalog, and rate-limit/timeout config from one canonical definition each, so contradictions (findings 1, 6, 8, 10, 11, 13, 14) can't reappear page-by-page. - Fix the things that break copy-paste and install. Resolve the
retry_afterfield name, the>=0.2.0version pin, and theSynap*Errorexception names (findings 1–3) — these turn a working quickstart into a runtime error. - Ship or unlink the dashboard pages, and reconcile the OpenClaw plugin. Publish
/dashboard/webhooksand/dashboard/monitoring-and-analyticsor remove their inbound links (findings 4, 5), and unify the plugin'smx_/MAXIMEM_API_KEY/app.maximem.aischeme with the coresynap_scheme (findings 16, 17).