RRM Academy Agent API
v1.0.0 · OpenAPI 3.1.0
Programmatic access to the RRM Academy research library and editorial guardrails, exposed via the Model Context Protocol (MCP). thousands of peer-reviewed articles on restorative reproductive medicine, NaProTechnology, fertility awareness-based methods, endometriosis, PCOS, and related women's health topics. Clinical content curated under the direction of Dr. Naomi Whittaker, MD. ## Versioning & deprecation This API is unversioned-stable: existing paths and response fields change only additively (new optional fields, new endpoints). A breaking change ships under a new path prefix (for example `/v2/...`) rather than mutating an existing path. Deprecated endpoints continue to work during a wind-down window and are announced via a `Deprecation` response header (RFC 8594) and a `Sunset` header carrying the earliest removal date, with at least 90 days' notice. Changes are also published on the changelog/webhooks page at https://rrmacademy.org/connect/. No uptime or latency SLA is implied.
Server
https://mcp.rrmacademy.org Authentication
Bearer API key. Self-service issuance at /account/mcp-keys. Send as an HTTP header on every authenticated request:
Authorization: Bearer YOUR_API_KEY The /health endpoint is unauthenticated. All other endpoints require a valid key.
Endpoints
/api/ask NLWeb capability metadata
Returns machine-readable capability JSON describing the /api/ask endpoint: supported methods, auth model, streaming transport, request/response shape, and guardrails. Unauthenticated. Cacheable for 1 hour.
Responses
-
200Capability descriptor
/api/ask Conversational AI over the RRM Research Library
Answers questions scoped to restorative reproductive medicine using the RRM Library corpus. Two auth paths: **Session-authenticated (20 req/day):** Include the `session` cookie from `/api/auth/login`. Supports both JSON and SSE response formats based on the `Accept` header. **Anonymous (2 req/day/IP):** No cookie required. Always returns `text/event-stream`. Rate-limited per IP. Set `Accept: text/event-stream` to receive a streaming SSE response regardless of auth state.
Responses
-
200Answer with citations. Content-Type is `application/json` for authenticated requests without SSE Accept header, or `text/event-stream` for anonymous requests and any SSE-accepting request. -
400 -
422 -
429Rate limit exceeded. Session path: 20/day reset at midnight UTC. Anonymous path: 2/day, 48h TTL. -
503Upstream service unavailable
/api/articles List published library articles
Returns a paginated list of published, non-retracted research articles from the RRM Academy library. Public, unauthenticated. Rate limited to 30 requests per minute per IP. Responses are edge-cached for 1 hour. Two interchangeable pagination modes: classic `page`+`limit` (unchanged, still supported), or opaque `cursor`. Each response returns `nextCursor` (null when the list is exhausted); pass it back as `cursor` to fetch the next page. When `cursor` is supplied, `page` and `limit` query params are ignored (the cursor carries the limit). The cursor is an encoded offset over the library's default sort, not a keyset position, so it is stable within a request session but not a durable bookmark across corpus changes.
Responses
-
200Paginated article list. -
400Invalid pagination parameters (`invalid_pagination`) or an undecodable/out-of-range `cursor` (`invalid_cursor`). -
429Rate limited (30 req/min per IP) -
503Upstream library service unavailable
/api/articles/bulk Bulk-fetch articles by ID
Returns up to 50 published library articles by ID in a single call. Comma-separated IDs in the `ids` query parameter. Missing IDs are returned in `not_found[]` instead of producing a 404. Shares the 30 req/min IP budget with /api/articles.
Responses
-
200Bulk article results. Missing IDs appear in not_found[]. -
400Missing or malformed ids parameter. -
429Rate limited (30 req/min per IP, shared with /api/articles) -
503Upstream library service unavailable
/api/bulk Bulk-fetch articles by ID (top-level alias)
Top-level alias for /api/articles/bulk. Identical behavior. Shares the 30 req/min IP budget. Exists so URL-pattern scanners recognize the batch endpoint at /api/bulk.
Responses
-
200Bulk-fetch results (BulkArticleResponse shape) -
400Malformed `ids` query (regex mismatch). -
429Rate limited (30 req/min per IP) -
503Upstream library service unavailable
/api/ask/sandbox No-auth sandbox test surface for /api/ask
The public, no-auth, no-key test surface for the /api/ask contract. Live at POST https://rrmacademy.org/api/ask/sandbox. Returns a canned, deterministic response with no LLM invocation, no authentication, no rate limit, and no data mutation (safe to call from any client, any number of times). Accepts the same JSON shape as /api/ask but ignores the body. Use it to validate that your client correctly parses the AskResponse-shaped reply before integrating with the authenticated /api/ask endpoint. Set Accept: text/event-stream for the SSE variant. Every response carries the `X-Sandbox: true` header. Also described under `info.x-sandbox`.
Responses
-
200Canned sandbox response. Shape matches SandboxAskResponse.
/mcp Bearer Invoke an MCP tool
Model Context Protocol JSON-RPC 2.0 entry point. Dispatches to one of five tools: search, check_guardrails, check_facts, get_article, find_related. Use the MCP client libraries (@modelcontextprotocol/sdk) for idiomatic access rather than raw HTTP.
Responses
-
200JSON-RPC response envelope -
400 -
401Missing or invalid bearer token -
422 -
429Rate limited -
503Upstream unavailable
/health Health check
Returns 200 when the MCP server is reachable. Unauthenticated.
Responses
-
200OK
/api/contact/submit Submit a contact form message
Responses
-
200Success -
400 -
422 -
429Rate limit exceeded -
503Service unavailable
/api/newsletter/subscribe Subscribe an email address to the newsletter
Responses
-
200Success -
400 -
422 -
429Rate limit exceeded -
503Service unavailable
/api/community/posts Create a community post
Responses
-
200Success -
400 -
401Authentication required -
422 -
429Rate limit exceeded
/api/community/posts Edit a community post the caller authored
Responses
-
200Success -
400 -
401Authentication required -
403Caller does not own the post -
422
/api/community/posts Delete a community post the caller authored
Responses
-
200Success -
400 -
401Authentication required -
403Caller does not own the post -
422
/api/community/comments Create a comment on a community post
Responses
-
200Success -
400 -
401Authentication required -
422 -
429Rate limit exceeded
/api/community/comments Edit a comment the caller authored
Responses
-
200Success -
400 -
401Authentication required -
403Caller does not own the comment -
422
/api/community/comments Delete a comment the caller authored
Responses
-
200Success -
400 -
401Authentication required -
403Caller does not own the comment -
422
/api/community/reactions Add a reaction to a post or comment
Responses
-
200Success -
400 -
401Authentication required -
422 -
429Rate limit exceeded
/api/community/reactions Remove a reaction the caller added
Responses
-
200Success -
400 -
401Authentication required -
422
/api/saved Save a library article to the caller's collection
Responses
-
200Success -
400 -
401Authentication required -
422
/api/saved Remove a library article from the caller's collection
Responses
-
200Success -
400 -
401Authentication required -
422
/api/auth/signup Register a new free RRM Academy account
Creates a user account and sends an email-verification magic link. Free registration; no payment. Public endpoint (no bearer auth). Protected by IP rate limiting (5 attempts / 15 min) and a Cloudflare Turnstile bot check. For anti-enumeration the endpoint returns the same 201 success shape whether or not the email is already registered.
Responses
-
201Account created (or, for an existing email, the anti-enumeration equivalent). Sets session and auth-hint cookies via Set-Cookie. An email-verification link is sent to the address. -
400Invalid JSON, invalid payload, failed field validation, weak or wrong-length password (8-128 chars required), or rejected email (disposable domain / bad MX). May include a `suggestion` for a likely email typo. -
403Turnstile bot check failed or verification service misconfigured. -
429Too many signup attempts from this IP. -
500Unexpected server error. -
503Email service temporarily unavailable, or client IP could not be determined.
/api/auth/verify-email Render the email-verification confirm page
Side-effect-free confirmation page for the verification magic link (GET does not consume the token, so email scanners and link prefetchers cannot silently verify). Returns an HTML page with a POST form. The `token` is the 32-64 lowercase-hex single-use token from the verification email.
Responses
-
200Confirm page (HTML) with a form that POSTs the token. -
400Malformed or missing token (HTML page). -
410Token expired or already consumed (HTML page).
/api/auth/verify-email Confirm an email address (consume the verification token)
Magic-link branch: submit the token as `application/x-www-form-urlencoded` (field `token`). On success the single-use token is consumed, the user is marked verified, a session is minted, and the response is a 303 redirect to /account/ with Set-Cookie. A dormant legacy branch accepts `application/json` `{ "code": "..." }` with an authenticated session cookie and returns `{ "ok": true }`. Rate limited (20 form attempts / 15 min per IP).
Responses
-
200Legacy JSON branch succeeded, or the link was already used. -
303Magic-link branch succeeded: redirect to /account/ with session cookies set. -
400Malformed token (HTML) or invalid legacy code/JSON (JSON AuthError). -
401Legacy branch: no authenticated session. -
410Verification token expired (HTML page). -
429Too many verification attempts.
Code samples
Discover the available tools, then call one. Replace YOUR_API_KEY with a key from /account/mcp-keys.
curl
# 1. Initialize the session
curl -X POST https://mcp.rrmacademy.org/mcp \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": { "name": "my-app", "version": "0.1" }
}
}'
# 2. List the available tools
curl -X POST https://mcp.rrmacademy.org/mcp \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }'
# 3. Call a tool (search the library)
curl -X POST https://mcp.rrmacademy.org/mcp \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "search",
"arguments": { "query": "endometriosis excision outcomes" }
}
}' Python (official MCP SDK)
# pip install "mcp[cli]"
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async def main():
headers = {"Authorization": "Bearer YOUR_API_KEY"}
async with streamablehttp_client(
"https://mcp.rrmacademy.org/mcp",
headers=headers,
) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
result = await session.call_tool(
"search",
{"query": "endometriosis excision outcomes"},
)
print(result)
import asyncio; asyncio.run(main()) Node.js (official MCP SDK)
// npm install @modelcontextprotocol/sdk
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const transport = new StreamableHTTPClientTransport(
new URL("https://mcp.rrmacademy.org/mcp"),
{
requestInit: {
headers: { Authorization: "Bearer YOUR_API_KEY" },
},
},
);
const client = new Client({ name: "my-app", version: "0.1.0" });
await client.connect(transport);
const tools = await client.listTools();
const result = await client.callTool({
name: "search",
arguments: { query: "endometriosis excision outcomes" },
});
console.log(result); Errors
All authenticated endpoints share the same error envelope:
{
"error": "machine_readable_code",
"message": "Human-readable explanation"
} 401— missing or invalid bearer token429— rate limited; back off and retry503— upstream unavailable; transient
Schemas
Full request/response shapes (JSON Schema) for the MCP envelope and error type are in the raw spec. The MCP SDKs handle envelope serialization for you — most users never need to construct JSON-RPC envelopes by hand.
Discovery files
- mcp.json descriptor — server metadata for agent discovery
- server card — full tool list with schemas
- RFC 9728 protected-resource metadata
- RFC 9727 API catalog
- llms.txt · llms-full.txt