Blue Banana EHR
A sandbox EHR: FHIR R4 API, webhook triggers and an MCP server over synthetic Synthea data. Every resource carries the extension http://bluebananaehr.com/synthetic-data; there is no real PHI.
Open the API playground → (sign in once; a Playground key is minted for you)
Quickstart
- Sign up. A private tenant
dev-<id>is created and seeded with 3 near-future Appointments, 3 Practitioners with Schedules, and open Slots. - Mint an API key on /account. Copy the
bb_…value; it is shown once. - Read the reference patient:
curl -H "Authorization: Bearer bb_…" \ "https://bluebananaehr.com/api/fhir/r4/Patient/eb1f768e-226a-c047-0f2f-8331341167c2"
Auth
- Send
Authorization: Bearer bb_…on every API call. Exceptions:/api/fhir/r4/metadata,/api/webhooks/echo,/f/{token}and/api/forms/submit(the signed token is the credential). - The key decides the tenant. There is no tenant header or parameter.
- Responses echo the tenant in
X-EHR-Tenant-Resolved. - MCP clients can use OAuth instead; the access token is a new API key named
MCP · <client>, listed and revocable on/account.
Limits
| Scope | Limit | When exceeded |
|---|---|---|
| Named API key (minted on /account) | 30 req/min | 429 with Retry-After + X-RateLimit-* |
| Playground key (auto-managed by /docs/api) | 10 req/min | 429 with Retry-After + X-RateLimit-* |
| MCP OAuth key (MCP · <client>) | 60 req/min | 429 with Retry-After |
| Tenant requests, all keys combined | 50 req/min | 429, no rate-limit headers |
| Tenant writes (create, update, patch, delete) | 2,000 lifetime | 429 until reset from /account (waiting does not reset it) |
| Webhook subscriptions per tenant (enabled or disabled) | 25 | 429 on create |
- Successful responses carry
X-RateLimit-Limit,X-RateLimit-RemainingandX-RateLimit-Reset(epoch seconds) for the key's window. - "Reset playground data" on
/accountdeletes your tenant's resources and subscriptions and resets the write counter.
Errors
Every error body is a FHIR OperationOutcome.
| Status | Meaning |
|---|---|
| 400 | Invalid JSON body, unknown or malformed search parameter, bad operation argument. |
| 401 | Missing, invalid or revoked API key. |
| 404 | Unknown resource type, or the resource is not visible to your tenant (never existed or deleted). |
| 409 | POST with an id that already exists. |
| 415 | PATCH with a content type other than the two patch types. |
| 422 | The patch could not be applied to the current version. |
| 429 | A rate limit or quota was hit — see Limits. |
Tenant model
- Readsmerge your tenant's overlay over the shared canonical seed pool. Your version of a resource wins.
- Writes go to your overlay only. The canonical seed is never modified and other tenants never see your writes.
- DELETE of a seed resource writes a tombstone in your overlay: it disappears for you and stays for everyone else. DELETE of your own resource removes it.
Writes
POSTcreates (server-assigned id unless the body has one).PUTupdates, or creates at that id.PATCHaccepts JSON Patch (application/json-patch+json) or JSON Merge Patch (application/merge-patch+json).- Bodies are normalised before storage:
null-valued properties are dropped, and doubled reference prefixes (Patient/Patient/123) collapse toPatient/123. - Every write sets
meta.versionId("1"on create, +1 per update) andmeta.lastUpdated. - Every write fires matching webhook subscriptions, whether it came from the REST API, MCP or the clinician workspace.
Surface map
| Path | What | Guide |
|---|---|---|
| /api/fhir/r4/* | FHIR R4 CRUD + PATCH + search on 43 resource types; _include (max 5); Patient/{id}/$everything; Appointment/$find (open slots). | API reference |
| /api/fhir/r4/metadata | CapabilityStatement. No auth. | Open |
| /api/subscriptions* | Webhook subscriptions (events, FHIR criteria, timers, cron), event catalogue, per-subscription delivery log. | Webhooks |
| /api/webhooks/echo | POST sink that answers 200 and stores nothing. Point a test subscription at it. | Webhooks |
| /api/eligibility/$check | Mock insurance eligibility check. | API reference |
| /api/forms/links, /api/forms/submit, /f/{token} | Patient forms: mint a signed 30-day link to a Questionnaire; submissions become QuestionnaireResponses. /f/{token} and submit need no key. | API reference |
| /api/testing/* | Graded test scenarios and evals for agents and workflows. | Testing |
| /api/mcp | MCP server (Streamable HTTP) for Claude, Codex and any MCP client. API key or OAuth. | MCP |
| Keragon connector | Every Blue Banana trigger and action in Keragon and the endpoint behind each. | Keragon |
| /openapi.json | OpenAPI 3.1 spec for every documented endpoint and outbound webhook. | Open |
| /llms.txt, /llms-full.txt | The docs in llms.txt format. | Open |