Developer · API Reference

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

Base URL https://mcp.rrmacademy.org
Description Apex MCP server (high-level RAG, semantic search, editorial guardrails). Bearer auth, self-service keys at https://rrmacademy.org/account/mcp-keys.

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

GET /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

  • 200 Capability descriptor
POST /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

  • 200 Answer 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
  • 429 Rate limit exceeded. Session path: 20/day reset at midnight UTC. Anonymous path: 2/day, 48h TTL.
  • 503 Upstream service unavailable
GET /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

  • 200 Paginated article list.
  • 400 Invalid pagination parameters (`invalid_pagination`) or an undecodable/out-of-range `cursor` (`invalid_cursor`).
  • 429 Rate limited (30 req/min per IP)
  • 503 Upstream library service unavailable
GET /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

  • 200 Bulk article results. Missing IDs appear in not_found[].
  • 400 Missing or malformed ids parameter.
  • 429 Rate limited (30 req/min per IP, shared with /api/articles)
  • 503 Upstream library service unavailable
GET /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

  • 200 Bulk-fetch results (BulkArticleResponse shape)
  • 400 Malformed `ids` query (regex mismatch).
  • 429 Rate limited (30 req/min per IP)
  • 503 Upstream library service unavailable
POST /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

  • 200 Canned sandbox response. Shape matches SandboxAskResponse.
POST /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

  • 200 JSON-RPC response envelope
  • 400
  • 401 Missing or invalid bearer token
  • 422
  • 429 Rate limited
  • 503 Upstream unavailable
GET /health

Health check

Returns 200 when the MCP server is reachable. Unauthenticated.

Responses

  • 200 OK
POST /api/contact/submit

Submit a contact form message

Responses

  • 200 Success
  • 400
  • 422
  • 429 Rate limit exceeded
  • 503 Service unavailable
POST /api/newsletter/subscribe

Subscribe an email address to the newsletter

Responses

  • 200 Success
  • 400
  • 422
  • 429 Rate limit exceeded
  • 503 Service unavailable
POST /api/community/posts

Create a community post

Responses

  • 200 Success
  • 400
  • 401 Authentication required
  • 422
  • 429 Rate limit exceeded
PATCH /api/community/posts

Edit a community post the caller authored

Responses

  • 200 Success
  • 400
  • 401 Authentication required
  • 403 Caller does not own the post
  • 422
DELETE /api/community/posts

Delete a community post the caller authored

Responses

  • 200 Success
  • 400
  • 401 Authentication required
  • 403 Caller does not own the post
  • 422
POST /api/community/comments

Create a comment on a community post

Responses

  • 200 Success
  • 400
  • 401 Authentication required
  • 422
  • 429 Rate limit exceeded
PATCH /api/community/comments

Edit a comment the caller authored

Responses

  • 200 Success
  • 400
  • 401 Authentication required
  • 403 Caller does not own the comment
  • 422
DELETE /api/community/comments

Delete a comment the caller authored

Responses

  • 200 Success
  • 400
  • 401 Authentication required
  • 403 Caller does not own the comment
  • 422
POST /api/community/reactions

Add a reaction to a post or comment

Responses

  • 200 Success
  • 400
  • 401 Authentication required
  • 422
  • 429 Rate limit exceeded
DELETE /api/community/reactions

Remove a reaction the caller added

Responses

  • 200 Success
  • 400
  • 401 Authentication required
  • 422
POST /api/saved

Save a library article to the caller's collection

Responses

  • 200 Success
  • 400
  • 401 Authentication required
  • 422
DELETE /api/saved

Remove a library article from the caller's collection

Responses

  • 200 Success
  • 400
  • 401 Authentication required
  • 422
POST /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

  • 201 Account 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.
  • 400 Invalid 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.
  • 403 Turnstile bot check failed or verification service misconfigured.
  • 429 Too many signup attempts from this IP.
  • 500 Unexpected server error.
  • 503 Email service temporarily unavailable, or client IP could not be determined.
GET /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

  • 200 Confirm page (HTML) with a form that POSTs the token.
  • 400 Malformed or missing token (HTML page).
  • 410 Token expired or already consumed (HTML page).
POST /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

  • 200 Legacy JSON branch succeeded, or the link was already used.
  • 303 Magic-link branch succeeded: redirect to /account/ with session cookies set.
  • 400 Malformed token (HTML) or invalid legacy code/JSON (JSON AuthError).
  • 401 Legacy branch: no authenticated session.
  • 410 Verification token expired (HTML page).
  • 429 Too 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 token
  • 429 — rate limited; back off and retry
  • 503 — 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