{
  "openapi": "/api/openapi.json",
  "pages": [
    {
      "title": "API reference",
      "url": "/docs/api/",
      "format": "mdx",
      "content": "# API reference\n\n<a href=\"/docs/explorer/\">Open the interactive API explorer</a> to browse every published operation, inspect its request schema, and execute requests. <a href=\"/api/openapi.json\">Download the OpenAPI 3.1 JSON specification</a> to import it into an API client or give it to an agent. The specification is also available at `/openapi.json`.\n\nThese are synthetic simulation contracts. The NHS adapters implement the documented subset, not production NHS wire compatibility. CIS2 credentials do not grant access to team records.\n\n## Find the contract\n\n| Reference | Contents |\n| --- | --- |\n| <a href=\"/docs/explorer/\">Interactive explorer</a> | Operations grouped by service, editable requests, responses, and authentication controls |\n| <a href=\"/api/openapi.json\">OpenAPI JSON</a> | Machine-readable paths, parameters, schemas, examples, and security requirements |\n| <a href=\"/api/catalogue\">Runtime catalogue</a> | Published workplaces, NHS adapters, scenarios, and documentation links |\n| <a href=\"/docs/handbook.json\">Handbook JSON</a> | Every published guide with its title, URL, source format, and content for agents |\n| [First API workflow](/docs/quickstart/) | Create a world, find a patient, and submit an action |\n\nThe explorer uses this website's origin. Requests on the local simulator affect local worlds. Requests on `sim.animahacks.com` affect hosted worlds.\n\n## Authenticate a request\n\nCopy your existing key from **Team & API key** in any workspace. In the explorer, select **Authorize** and enter the key into the team bearer scheme. Enter the key without the `Bearer` prefix. Expand an operation, choose **Try it out**, fill its parameters, and select **Execute**.\n\nOutside the explorer, send `Authorization: Bearer YOUR_TEAM_KEY`. A key belongs to one world. It can also have service restrictions. The explorer clears authorization on reload.\n\n`POST /api/keys` is public. It creates or joins a team by name, normalised to lowercase with all whitespace removed. Repeating a name returns the same world and reusable `apiKey`. Anyone who knows the name can join its simulation world. Omit `site` when creating a team to access all API-enabled services; joining preserves its existing scopes. Each deployment supports up to 5,000 distinct team worlds, and existing teams can still rejoin at capacity. There is no per-IP or time-based sign-up throttle, so participants can create teams together on shared hackathon Wi-Fi.\n\nOperator operations require the separately configured operator token. List named teams with `GET /api/control/teams`, then inspect `GET /api/control/teams/{world}/activity` using a returned world ID. Existing clock, incident and agent operations select their world with `?world=...`. A team key cannot select another team's world. CIS2 access tokens are used only for the emulated identity endpoints.\n\n`POST /api/control/teams/{world}/session` lets an organiser obtain a participant key for that existing world without changing its permissions or resetting its records. The [organiser console](./organisers.mdx) provides team selection, request history, affected patients and an explicit return path after exploring as a team.\n\n## Read records and submit actions\n\n`GET /api/sites/{site}/patients` searches and pages through the team's patients. Use the returned patient identifier with `GET /api/sites/{site}/view?patient=SIM-000001`. This view includes the patient's visible records and service resources without a patient.\n\nThe resource view defaults to 500 resources. Use `limit` from 1 through 500 and advance `offset` to read more. Check `resourceTotal`, `resourceOffset`, and `resourceLimit`. A view is a projection of visible records, not an export of the complete world or its scheduled jobs.\n\n`POST /api/sites/{site}/actions` submits a typed action. The explorer's schema and examples describe the action fields. Use the relevant service and workflow guide. An action can create work for another service, so its response does not necessarily represent a completed patient journey.\n\nFor a versioned update, send the resource's current `version` as `expectedVersion`. Send an `Idempotency-Key` header when retrying the same logical action. The `clientRequestId` action field provides a body-based alternative. If both are present, the header takes precedence. Reuse the original request and key after an uncertain network response. A changed request with the same key returns a conflict.\n\n`POST /api/clock` controls only the authenticated team's clock. Send `paused: true` with `advanceMinutes` to pause and process scheduled results, visits, deliveries, and other activity. Stepping a running clock without pausing returns `409`. The explorer executes real changes in that synthetic world.\n\n## Raw secondary care records\n\nThe **Secondary care** Explorer group publishes `GET /api/sites/hospital/consultations` and `GET /api/sites/hospital/genomes`. Both require `hospital` scope. They preserve the stored resource payload, including consultation text and genomic SNP calls, with versions and provenance.\n\nBoth operations accept an exact `patient` filter and `offset` and `limit` pagination. Responses contain `items`, `total`, `offset`, `limit`, and `now`. The default page size is 100, with a maximum of 500. Team keys always access their own world.\n\nSee [clinical and genomic records](/docs/clinical-records/) for included consultation kinds, synthetic CYP2C19 data, and the hospital-only access boundary.\n\n## Wearable data\n\nThe **Wearables** Explorer group publishes the Home Health records in the authenticated team's world. Both operations require `wearables` scope.\n\n| Operation | Records | Filters |\n| --- | --- | --- |\n| `GET /api/sites/wearables/devices` | Connected synthetic devices, including battery and sync data where recorded | `patient` |\n| `GET /api/sites/wearables/readings` | Wearable observations with recorded values, units, quality, and timestamps | `patient`, `metric` |\n\nBoth responses contain `items`, `total`, `offset`, `limit`, and `now`. Pagination defaults to 100 records, with a maximum `limit` of 500. `total` counts all matching records before pagination. The optional `patient` filter matches an exact SIM identifier. The optional `metric` filter matches an exact recorded metric, such as `steps`, `heart-rate`, or `sleep`.\n\nEach item uses the simulator's resource envelope, with wearable fields inside `data`. Timestamps use Unix milliseconds. Disconnected devices can produce readings with `data.value: null` and `data.quality: \"missing\"`. These endpoints contain synthetic wearable records; laboratory results remain separate.\n\nThe [Home Health guide](/docs/home/) includes the watch connection and first-reading workflow. These operations and their schemas also appear in the downloadable OpenAPI document.\n\n## Follow a workflow\n\n- [GP Records](/docs/systemtwo/) covers GP consultations and patient records.\n- [Appointment book](/docs/appointments/) covers session slots, bookings, and attendance.\n- [Clinical records and hospital orders](/docs/clinical-records/) covers signed notes, addenda, prescriptions, and laboratory requests.\n- [Document processing](/docs/documents/) covers hospital discharge letters and GP filing.\n- [Practice and patient messaging](/docs/messaging/) covers role-specific messaging APIs, patient-initiated conversations, delivery, and scripted automatic replies.\n- [Home Health and wearable data](/docs/home/) covers connected devices, recorded readings, and simulated watch updates.\n- [Pharmacy](/docs/pharmacy/) covers referrals, dispensing, supplier quotes, purchasing, and stock costs.\n- [FHIR-shaped APIs](/docs/fhir/) covers supported demographics and organisation search.\n- [Legacy browser integration](/docs/legacy/) covers the HTML-only service and session cookies.\n- [Organiser controls](/docs/organisers/) covers administration and the configured model agent.\n\n## Handle errors\n\nRead the status code and response body. Most simulation errors use JSON with an `error` message. FHIR endpoints use their documented `OperationOutcome` responses, and CIS2 uses OAuth errors where appropriate.\n\n| Status | Typical cause | Next step |\n| --- | --- | --- |\n| `400` | Invalid input or malformed JSON | Correct the request using the operation schema |\n| `401` | Missing or invalid credentials | Check the authentication scheme and key |\n| `403` | Wrong scope, ownership, or operator permission | Use an authorized service and credential |\n| `404` | Unknown endpoint or missing resource | Check the path and the current world |\n| `409` | Stale version, changed retry payload, or invalid state transition | Refresh the record and resolve the conflict |\n| `413` | Request body exceeds the server limit | Reduce the payload |\n| `429` | The deployment has reached its 5,000-team cap | Contact the organiser |\n| `501` | A deliberately unsupported API capability | Use the documented alternative |\n| `503` | Simulated identity unavailability | Check the configured CIS2 scenario |\n\n## Keep integrations aligned\n\nThe JSON specification is generated from the implemented route catalogue and shared validation schemas where available. Repository tests check route coverage. The build packages the explorer with its JavaScript and CSS, and smoke checks verify that the published reference and explorer remain accessible.\n\nFor production NHS integration requirements, use the [NHS API catalogue](https://digital.nhs.uk/developer/api-catalogue). This simulator does not need live NHS credentials."
    },
    {
      "title": "Appointment sessions and slots",
      "url": "/docs/appointments/",
      "format": "mdx",
      "content": "Open **Appointment book** in GP Records. Each clinician has AM and PM sessions containing timed slots. The calendar, clinician filter and AM/PM controls change the diary view. All times use the team's simulation clock in UTC.\n\n## Book and manage a slot\n\nSelect an **Available** slot. Search for a patient by name or SIM identifier, enter the appointment reason and choose **Book appointment**. The session supplies the clinician, location, contact type and duration. Booked slots show the patient's name and appointment reason.\n\nSelect a booking to mark the patient as arrived, complete the appointment or cancel it. Cancelling makes the slot available again. Completed appointments remain in the diary as history. Bookings preserve the team that made each change.\n\n## Protected time and sessions\n\nSelect a free slot and expand **Block this slot** to record protected time, such as a break or administrative work. Select a blocked slot to unblock it. A booked slot cannot be blocked, and a blocked slot cannot be booked.\n\nThe initial diary contains seven days of GP, telephone and practice-nurse sessions. Use **Create session** to add more dates or clinicians. Sessions for the same clinician cannot overlap. The hours must divide into whole slots. Existing appointments outside session hours remain listed beneath the diary.\n\n## Agent API\n\n`GET /api/sites/gp/appointments?date=2026-09-12` returns `sessions`, `appointments` and patient names for the selected date. Use your team's actual simulation date.\n\nPost to `/api/sites/gp/actions`:\n\n- `create_appointment_session`: supply `title`, `clinician`, `location`, `startsAt`, `endsAt`, `slotMinutes` and `mode`.\n- `book_appointment`: supply `sessionId`, `sessionVersion`, `startsAt`, `patientId` and `title`.\n- `set_appointment_slot`: supply the session `resourceId`, `expectedVersion`, slot `startsAt`, `slotCommand` of `block` or `unblock`, and a `text` reason when blocking.\n- `arrive_appointment`, `complete` and `cancel_appointment`: supply the appointment `resourceId` and `expectedVersion`.\n\nTimestamps are Unix milliseconds. Slot booking validates the session grid, protected time and existing bookings on the server. Session availability supplies its own capacity; the aggregate capacity counter continues to apply to older bookings made without a session. Use an idempotency key when retrying mutations."
    },
    {
      "title": "Follow one patient across services",
      "url": "/docs/challenges/",
      "format": "mdx",
      "content": "# Follow one patient across services\n\nStart at the neighbourhood map. Riverside Practice opens GP Records for primary care. Northbank General opens Hospital EPR for secondary care. Use the same team key in both.\n\nCommunity and pharmacy have their own workspaces and scoped APIs. Diagnostics and referrals expose supporting APIs. Query those services when you need to distinguish a completed handover from a request.\n\n## Discharge to primary care\n\nUse `SIM-000001`. Inspect the hospital document in Hospital EPR and the follow-up task in GP Records. Check which records each service can see. Use `share_record` when the receiving service needs a document.\n\nOrder monitoring through `/api/sites/gp/actions` with `order_test`. Advance the world by 121 minutes through `/api/clock`, then read `/api/sites/diagnostics/view?patient=SIM-000001` and confirm the test is available. A pathology outage can hold a result in diagnostics until the organiser restores the feed.\n\nThe original document, receiving service's visible copy and final test status can be inspected separately.\n\n## Medicines and support at home\n\nDraft a prescription with `draft_prescription` from GP or hospital. Its owner is pharmacy. GP can retrieve its prescriptions at `/api/sites/gp/prescriptions` and perform `review` then `accept` through `/api/sites/gp/actions` for prescriptions originally drafted by GP. Send the latest `expectedVersion` with each GP change. Read the product catalogue at `/api/sites/pharmacy/pharmacy-workspace`. Use `link_prescription_stock` to choose a matching `productId` and `quantity` in units. If the prescription has not been approved, use `/api/sites/pharmacy/actions` to `review` then `accept` it. For an approved prescription, continue with `dispense` then `collect` through that endpoint. Send its current `expectedVersion` with each action. Acceptance changes it to `approved`; dispensing deducts the linked quantity from stock. Use a team key with pharmacy scope.\n\nFor `SIM-000006`, inspect the support plan through `/api/sites/community/view?patient=SIM-000006`. Create a `schedule_visit` action from GP or hospital and advance the clock by 91 minutes. Confirm the visit completed in the community projection. Exhausted community capacity rejects new bookings with HTTP 409.\n\nShow medicine status and visit completion separately. Collection does not establish adherence, and a simulated visit does not establish a real clinical outcome.\n\n## Resolve a rejected referral\n\nUse `SIM-000002`. Read the referral and diagnostics projections. Retrieve the additional letter through the [legacy browser](/docs/legacy/). Share the evidence needed by the receiving service, then progress the original referral through `/api/sites/referrals/actions`.\n\nThe original referral identifier stays the same as its status changes.\n\n## Test an identity integration\n\nUse the <a href=\"/cis2/\">CIS2 emulator</a> for a separate staff sign-in exercise. Its identity tokens and a team's API key have different purposes. See the [API reference](/docs/api/) for the implemented protocol and operator controls.\n\n## What comes next\n\nThe EPRs, pharmacy dispensing bench and community visit board demonstrate handovers and support after discharge. The current simulation has finite visit capacity and a prescription lifecycle, but does not model travel, home access, medication adherence or clinical outcomes.\n\nKeep the seed and intervention sequence fixed when comparing runs. Present initial records, actions, rejected requests and final states. Test the expected number of concurrent team worlds before the event.\n\n\n## Try a complete journey in ten minutes\n\n1. Create a team from the map. In GP Records, choose Amira Khan, `SIM-000001`, and send a referral to the hospital.\n2. Open Hospital EPR from the map. Select Amira and find your referral in **Handover**.\n3. Open High Street Pharmacy. Select the discharge prescription, link its matching catalogue item and quantity, dispense it, and confirm collection. A newly drafted prescription must first be reviewed and approved.\n4. Open Neighbourhood Care, select Amira and schedule a home visit. Open Simulation controls and advance 121 minutes. The visit changes to completed after its 90-minute delay.\n5. Open At home from the map and choose a resident by name or patient ID. Connect a simulated watch if they do not have one. The first activity reading is due after 10 simulation minutes. Use **+15 minutes** to observe it; subsequent readings arrive hourly. Change resident to explore another person's records.\n\nThese steps introduce the available controls. Choose your own problem from the [ten-year plan](/docs/ten-year-plan/) when deciding what to build.\n\nThe home readings are authored synthetic examples. They do not provide health assessments. A new team includes the wearable history and scope; older teams keep their original data and permissions. If your old key lacks wearable access, create a new team to try the whole journey."
    },
    {
      "title": "Clinical records and hospital orders",
      "url": "/docs/clinical-records/",
      "format": "mdx",
      "content": "GP Records's **Journal** shows dated contacts with their recorded narrative and authors. Search includes note text. Expand **Record details and activity** to inspect the history or open an action.\n\n## Hospital documentation\n\nOpen a patient in Hospital EPR and choose **Documentation** or **New note**. Select a free-text, history-and-physical or progress-note template. The section navigator moves between parts of the note.\n\n**Save draft** stores an editable note. **Review & sign** previews the note before **Confirm & sign** records the signature. Signed text is preserved. Use **Add addendum** to append further information with its own attribution. Drafts and signed notes remain in the documentation list after reload.\n\n## Prescriptions and blood tests\n\n**Prescription** opens the medication catalogue and order details. Enter the medicine, dose, unit, route, frequency, duration, quantity and indication. Review the details before sending the draft to pharmacy. Pharmacy review and approval are still required before dispensing.\n\n**Request test** opens the laboratory catalogue. Choose a supported panel, specimen, priority, collection round and clinical details. Review and sign the request. Results become available after 120 simulation minutes for collection now, or 240 minutes for the next round.\n\nThe **Orders** view retains the submitted details. **Results** shows measurements against sample dates. Use the panel navigator to filter the grid, or select measurements and choose **Graph selected** to inspect trends. Values and reference intervals are synthetic.\n\n## Agent requests\n\nThese workflows use `POST /api/sites/hospital/actions` with the team's API key. Patient records are read through `GET /api/sites/hospital/view?patient=SIM-000006`.\n\nThe **Secondary care** group in the <a href=\"/docs/explorer/\">API explorer</a> also exposes raw records:\n\n- `GET /api/sites/hospital/consultations?patient=SIM-000006` returns hospital-owned consultation records, including saved note sections, full text, signatures, and addenda where recorded.\n- `GET /api/sites/hospital/genomes?patient=SIM-000006` returns the resident's raw synthetic genomic records.\n\nBoth require `hospital` scope and return `items`, `total`, `offset`, `limit`, and the simulation time `now`. Each item retains its resource ID, version, original `data`, and provenance. Use `limit` from 1 to 500 and increase `offset` to page through results. The default limit is 100. Omitting `patient` reads across the team's world. An unknown patient returns an empty list.\n\nThe consultation endpoint includes hospital-owned `hospital-note`, `clinical-note`, `consultation`, and `encounter` records. Their data shapes depend on the record kind. GP consultations and discharge-summary documents are separate records.\n\n## Synthetic genomic records\n\nEvery synthetic patient receives a hospital-only CYP2C19 SNP panel for the clopidogrel integration scenario. The panel contains unphased genotype calls for `rs4244285`, `rs4986893`, and `rs12248560`. The SNP identifiers and reference alleles follow [PharmVar's CYP2C19 catalogue](https://www.pharmvar.org/gene/cyp2c19). Patient genotypes are deterministic synthetic fixtures, unrelated to real patients or population frequencies.\n\n`data.variants` contains each SNP's identifier, reference allele, alternate allele, and diploid genotype. The panel labels itself as synthetic and identifies its source. It is a targeted SNP fixture, not a whole-genome sequence, inferred haplotype, or prescribing recommendation.\n\nExisting populations and team-specific patients receive missing panels through a transactional startup migration. Repeated startup preserves existing records. New patients receive a panel when generated. Genome records remain owned by and visible to `hospital`; generic actions cannot edit or share them with another service. Operator access remains available for simulation administration.\n\nThe <a href=\"/api/openapi.json\">OpenAPI document</a> describes the response schemas. The genome endpoint returns `genome-record` resources. The older `genomic-test` workflow stub is not a raw SNP record.\n\n## Submit hospital actions\n\n- Notes use `type: \"hospital_note\"` and `hospitalNoteCommand`. The command is `{kind: \"save\", template, sections}` for drafts, `{kind: \"sign\"}` for signing, or `{kind: \"addendum\", text}` for additions. Each section contains `id`, `heading` and `text`. Creation also requires `patientId` and `title`; edits require `resourceId` and `expectedVersion`.\n- Prescriptions use `type: \"draft_prescription\"`, `patientId`, `title` and `medicationOrder`, containing `drug`, `dose`, `unit`, `route`, `frequency`, `duration`, `quantity` and `indication`.\n- Laboratory requests use `type: \"order_test\"`, `patientId`, `title` and `bloodTestOrder`, containing `panel`, `panelId`, `specimen`, `priority`, `collection` and `clinicalDetails`. Supported panel IDs are `fbc`, `ue`, `hba1c`, `lft`, `crp` and `lipids`. Collection is `now` or `next-round`.\n\nSupply an `Idempotency-Key` header or a `clientRequestId` UUID and reuse it for an identical retry. Stale edits and attempts to overwrite signed notes fail without changing the record.\n\n\n## Deceased synthetic patients\n\nPatient records include an optional `death` object with the date, authored cause, `synthetic: true`, and source `authored-synthetic-mortality-v1`. Its presence means the patient is deceased. GP and hospital records show the status, date, cause, and age at death. Existing consultation and medication history remains available.\n\nThe fixtures are `SIM-000009` (road traffic collision), `SIM-000011` (pneumonia), `SIM-000012` (sepsis), `SIM-000018` (cancer), and `SIM-000019` (stroke). Names, dates of birth, and ages can differ between populations. Read the current patient record for those values. These causes are authored simulation data.\n\nThe startup migration adds the fixtures to existing shared populations and patient overlays without changing demographics or replacing existing death records. New generated populations receive the same fixture IDs. Deceased patients are excluded from future generated demand and new telephone calls. Patient APIs retain the `death` object; PDS FHIR Patient responses include `deceasedDateTime`. FHIR `active` continues to describe record availability.\n\n## Issue and approve primary care prescriptions\n\nUse a team key with `gp` scope. All records are synthetic.\n\n1. POST to `/api/sites/gp/actions` with `type: \"draft_prescription\"`, `patientId`, `title`, and the `medicationOrder` fields listed above. The response contains the prescription `id`, `version`, and `status: \"draft\"`.\n2. Retrieve prescriptions with `GET /api/sites/gp/prescriptions?patient=SIM-000001&offset=0&limit=100`. The response contains `items`, `total`, `offset`, and `limit`. Each item includes its medication details, status, version, and provenance. Omit `patient` to list all GP-visible prescriptions. The maximum `limit` is 500.\n3. POST to `/api/sites/gp/actions` with `type: \"review\"`, `resourceId`, and the latest `expectedVersion`. The status becomes `reviewed`.\n4. POST to the same endpoint with `type: \"accept\"`, `resourceId`, and the version returned by review. The status becomes `approved`.\n\nGP can review and approve prescriptions originally issued through GP. Sharing a hospital prescription with GP permits retrieval but does not grant approval permission. Pharmacy retains ownership and handles stock linking, dispensing, and collection. A GP-only key can complete issuance and approval without pharmacy scope.\n\nApproval before review and stale versions return `409`. Missing versions on GP review or approval return `400`. Unauthorized service changes return `403`. Send an `Idempotency-Key` header when creating or changing a prescription to retry that exact request safely."
    },
    {
      "title": "Arrange community visits in Community Care",
      "url": "/docs/community/",
      "format": "mdx",
      "content": "# Arrange community visits in Community Care\n\nOpen [Neighbourhood Care](/community/) with your team's key. Community Care shows upcoming visits, care plans, and the selected person's recorded needs and goals.\n\n## Arrange a visit\n\n1. Find a person in **Patient directory**.\n2. Choose **Arrange a visit** and enter its purpose.\n3. Choose **Schedule visit**. The visit appears in your team's records with a due time 90 simulation minutes later.\n4. Open **Simulation controls** and advance time past the due time.\n5. Select **Completed** to inspect the completed visit and its attribution.\n\nYou can also choose **Complete visit** to record completion manually. **All visits** includes the earlier history. Scheduling consumes community capacity, so an exhausted allocation rejects a new visit rather than creating it silently.\n\nA GP or hospital **Home visit** request enters the same community workflow. Use the same patient ID to inspect the handover across services. The visit board does not calculate travel routes, access to the home, or clinical outcomes.\n\n## Connect an agent\n\nRead `GET /api/sites/community/view?patient=SIM-000006` with your team's bearer key. Submit `type: \"schedule_visit\"`, `patientId`, and `title` to the GP, hospital, or community action endpoint. Complete a visit through the community action endpoint using `type: \"complete\"`, `resourceId`, and `expectedVersion`.\n\nInspect the resulting resource after time advances; successful scheduling does not mean the visit has completed. The <a href=\"/docs/explorer/\">API explorer</a> documents these actions and the clock endpoint."
    },
    {
      "title": "The data in your world",
      "url": "/docs/data/",
      "format": "markdown",
      "content": "# The data in your world\n\nEvery person in NHS-SIM is fictional. Patient identifiers start with `SIM-`. The repository does not contain real patient records or production NHS credentials.\n\n## Patient and record shapes\n\nA patient has an `id`, `name`, `birthDate`, service-specific `localIds`, `conditions`, `needs`, `goals`, and `synthetic: true`.\n\nA resource has an `id`, optional `patientId`, `kind`, `title`, `status`, `owner`, `visibleTo`, `priority`, timestamps, `data`, and `version`. Authored changes also record provenance and audit history. The owning service controls its transitions. Other services see it only when visibility permits.\n\n`GET /api/sites/{site}/patients` returns a paginated demographic directory. `GET /api/sites/{site}/view` returns the service's visible records and current operational state. The resource view defaults to at most 500 records. Use `limit` and `offset`, and inspect `resourceTotal`, `resourceOffset`, and `resourceLimit` when reading additional pages. The view does not expose hidden scheduled events.\n\n## Separate team worlds\n\nA new normalised team name creates a world. Reusing that name joins its existing world and returns its reusable key. Names are lowercase with whitespace removed and act as shared join codes. Portal activity and API actions using that key affect the same world. Distinct team names keep separate worlds.\n\nThe simulation clock controls delayed events. A test order schedules a result. A community visit schedules a completion. Read the resulting resource after advancing time to check whether it completed.\n\n## Record authorship\n\nRecord details distinguish the original author from later changes. Portal actions and API actions use the acting team's name. A named assignee records responsibility without impersonating that person. Seeded records identify their synthetic source or fictional author. Legacy records without an author retain an explicit unknown-author label rather than an invented identity.\n\nHospital notes preserve their signed text. Later corrections are separate attributed addenda. GP consultation edits update the version and retain their audit history.\n\nOrganisers can inspect authenticated team API calls and team-attributed patient changes. The request log records wall-clock time, method, a redacted path, response status, duration and referenced synthetic patient IDs. It excludes credentials, query strings and request bodies, and retains up to 2,000 requests per world for seven days. Earlier calls from before logging was enabled are unavailable. Reading a patient record does not count as changing it.\n\n## Provenance and coverage\n\nThe generator combines independently sampled collection-size bands with authored fictional content. Its size profile comes from 50 bounded JSON objects in a production EHR export. The profiler retains counts only, rounds counts down to multiples of five, and suppresses cells below five.\n\nProblems, medications, allergies, and miscellaneous-code collections are sampled independently. Names, identifiers, dates, codes, and text are fictional. The generator does not copy a patient's source bundle.\n\nThe sample is selection-biased and may contain repeated patients. It does not establish demographic or disease prevalence, clinical validity, or a formal privacy guarantee.\n\nThe checked-in aggregate profile is `packages/engine/src/ehr-profile.json`. Organisers can regenerate it with `scripts/profile-ehr.py`, which reads source objects in memory and emits the aggregate profile. Production credentials are not needed to run the simulator.\n\nMissing records do not establish that a condition is absent. Message delivery does not establish treatment, and prescription collection does not establish adherence.\n\nSee the [API reference](./api.mdx) for each adapter's implemented data and deviations from the corresponding NHS service."
    },
    {
      "title": "Document Inbox and discharge letters",
      "url": "/docs/documents/",
      "format": "mdx",
      "content": "<a href=\"/gp/documents/\">Open Document Inbox</a>, the GP practice's document office. It opens as a separate workspace with its own inbox, letter reader and processing panel. The GP building on the neighbourhood map offers separate GP Records and Document Inbox entrances. Hospital teams write discharge summaries in their discharge correspondence workspace and explicitly send them to the GP practice in the same team world.\n\n## Hospital handover\n\nSelect any patient in the hospital directory, then open **Discharge summaries**. Choose **New discharge summary** and enter the reason for admission, hospital course, diagnoses, medication changes, results, follow-up arrangements and requested GP actions.\n\n**Save draft** keeps the letter available only to the hospital. You can return to edit it. **Send to GP practice** requires every section to contain text. Where information is absent, record that explicitly. Sending locks the clinical text and makes the letter available in Document Inbox. The original author and sending team remain visible.\n\n## GP document processing\n\nOpen **Document Inbox** from the map or the GP Records Workflow menu and select an incoming letter. Search by patient or letter title, then narrow the queue by processing stage or priority. Read the complete letter and its authorship history. You can assign it to a named clinician or team.\n\nEnter a review note and choose **Confirm reviewed**. Then enter the filing outcome and choose **File to patient history**. Filing requires a recorded review. Filed letters remain accessible in the **filed** queue, including the original hospital text and the GP review and filing notes. Assignment records responsibility; it does not impersonate a clinician or change access permissions.\n\nThese actions do not automatically prescribe medication, create diagnoses, arrange follow-up or send messages outside the simulation. Teams must carry out any additional simulated actions separately.\n\n## Tags and SNOMED codes\n\nOpen **Tags and SNOMED codes** in the processing panel. Add document tags such as `Follow-up needed`, search a clinical term or numeric code, and select a concept. Choose **Save tags and codes** to persist your selections. Remove a tag or code with its remove button and save again. The inbox search includes saved tags, concept names and codes.\n\nCoding remains available after filing and records the acting team in the document history. These are annotations on the letter. They do not automatically add diagnoses to the patient problem list or change the original hospital text.\n\nThe searchable simulation subset contains 10 concepts from the [HL7 IPS CORE problem-list expansion](https://www.hl7.org/fhir/uv/ips/2019Sep/ValueSet-core-problem-list-disorders-uv-ips.html). It is not a complete terminology service.\n\n## Agent API\n\nUse your team API key with the existing authenticated site action endpoints. Documents are shared only within that team's world.\n\n- `GET /api/sites/hospital/documents` returns hospital drafts and sent letters with patient names.\n- `GET /api/sites/gp/documents` returns delivered letters and their processing status.\n- `POST /api/sites/hospital/actions` with `type: \"save_discharge_summary\"`, `patientId`, `title` and `dischargeSections` creates a draft. The sections object contains `reason`, `course`, `diagnoses`, `medicationChanges`, `results`, `followUp` and `gpActions`.\n- To update a draft, include its `resourceId` and `expectedVersion`.\n- Send through the hospital action endpoint using `type: \"process_document\"`, `documentCommand: \"send\"`, `resourceId` and `expectedVersion`.\n- To save annotations, use `documentCommand: \"annotate\"`, `documentTags: [\"Follow-up needed\"]` and `documentSnomedCodes: [{\"code\": \"195967001\", \"display\": \"Asthma\"}]`, with `resourceId` and `expectedVersion`. Both arrays are required; empty arrays clear annotations. The server accepts catalogue codes and stores their canonical display terms.\n- The GP action endpoint accepts document commands `assign` (with `clinician`), `review` (with `text`) and `file` (with `text`). Each requires `resourceId` and `expectedVersion`.\n\nUse a fresh idempotency key for each operation and reuse it when retrying the identical request. Stale resource versions and invalid stage changes return an error without changing the letter.\n\n## Default correspondence\n\nNew and existing team worlds receive 61 fictional letters across multiple patients and specialties. The population includes awaiting-review, reviewed, filed and hospital-only draft letters, with named synthetic authors, assignments and processing history. Existing letters and team edits are preserved when the seed population is added. Hospital drafts remain hidden from the GP inbox until sent."
    },
    {
      "title": "Demographics and organisation APIs",
      "url": "/docs/fhir/",
      "format": "mdx",
      "content": "# Demographics and organisation APIs\n\nUse the PDS-style API to find a person in your team's world, then follow their GP reference into the ODS-style organisation directory. These endpoints return FHIR R4 resource shapes with synthetic identifiers. They implement a documented subset of the NHS APIs.\n\nSupply your team key in `Authorization: Bearer …`. PDS requires the `gp` scope; ODS requires `referrals`. A normal team key has both. CIS2 staff tokens demonstrate identity protocols and do not replace team keys.\n\n## Find and read a patient\n\n```bash\ncurl --fail --silent \"$SIM_ORIGIN/api/nhs/pds/Patient?family=Khan&_count=10\" \\\n  -H \"Authorization: Bearer $SIM_KEY\"\n\ncurl --fail --silent \"$SIM_ORIGIN/api/nhs/pds/Patient/SIM-000001\" \\\n  -H \"Authorization: Bearer $SIM_KEY\"\n```\n\nSearch returns a `Bundle` with `type: searchset`, the total match count, entries and pagination links. Read returns a `Patient` with structured names, date of birth, synthetic address and contact details, local identifiers and a registered-GP reference. The names and birth dates come from the same patients you open in GP Records.\n\n| Parameter | Behaviour |\n| --- | --- |\n| `family`, `given` | Case-insensitive prefix matching |\n| `birthdate` | Exact calendar date, `YYYY-MM-DD` |\n| `identifier` | Local simulator patient identifier |\n| `_count` | Page size, 1–100 |\n| `_offset` | Nonnegative result offset |\n\nFilters combine with AND. Follow the returned `next` link rather than constructing page offsets. Unknown or repeated parameters and invalid dates return HTTP 400 with an `OperationOutcome`.\n\n## Find the registered organisation\n\n```bash\ncurl --fail --silent \"$SIM_ORIGIN/api/nhs/ods/Organization/SIM-RIVERSIDE\" \\\n  -H \"Authorization: Bearer $SIM_KEY\"\n```\n\nThe directory includes Riverside Practice, Northbank General, Riverside Community Services and Riverside Pharmacy. The organisation identifiers match the fictional staff assignments in the CIS2 emulator.\n\nSearch `Organization` using `name`, `identifier` or `active`, with the same pagination parameters. Read a specific organisation using its `SIM-…` identifier.\n\n## Exercise failure paths\n\n| Request | Response |\n| --- | --- |\n| Missing team key | 401, `OperationOutcome` |\n| Key without the required service scope | 403, `OperationOutcome` |\n| Unknown patient or organisation | 404, `OperationOutcome` |\n| Invalid search date or unsupported parameter | 400, `OperationOutcome` |\n| POST, PUT, PATCH or DELETE | 405, `OperationOutcome` |\n\nSuccessful resources and errors use `application/fhir+json`. These are read-only endpoints. Changes made through the simulator's normal patient workflows appear in subsequent reads.\n\n`/api/nhs/pds/metadata` and `/api/nhs/ods/metadata` describe this simulator's supported interactions. The PDS metadata route is a local discovery aid, not an endpoint offered by the real NHS PDS API.\n\n## Scope of the simulation\n\nThe shapes are based on the official [PDS FHIR API](https://digital.nhs.uk/developer/api-catalogue/personal-demographics-service-fhir) and [ODS FHIR API](https://digital.nhs.uk/developer/api-catalogue/organisation-data-service-fhir). The emulator does not claim NHS conformance. It does not implement real NHS-number lookup, probabilistic patient matching, national organisation data, demographic updates or NHS production authentication.\n\nEvery resource is marked synthetic. Addresses and contacts are fictional fixtures; SIM identifiers are not NHS numbers or real ODS codes. No request contacts an NHS service."
    },
    {
      "title": "Explore home readings in Home Health",
      "url": "/docs/home/",
      "format": "mdx",
      "content": "# Explore home readings in Home Health\n\nOpen **At home** on <a href=\"/control/\">the neighbourhood map</a> and launch Home Health. The wearable dashboard and patient Messages are separate apps at the same location.\n\n## Select a resident and inspect readings\n\nOpen the resident selector and find any synthetic person by name or SIM identifier. Choose a person to view their devices and observations. Use the day or week controls to change the reading period, and expand **Recorded readings** to inspect dated values and quality labels.\n\nA missing value remains missing. Charts and summaries use the selected resident's recorded data. They do not assess health or recommend treatment.\n\n## Connect a simulated watch\n\nIf the resident has no active activity watch, choose **Connect simulated watch**. The first activity reading is due after 10 simulation minutes, followed by hourly readings. Advance time using **Simulation controls** in the fixed bottom bar, then inspect the new reading.\n\nNo physical device or external Apple or Withings account connects to the simulator. Reconnecting an already active activity watch does not create another device. An organiser can simulate device disconnection; missing readings stay visible as missing data.\n\n## Read and reply to practice messages\n\nReturn to the home desktop and launch **Messages**. Select the same resident to see delivered practice conversations. Reply where the practice allows replies. See [Messagey messaging](./messaging.mdx) for the delivery simulator and practice inbox.\n\n## Connect an agent\n\nOpen the **Wearables** group in the <a href=\"/docs/explorer/\">API explorer</a> and authorize with your team key. The key must include `wearables` scope. New teams created without a `site` restriction include this scope; joining an existing team preserves its permissions.\n\nRead a resident's devices with `GET /api/sites/wearables/devices?patient=SIM-000006`. Read their recorded values with `GET /api/sites/wearables/readings?patient=SIM-000006`. Fresh simulator seeds include activity, resting heart rate, and sleep history for this resident. Existing published populations retain their stored history and can return empty lists. Connect a simulated watch to generate new activity readings.\n\nAdd `metric=steps`, `metric=heart-rate`, or `metric=sleep` to filter readings. Each response contains `items`, `total`, `offset`, `limit`, and the simulation time `now`. Requests default to 100 records; use `limit` from 1 to 500 and increase `offset` to retrieve further pages. Omitting `patient` returns matching records across your team's world.\n\nRead each item's `data` for its metric, value, unit, quality, and observation time. Timestamps use Unix milliseconds. Preserve `null` values and their quality labels when a device disconnects. A missing reading is not zero.\n\nTo generate readings for another resident, post this JSON to `/api/sites/wearables/actions`:\n\n```json\n{\n\t\"type\": \"connect_device\",\n\t\"patientId\": \"SIM-000003\",\n\t\"title\": \"Home activity watch\"\n}\n```\n\nPost `{\"paused\": true, \"advanceMinutes\": 10}` to `/api/clock`, then request `/api/sites/wearables/readings?patient=SIM-000003` to retrieve the first activity reading.\n\nUse `GET /api/sites/wearables/view?patient=SIM-000006` when you also need shared service records and events. See the [API reference](./api.mdx) for authentication and clock operations. The <a href=\"/api/openapi.json\">OpenAPI JSON</a> includes the wearable request parameters and response schemas."
    },
    {
      "title": "Work in Hospital EPR",
      "url": "/docs/hospital/",
      "format": "mdx",
      "content": "# Work in Hospital EPR\n\nOpen [Northbank General](/hospital/) with your team's key. Hospital EPR opens the **Patient List Manager**. Select a row to manage an attendance, or select the patient's name to enter the chart.\n\n## Register and progress an attendance\n\n1. Choose **Register A&E attendance** and find a synthetic patient.\n2. Enter the presenting complaint, acuity, and location. A responsible clinician is optional at registration.\n3. Register the attendance, then select its row in **A&E tracking**.\n4. Enter a responsible clinician and choose **Start assessment**.\n5. Choose **Refer to medical take**, then open **Medical take**.\n6. Enter the inpatient location and choose **Admit to entered location**. The patient moves into **Inpatient census**.\n\nUse **Save assignment** to update the location, acuity, or responsible clinician. To finish an attendance, enter its discharge outcome and choose **Discharge**. Completed attendances remain in **Discharged**. Discharging an attendance does not write or send a discharge letter automatically.\n\n## Read the waiting measures\n\n**Current mean wait** uses people still waiting for assessment. **Mean assessment wait · arrivals today** uses people who arrived today and have an assessment time. **In A&E ≥4 hours** counts current A&E attendances that have reached four hours. A&E tracking includes patients awaiting the medical take.\n\nThe measures use the team's simulation clock. They describe this world's attendances and are not national hospital statistics. Change time through **Simulation controls** and refresh the lists to observe the effect.\n\n## Work in a patient chart\n\nUse **Documentation** for the document list and readable note preview. Notes show sections vertically, with their original author and later changes. Create a draft, review and sign it, or add an attributed addendum to a signed note. Follow [Clinical records and hospital orders](./clinical-records.mdx).\n\n**Prescription** and **Request test** open separate catalogues and order details. **Orders** retains the submitted fields. **Results** shows a laboratory flowsheet with sample dates in columns and controls for trends. Pharmacy must review and approve a new prescription before dispensing.\n\nUse [Discharge summaries and Document Inbox](./documents.mdx) to write and send a handover to the GP. Use **Handover** for related tasks and onward-care records. Creating a task, arranging a visit, and sending a letter are separate actions.\n\n## Agent requests\n\n`GET /api/sites/hospital/attendances` returns attendance resources, patient names, and simulation time. Submit mutations to `/api/sites/hospital/actions` using the team's bearer key.\n\nCreate with `type: \"register_attendance\"`, `patientId`, `title`, `acuity`, and `location`. `clinician` is optional. Update with `type: \"update_attendance\"`, `resourceId`, `expectedVersion`, and `hospitalCommand`, one of `assign`, `assess`, `refer`, `admit`, or `discharge`. Include the fields needed by that transition, such as a clinician for assessment or `disposition` for discharge.\n\nUse the <a href=\"/docs/explorer/\">API explorer</a> for request schemas and supported values. Reuse the same idempotency key for an identical retry and reload after a version conflict."
    },
    {
      "title": "Test staff sign-in with Staff Identity",
      "url": "/docs/identity/",
      "format": "mdx",
      "content": "# Test staff sign-in with Staff Identity\n\n<a href=\"/cis2/\">Staff Identity</a> is the simulator's CIS2-style staff identity service. Its staff identities, organisations, and roles are fictional. A staff token does not replace the team API key or grant access to a team world.\n\n## Try a staff sign-in\n\n1. Open Staff Identity and choose the simulated smartcard or security-key method.\n2. Start sign-in and choose a fictional staff identity.\n3. Select an organisation and role, then continue.\n4. Inspect the identity on the result page. Expand **Technical session details** for the returned identity and expiry information.\n\nNo hardware, smartcard PIN, or NHS password is required. Cancelling sign-in returns a denial instead of creating a staff session.\n\n## Connect your application\n\nDiscover the issuer through <a href=\"/cis2/.well-known/openid-configuration\">the OpenID discovery document</a>. The implemented flow uses authorization code with PKCE S256 and RS256-signed identity tokens. The bundled `nhs-sim-client` uses this origin's `/cis2/callback` route.\n\nAn organiser can register your client ID and exact redirect URLs. Start a fresh authorization request with your own state, nonce, and PKCE challenge. Exchange the one-time code using its verifier, then call the discovered userinfo endpoint with the staff access token. Use the discovery document's JWKS URL to verify identity-token signatures.\n\nThe [API reference](./api.mdx), <a href=\"/docs/explorer/\">API explorer</a>, and <a href=\"/api/openapi.json\">OpenAPI JSON</a> describe the simulator's endpoints. The browser sign-in demonstrates the interactive consent and role-selection flow.\n\n## Exercise failure cases\n\nIn **Developer and operator tools**, enter the organiser token and choose **Unlock operator controls**. Select normal sign-in, consent denied, service unavailable, or session expired. Set a token lifetime between 30 and 3,600 seconds, or edit the registered applications.\n\n**Apply settings** revokes existing interactions, codes, and access tokens. **Revoke all sessions** invalidates them without changing the scenario. These settings affect the running application, not a single team world. Restore normal sign-in after the exercise. Restarting the application resets identity settings and sessions."
    },
    {
      "title": "Explore the neighbourhood",
      "url": "/docs/",
      "format": "mdx",
      "content": "# Explore the neighbourhood\n\nA referral gets rejected. A result arrives late. A discharge letter sits in another system. Enter the map, open a workplace, and follow one fictional patient across their care.\n\n<div className=\"handbook-grid\">\n<a href=\"/docs/team-access/\"><strong>Start a team world</strong><span>Copy your key, choose patients, and control simulation time.</span></a>\n<a href=\"/docs/systemtwo/\"><strong>Work in GP Records</strong><span>Consultations, problems, allergies, and the appointment book.</span></a>\n<a href=\"/docs/hospital/\"><strong>Work in Hospital EPR</strong><span>A&E tracking, the medical take, inpatient charts, and orders.</span></a>\n<a href=\"/docs/pharmacy/\"><strong>Run Pharmacy</strong><span>Receive referrals, dispense prescriptions, compare suppliers, and manage stock.</span></a>\n<a href=\"/docs/ten-year-plan/\"><strong>Explore the ten-year plan</strong><span>Connect the plan's questions to people and places in the simulator.</span></a>\n<a href=\"/docs/explorer/\"><strong>Try an API request</strong><span>Interactive Swagger UI, request schemas, and responses.</span></a>\n</div>\n\n## Choose a workplace\n\nOpen <a href=\"/control/\">the neighbourhood map</a>. Each location opens a desktop with its apps. The map gets out of the way inside the workplace.\n\n- Riverside Practice offers [GP Records](./systemtwo.mdx), [Document Inbox](./documents.mdx), and [Messagey](./messaging.mdx).\n- Northbank General opens [Hospital EPR](./hospital.mdx).\n- High Street Pharmacy opens [Pharmacy](./pharmacy.mdx).\n- Neighbourhood Care opens [Community Care](./community.mdx).\n- At home offers [Home Health](./home.mdx) and [patient Messages](./messaging.mdx).\n\nOne team key opens one isolated world. The portals and APIs read the same records. Use the same patient identifier across workplaces to follow a handover. Actions can schedule work, reserve capacity, or make a record visible to another service. Advance the clock to observe delayed results.\n\n## Use the records\n\n- [Team access and simulation time](./team-access.mdx) covers onboarding, the key, patient selection, and the activity trail.\n- [Appointment sessions and slots](./appointments.mdx) covers booking, protected time, and session management.\n- [Clinical records and hospital orders](./clinical-records.mdx) covers narrative journals, signed notes, prescriptions, and blood tests.\n- [Discharge letters and GP document processing](./documents.mdx) covers hospital drafts, sending, GP review, filing, tags, and codes.\n- [Patient workflows](./challenges.mdx) follows records across services without assigning a hackathon challenge.\n\n## Build an integration\n\nStart with [your first API call](./quickstart.mdx), then use the [API reference](./api.mdx) and <a href=\"/docs/explorer/\">interactive API explorer</a>. <a href=\"/api/openapi.json\">OpenAPI JSON</a> is available for client generators and agents. <a href=\"/docs/handbook.json\">The handbook JSON</a> contains the published guides with their titles, URLs, and source text.\n\nUse [PDS and ODS FHIR APIs](./fhir.mdx) for demographics and organisations, [Staff Identity](./identity.mdx) for staff sign-in, or [the legacy browser workflow](./legacy.mdx) for HTML-only document transfer.\n\n## Understand the world\n\nRead [the data guide](./data.md) for fictional identities, authorship, record visibility, and production-derived aggregate counts. [The population guide](./population.mdx) explains reproducible people, public appointment statistics, and isolated team changes.\n\nThe [ten-year plan guide](./ten-year-plan.mdx) connects the plan's themes to existing patients and workflows. You choose what to build. [Run the event](./organisers.mdx) covers operator setup, incidents, and verification.\n\n<a href=\"/control/\">Open the simulator</a>"
    },
    {
      "title": "Transfer a legacy record",
      "url": "/docs/legacy/",
      "format": "mdx",
      "content": "# Transfer a legacy record\n\nThe legacy EPR exposes its documents through HTML. `/api/sites/legacy/view` deliberately returns HTTP 501.\n\n## Establish a browser session\n\nObtain a normal [team key](./quickstart.mdx). Create a browser session with that key before opening `/browser/legacy`.\n\nFor an automation client, call `POST /api/session` with the team's Bearer key. Retain the returned HttpOnly, SameSite cookie in the browser session, then open `/browser/legacy` on the same origin.\n\n## Transfer the document\n\nInspect the HTML table. Submit the **Send copy to GP** form for the chosen letter. Submit the form's actual hidden CSRF field and resource identifier. The successful form response redirects with HTTP 303.\n\n## Verify the transfer\n\nRead `/api/sites/gp/view` with the same team key. Find the transferred resource by its identifier. The document is now visible in GP.\n\nThe transfer changes visibility in your team's world. It does not create a real NHS message or contact a patient."
    },
    {
      "title": "Messagey messaging",
      "url": "/docs/messaging/",
      "format": "mdx",
      "content": "# Messagey messaging\n\nMessagey is the fictional primary-care messaging workspace beside GP Records and Document Inbox. Open Riverside Practice on the neighbourhood map and choose **Messagey**. It uses the same team world and patient directory as the other products.\n\n## Send a patient message\n\n1. Select **New message** and find any synthetic patient by name or ID.\n2. Enter a subject and message, or choose an editable practice template.\n3. Choose SMS or email and whether to allow replies.\n4. Select **Queue message**.\n5. Open **Delivery simulator** and choose **Deliver** or **Fail delivery**.\n\nMessages never leave the simulation. A queued message does not appear in the patient's app until it is delivered. Failed messages can be retried; the complete delivery history remains attached to the original message.\n\n## Reply from home\n\nOpen **Messages** at the patient's home, or follow the patient Messages link from a practice conversation. Choose the same patient, open a delivered conversation and send a reply. Replies appear in the practice inbox. A completed conversation or one-way message cannot accept replies. The practice can reopen a completed conversation.\n\nInternal practice notes and assignments are not shown in the patient app. The patient selector lets hackathon teams role-play different synthetic people inside their own world.\n\n## Manage the inbox\n\nConversations can be assigned to a named clinician or team, supplemented with internal notes and marked done. **Done** holds completed conversations and offers a reopen action. Patient messages retain their original text and author when colleagues add subsequent messages.\n\n**Templates** supports creating, editing and archiving reusable SMS and email text. Applying a template copies its content into an editable composer; changing a template does not rewrite previously sent messages.\n\n## Send messages through the API\n\nOpen the **Messaging** group in the <a href=\"/docs/explorer/#/Messaging\">API explorer</a>. Authorize with your team key. Both practice and patient operations require `gp` scope and stay inside your team's world. The key lets participants role-play synthetic patients; selecting a patient is not an identity sign-in.\n\nUse these operations:\n\n| Operation | Purpose |\n| --- | --- |\n| `GET /api/sites/gp/messages` | Read practice conversations, optionally filtered by `patientId` |\n| `POST /api/sites/gp/messages` | Send as the practice or doctor, manage delivery, and configure patient scripts |\n| `GET /api/sites/patient/messages?patientId=SIM-000003` | Read one patient's visible conversations |\n| `POST /api/sites/patient/messages` | Start a patient conversation or reply as that patient |\n| `GET /api/messaging/reply-presets` | Retrieve editable administrative reply scripts without authentication |\n\nList responses contain `items`, `total`, `offset`, `limit`, and `now`. The default page size is 100; the maximum `limit` is 500. Increase `offset` to retrieve further pages. Conversations retain storage order. Patient reads require `patientId` and exclude private notes, queued or failed outgoing messages, and script configuration.\n\n### Start as a patient\n\nPost this request to `/api/sites/patient/messages`:\n\n```json\n{\n\t\"patientId\": \"SIM-000003\",\n\t\"command\": {\n\t\t\"kind\": \"patient_create\",\n\t\t\"subject\": \"Appointment request\",\n\t\t\"body\": \"Could I arrange an afternoon appointment?\",\n\t\t\"channel\": \"sms\"\n\t}\n}\n```\n\nThe incoming conversation appears immediately in the practice inbox and patient app. Save the returned `id` and `version`.\n\n### Send as the practice or doctor\n\nTo start a practice conversation, post to `/api/sites/gp/messages`:\n\n```json\n{\n\t\"patientId\": \"SIM-000003\",\n\t\"command\": {\n\t\t\"kind\": \"create\",\n\t\t\"subject\": \"Appointment preference\",\n\t\t\"body\": \"Would a morning or afternoon appointment suit you?\",\n\t\t\"channel\": \"sms\",\n\t\t\"allowReply\": true\n\t}\n}\n```\n\nTo send another message in an existing conversation, use `command: {\"kind\": \"send\", \"body\": \"Please confirm Tuesday.\", \"channel\": \"sms\"}` with top-level `resourceId` and `expectedVersion`. The message is queued. Copy the outgoing entry's `id` from `data.entries`, then send a `delivery` command with that `entryId` and `status: \"delivered\"`.\n\nPractice messages have direction `outgoing`; patient messages have direction `incoming`. The authenticated team remains the audit actor, so the API cannot forge another participant's identity.\n\n### Enable scripted patient replies\n\n1. Retrieve `/api/messaging/reply-presets`. Choose appointment preference, acknowledgment, or collection availability. You can edit the returned text or supply your own synthetic administrative script.\n2. Before delivering the practice message, post a script to `/api/sites/gp/messages`. Replace the conversation ID and version below with the latest returned values.\n\n```json\n{\n\t\"resourceId\": \"REPLACE_WITH_CONVERSATION_ID\",\n\t\"expectedVersion\": 1,\n\t\"command\": {\n\t\t\"kind\": \"configure_auto_reply\",\n\t\t\"steps\": [\n\t\t\t{\"body\": \"An afternoon appointment would suit me, thank you.\", \"delayMinutes\": 2},\n\t\t\t{\"body\": \"Tuesday works for me.\", \"delayMinutes\": 1}\n\t\t]\n\t}\n}\n```\n\n3. Use the new version to deliver the queued outgoing message:\n\n```json\n{\n\t\"resourceId\": \"REPLACE_WITH_CONVERSATION_ID\",\n\t\"expectedVersion\": 2,\n\t\"command\": {\n\t\t\"kind\": \"delivery\",\n\t\t\"entryId\": \"REPLACE_WITH_OUTGOING_ENTRY_ID\",\n\t\t\"status\": \"delivered\"\n\t}\n}\n```\n\n4. Post `{\"paused\": true, \"advanceMinutes\": 2}` to `/api/clock`.\n5. Read the conversation again. The first scripted reply appears as an incoming message attributed to the synthetic patient. Use its updated version for further commands.\n\nEach successfully delivered practice message consumes one script step. The second step waits for another outgoing message to be delivered; it does not follow the first reply automatically. A script accepts up to 20 steps, each delayed by 0 through 10,080 simulation minutes. A zero-minute step runs on the next clock advance, including an advance of zero. Pausing the clock pauses replies. Scripts and pending replies persist in the team's world.\n\nOnly open conversations with replies enabled accept a nonempty script. Failed delivery does not consume a step. Scripts stop when their steps run out. Multiple delivered messages can have separate pending replies. Each delay starts at that message's delivery, so shorter delays can produce replies before earlier scheduled ones.\n\nTo disable automation, send `configure_auto_reply` with `steps: []`. Replacing a script cancels pending replies, resets its sequence, and applies only to future deliveries. Manually replying as the patient or completing the conversation also cancels pending replies. Those reserved steps remain consumed, including after reopening. Private notes and assignments do not cancel replies. No model or external SMS/email provider generates these responses.\n\n### Reply manually as the patient\n\nPost to `/api/sites/patient/messages` with the matching `patientId`, the conversation's `resourceId` and `expectedVersion`, and `command: {\"kind\": \"reply\", \"body\": \"Tuesday afternoon works for me.\"}`. A reply requires an open conversation with replies enabled and a delivered practice message. The patient API rejects practice commands and a mismatched patient.\n\n### Retry and manage conversations\n\nSend an `Idempotency-Key` header when retrying the same request. Reuse its original body. A changed body with the same key returns `409`. Existing resources require the latest `expectedVersion`; stale updates also return `409`. Scheduled replies change the version, so refresh before the next update.\n\nThe practice API also accepts `note`, `assign`, `complete`, `reopen`, `retry`, `save_template`, and `archive_template`. Their fields appear in the Explorer's `PracticeMessageRequest` schema. Templates remain in `GET /api/sites/gp/messaging-workspace` alongside conversations.\n\nExisting integrations can continue posting `type: \"messaging_action\"` with `messagingCommand` to `/api/sites/gp/actions` or `/api/sites/patient/actions`. The same role restrictions, versions, scripts, and retry handling apply."
    },
    {
      "title": "Run the event",
      "url": "/docs/organisers/",
      "format": "mdx",
      "content": "# Run the event\n\n## Start the environment\n\nConfigure `OPERATOR_TOKEN` with at least 16 characters. Launch the application and PostgreSQL with the repository's Compose configuration. Keep the operator token with organisers.\n\nBefore participants arrive, run:\n\n```bash\npnpm verify -- doctor\npnpm smoke\npnpm verify -- journey\n```\n\nThe smoke test creates a team world, reads each advertised adapter, transfers a legacy document, and checks a delayed test result.\n\n## Prepare teams\n\nAsk participants to create or join a team from the map. Names are lowercased with all whitespace removed; entering the same name returns the same world and reusable API key. Test the expected number of concurrent worlds before the event. PostgreSQL stores patient records, resources, events, and receipts as separate rows. Teams share the published population baseline and keep their changes in separate worlds.\n\n## See what teams are doing\n\nOpen <a href=\"/control/?operator=1\">Organiser controls</a> and connect with this deployment's operator token. A participant key cannot unlock this screen. The local Compose default is `local-operator-change-me`; the hosted deployment has its own token. You can paste the token alone or with a `Bearer` prefix.\n\nSearch the team directory and select a team by name. Review its API requests, affected patients and recorded changes. API requests show the method, path, status and duration. A request that reads a patient is distinct from a recorded change to that patient.\n\nRequest logging starts when this feature is deployed; older API calls cannot be reconstructed. Recorded changes use existing provenance, so earlier attributed work can still appear. Request history retains up to 2,000 calls per world for seven days. It omits credentials, query strings and request bodies. The console refreshes while open.\n\nChoose **Explore as this team** to open the team's existing world using its participant permissions. This switches the shared participant connection across tabs in this browser. Changes you make there are real changes in that synthetic world and carry its team attribution. The exploration banner identifies the active team in each tab. Use **Return to organiser** to restore your previous team connection and return to the console.\n\nOperator access is remembered in this browser tab for navigation to CIS2 and the clinical apps. Disconnect operator access when finished. Operator credentials are separate from the participant key saved in localStorage.\n\n## Delete an unused team\n\nSelect a team and choose **Delete team**, or use the directory checkboxes to select several teams. **Select all matching** selects the teams in the current search. Review the selected names and world IDs, then confirm permanent deletion once. You do not need to type team names. Cancel leaves every team unchanged.\n\nDeletion revokes all keys for that world and removes its records, events, pending work and API request history. It frees a team slot. Other teams and shared synthetic population baselines remain intact. Anyone entering the deleted name later creates a new world; they cannot recover the old records that way.\n\nUse the activity history to decide which teams to remove. **No requests yet** can mean the team predates request logging, so it is not proof that the team is unused. Teams are never deleted automatically.\n\n## Introduce a failure\n\nIn Organiser controls, select the team before changing incidents, agents or simulation time. The selected team's name identifies which world each control affects. Leave deterministic agents enabled for the initial run, then introduce one incident for the team's second demonstration.\n\nWorld agents are scripted simulation workers, not participants' AI models. They process work as simulation time advances. Turning off an incident removes the fault condition but does not undo requests or arrivals it already created.\n\nUse **All-team incidents** to introduce the same disruption across every current team, or restore that service across all teams. Choose the incident and review the number of teams before confirming. The default seed world is excluded, and teams created later do not inherit the disruption. If the team list changes before you confirm, refresh and review the new count before trying again.\n\nOperator API endpoints accept `?world=...`. Team clock requests always affect the key's own world.\n\n## Exercise staff sign-in\n\nOpen <a href=\"/cis2/?operator=1\">Staff Identity organiser controls</a> from the console. The same tab reuses your validated operator token. You can also enter it directly in the CIS2 token form. The workbench provides operator controls for client registration, token lifetime, denial, session expiry and service unavailability, alongside the fictional staff sign-in.\n\nIdentity settings affect the whole running application. Changing them revokes current interactions, codes and access tokens. Restore the normal scenario before another team tries to sign in. Restarting the application resets identity settings to their defaults.\n\nTeam worlds and API keys are separate from these identity sessions. A successful staff sign-in does not create or unlock a team world.\n\n## Evaluate the result\n\nAsk for the initial state, the action sequence, and the final records. Distinguish accepted requests from completed work. Keep model APIs optional so teams can run the default deterministic agents without a paid model account.\n\n[API and operator endpoint reference](./api.mdx)"
    },
    {
      "title": "Run the pharmacy",
      "url": "/docs/pharmacy/",
      "format": "mdx",
      "content": "# Run the pharmacy\n\nOpen <a href=\"/pharmacy/\">High Street Pharmacy</a> and connect your team. The pharmacy uses the same patients and team world as the EPRs. Its prices, suppliers, and stock are fictional.\n\n## Complete a Pharmacy First referral\n\n1. Select a patient from the directory.\n2. Open **Pharmacy First** and receive a referral. Record its source, pathway, and reason.\n3. Open the referral and accept it.\n4. Start the consultation, record its outcome, and return it to the referring service.\n5. Open the same patient in the GP record to inspect the shared outcome and team attribution.\n\nThe simulator records the workflow and your entered outcome. It does not choose treatment. The service categories follow [NHS England's Pharmacy First overview](https://www.england.nhs.uk/primary-care/pharmacy/pharmacy-services/pharmacy-first/).\n\n## Dispense a prescription\n\n1. Open **Dispensing bench** and select a prescription, or create one for the selected patient.\n2. Link the matching catalogue product and enter the quantity in units.\n3. Record the review and approve the prescription.\n4. Dispense the item. The product's stock falls by the dispensed quantity.\n5. Confirm collection. Open **Stock & buying → Stock activity** for the stock change and its author.\n\nIf stock is insufficient, receive a delivery before dispensing. Repeating a completed dispense cannot deduct stock again.\n\n## Buy medicines without selecting a patient\n\nSwitch to **Stock & buying**. This area belongs to your team's pharmacy. Patient selection only applies to **Patient care**.\n\n1. Open **Buy medicines** and search the catalogue, including products with no stock.\n2. Choose the exact medicine and formulation. Enter the number of units you need.\n3. Compare suppliers. Each offer shows pack size, pack cost, minimum order, delivery time, and total cost for the required quantity.\n4. Add an offer to the basket. Repeat for other medicines. The basket is saved in your team world.\n5. Review the basket and place the order. Supplier orders retain their agreed prices and delivery dates.\n\nPack sizes and minimum orders can mean buying more units than requested. Compare the total and excess units as well as price per pack. Different strengths and formulations are separate products.\n\n## Receive or cancel an order\n\nOpen **Orders** to see what is due. Advance simulation time using the fixed bottom bar when waiting for a delivery. Receive the number of packs actually delivered and give the receipt a reference. You can receive a delivery in parts, or cancel the quantity still outstanding with a reason.\n\nPlacing an order creates a commitment. It does not increase stock. Receiving packs increases stock and inventory cost. Cancelling outstanding packs releases the remaining commitment and leaves received stock unchanged.\n\n## Inspect inventory and stock activity\n\n**Inventory** shows the medicine catalogue and current stock. **Stock activity** records physical movements across the pharmacy, including opening balances, receipts, and dispensing. It is not filtered by the selected patient. A dispensing movement links back to the prescription that consumed the stock.\n\nOpening balances explain stock already present when tracking begins. They do not invent historical purchases or their authors.\n\n## Connect a stock-management agent\n\nUse **Team & API key** in the fixed bottom bar to copy your key. Read the pharmacy workspace through `GET /api/sites/pharmacy/pharmacy-workspace`. Submit changes through `POST /api/sites/pharmacy/actions`. See the [action reference](/docs/api/) for request fields.\n\nAn agent can compare quotes, inspect stock and reorder levels, build a shared basket, place purchase orders, receive deliveries, cancel outstanding quantities, and observe completed dispensing. Use `expectedVersion` when changing a record and an `Idempotency-Key` header when submitting an action that may be retried. Refresh the record after a version conflict.\n\nAssess ordering decisions using stock availability as well as cost. An experiment that reduces stock value while leaving prescriptions unfilled has changed the service level. Record both outcomes in your demo.\n\n## Read the financial figures\n\nPurchase spend measures acquisition costs. Stock value measures the cost of inventory still on hand. Dispensing cost measures the stock consumed by completed dispenses. Simulated revenue uses the item's indicative selling price at dispensing time. Gross margin is that revenue minus dispensing cost.\n\nHistorical dispensing figures retain their original cost and revenue when a catalogue price changes. These figures describe the simulated medicine inventory. They exclude staffing, rent, tax, reimbursement rules, and other overheads, so gross margin is not net profit."
    },
    {
      "title": "The synthetic population",
      "url": "/docs/population/",
      "format": "mdx",
      "content": "# The synthetic population\n\nThe large population is generated in reproducible batches. Each new person has a fictional identity, age-appropriate circumstances, conditions, contact preferences, a structured GP record, three to eight dated consultations and a historical appointment. The original authored patients and team work remain available.\n\n## Public data and fictional stories\n\nHistorical appointment categories use published NHS England counts from [Appointments in General Practice, May 2025](https://digital.nhs.uk/data-and-information/publications/statistical/appointments-in-general-practice/may-2025). The source contains 29,114,064 recorded appointments, not unique patients. It describes England in that month and is not a current population estimate.\n\nWe sample contact mode, attendance, professional type and service setting independently. Unknown source categories remain unknown. The relationship between these categories, a person's health conditions and their appointment date is authored for the simulation. It is not an observed relationship in the source data.\n\nMedication histories use named medicines and formulations, linked to recorded conditions where the catalogue supports them. Entries distinguish current and previous issues, repeat and acute prescriptions, supply status and review dates. Allergy entries can include named substances and recorded reactions. These are fictional histories, with no prescribing instructions.\n\nClinical histories use synthetic templates. Aggregate production EHR collection sizes shape record length; no production patient narratives or identifiers are copied. The generator is not a validated prevalence or treatment-outcome model.\n\nContains information from NHS England, licenced under the [Open Government Licence v3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/).\n\n## Explore a person\n\n<a href=\"/gp/\">Open GP Records</a> and search by name, patient ID, condition or care need. Open **Consultations** to read the dated narrative. Use the journal and coded history for structured records. The appointment book can show historical appointments by date; missed and unknown historical entries have no live arrival controls.\n\n## Shared population, separate team changes\n\nA published population is stored once. New teams refer to that baseline and store their own changes separately. A consultation saved by one team does not alter another team's record. Older worlds can attach the baseline while preserving their existing patients and resources.\n\n## Reproduce or resume an import\n\nOperators can run `scripts/seed-population.mjs --target 50000 --world default` with `OPERATOR_TOKEN` supplied in the environment. The command adds at most 500 people per request and reports progress. Re-running resumes from the world's existing population count. The manifest fixes the generator version, seed and generation clock.\n\nThe operator publishes the resulting baseline with `POST /api/control/population/publish?world=default`. To attach it to an existing world, use `POST /api/control/population/attach?world=TEAM_WORLD` with body `{\"source\":\"default\"}`. These endpoints require an operator token.\n\nThe legacy database snapshot is retained during migration. Patient records, resources, events and receipts are persisted as separate rows. Imports and ordinary changes commit atomically."
    },
    {
      "title": "Make your first API call",
      "url": "/docs/quickstart/",
      "format": "mdx",
      "content": "# Make your first API call\n\nCreate a team at <a href=\"/control/\">the map</a>, copy its API key, and open Riverside Practice to enter GP Records. The key stays available in **Team & API key** in the fixed bottom bar. Read [Team access and simulation time](./team-access.mdx) for browser setup.\n\nThe commands below create or join a team world and add a GP task for a fictional patient. They use `curl` and `jq`. If you already have a key, assign it to `SIM_KEY` and skip key creation.\n\n## Create a team world\n\nSet the origin to the address your organiser gave you. For a local instance:\n\n```bash\nexport SIM_ORIGIN=http://localhost:8080\n```\n\nCreate your key once:\n\n```bash\nexport SIM_KEY=$(curl --fail --silent \"$SIM_ORIGIN/api/keys\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"teamName\":\"Example builders\"}' | jq -r '.apiKey')\n```\n\nShare this key or your team name with teammates. Repeating the same name returns the same world and reusable key. Names are lowercase with whitespace removed, so `Example builders` and `examplebuilders` match. Use a distinct team name to start a separate world.\n\n## Read a patient\n\n```bash\ncurl --fail --silent \"$SIM_ORIGIN/api/sites/gp/patients?q=SIM-000001\" \\\n  -H \"Authorization: Bearer $SIM_KEY\" | jq\n```\n\nThe response contains `items` and `total`. The patient has a fictional identity and a `SIM-` identifier.\n\n## Add a task\n\n```bash\ncurl --fail --silent \"$SIM_ORIGIN/api/sites/gp/actions\" \\\n  -H \"Authorization: Bearer $SIM_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  -H 'Idempotency-Key: first-gp-task' \\\n  -d '{\"type\":\"create_task\",\"patientId\":\"SIM-000001\",\"title\":\"Check discharge follow-up\"}' | jq\n```\n\nThe response is the created resource, including its `id` and `status`. Repeating this request with the same idempotency key and payload returns the same result.\n\n## Check the result\n\n```bash\ncurl --fail --silent \"$SIM_ORIGIN/api/sites/gp/view\" \\\n  -H \"Authorization: Bearer $SIM_KEY\" \\\n  | jq '.resources[] | select(.title == \"Check discharge follow-up\")'\n```\n\nOpen <a href=\"/gp/\">GP Records at Riverside Practice</a> and use the same team key to inspect the task in the portal.\n\n[Explore the ten-year plan](./ten-year-plan.mdx) or read the [action reference](./api.mdx)."
    },
    {
      "title": "Working in GP Records",
      "url": "/docs/systemtwo/",
      "format": "mdx",
      "content": "# Working in GP Records\n\n<a href=\"/gp/\">Open GP Records</a> with your team key. Search for a person by name, local ID, condition or care need.\n\n## Record a consultation\n\nSelect a patient, then choose **New consultation**. Enter a title, contact type and notes. **Save draft** stores unfinished notes in your team world. **Save consultation** adds a saved entry to the patient journal.\n\nThe **Consultations** tab lists previous contacts. Expand an entry to read its narrative. Entries created through this editor have **Open for editing**. Saving an edit updates that record and its version. A conflicting edit is rejected, so reload the entry before trying again.\n\nUnsubmitted title, contact type and note text remain in the current browser session for that patient. Use **Save draft** before moving to another device. **New consultation** starts a separate note. The simulator does not provide clinical recommendations or a production clinical signing process.\n\n## Maintain problems and allergies\n\nOpen **Problems**, then **Add problem**. Enter the problem name and optionally a code and onset date. Saving updates the patient's active-problem summary. **Edit / resolve** lets you correct or resolve an entry; choose **Active and resolved** to see its history or reactivate it.\n\nOpen **Allergies**, then **Add allergy** to record an allergen and optional reaction. Active entries appear in the patient banner. Inactive entries remain in history. An empty list means no allergy information has been recorded; it does not confirm that the patient has no allergies.\n\nProblem and allergy changes carry team attribution and version history and are visible to the hospital, community and pharmacy in the same team world. Historical entries without a recorded author retain that distinction.\n\n## Follow up requests\n\n**Medication** shows new prescription requests and their pharmacy status alongside recorded medication history. **Open in pharmacy** takes you to the receiving system. **Refer to hospital** creates a referral the hospital can review and accept.\n\nAfter requesting a test and advancing simulation time, open its entry in **Results** and choose **Mark reviewed**. GP review is recorded with your team's attribution. Follow-up tasks can be reviewed, accepted and completed from their record details.\n\n## Work the appointment book\n\nChoose **Appointment book** before or after selecting a patient. The diary groups timed slots into clinician sessions. Filter by date, clinician, or AM and PM. Dates and times use the simulation's UTC clock.\n\nSelect an available slot, find the patient, and enter the reason. The session supplies the clinician, location, contact type, and duration. A booked slot offers arrival, completion, and cancellation. Cancelled slots become available again; completed bookings remain in history.\n\nUse **Create session** to add sessions. A free slot can be blocked for protected time and unblocked later. The server rejects overlapping sessions and bookings. Read [Appointment sessions and slots](./appointments.mdx) for the complete workflow and request fields.\n\n## Explore the population\n\nNew teams inherit the published synthetic population, including the authored life histories with personal context and distinct past contacts. Before a large population is published, a team starts with 500 people. Existing patient work is preserved when an older world is expanded. See the <a href=\"/docs/population/\">population guide</a> for generation and public-data sources.\n\nTry Nina Brooks, a child whose appointment arrangements involve her parent; Yasmin Ahmed, taking over her own appointment arrangements as she starts college; or Frank Walsh, coordinating longer appointments with community transport. Open their journal and consultation history to follow their earlier contacts.\n\n## Build against the same records\n\nUse `POST /api/sites/gp/actions` with your bearer key. A consultation action contains `type: \"save_consultation\"`, `patientId`, `title`, `text`, `mode` and `consultationStatus`, either `draft` or `saved`. Updates also require `resourceId` and `expectedVersion`.\n\nA session-based `book_appointment` action supplies `sessionId`, `sessionVersion`, `startsAt`, `patientId`, and `title`. The session defines its duration, clinician, and contact type. `arrive_appointment`, `complete`, and `cancel_appointment` take a resource ID and support `expectedVersion`.\n\nUse `save_problem` with `patientId`, `title`, and `problemStatus` (`active` or `resolved`); `problemCode` and `onsetDate` are optional. Use `save_allergy` with `patientId`, `title`, and `allergyStatus` (`active` or `inactive`); `reaction` is optional. Editing either resource requires `resourceId` and `expectedVersion`. Their records remain in history after resolution or deactivation.\n\nRead the book with `GET /api/sites/gp/appointments?date=2026-09-10`. It returns appointments and the names needed to display them. Patient consultation records remain available through the normal patient view.\n\n## Blood results and trends\n\nOpen a patient and select **Results** in GP Records, or **Results review** in the hospital chart. Both EPRs show the same laboratory history for that patient and team world.\n\nThe synthetic history includes FBC, U&E, HbA1c, LFT, CRP and lipids, with six sample dates per panel. GP Records shows each analyte's latest value, units, sample date, previous value, and illustrative reference interval. Hospital Results defaults to a flowsheet with sample dates in columns and a panel navigator. Lab results are separate from wearable observations. Pending requests and other investigations remain under their own disclosure.\n\nChoose **View trend** beside one test, or tick several tests and choose **Graph selected**. Each test gets its own chart and units in a popup. Change the history period or expand **View values** to inspect the dated values. Close the popup with its Close button or Escape.\n\nAll values and reference intervals are fictional simulation data. Historical dates respect the patient's date of birth. Existing team worlds receive each patient's history on first opening that record, without replacing their own records or expanding every large cohort at startup."
    },
    {
      "title": "Team access and simulation time",
      "url": "/docs/team-access/",
      "format": "mdx",
      "content": "# Team access and simulation time\n\n## Create or reconnect a team\n\n1. Open <a href=\"/control/\">the neighbourhood map</a> and choose a workplace.\n2. Enter your team name to create or join its world. You can also expand the existing-key option and connect with an API key.\n3. For a new team, choose **Copy API key** before continuing. The confirmation stays open until you continue.\n4. Launch an app. The first-use tour points out the key, simulation controls, and patient selector.\n\nTeam names are join codes: anyone who enters the same name can join that simulation world. Names are converted to lowercase with all whitespace removed, so `Team Rocket`, `teamrocket`, and ` TEAM ROCKET ` join the same world and return the same reusable key. Choose a distinct name for your team.\n\nThe browser saves the connection in localStorage, so it survives refreshes and reopening the browser. Teammates on another device can enter the same name or use the shared API key.\n\nOlder worlds keep their existing keys. The first name-based join may issue a new reusable key for that same world because older keys were stored only as hashes. If a name matches several older worlds, use the existing key to select the right world.\n\nOpen **Team & API key** in the fixed bottom bar to copy or reveal the connected key. **Disconnect this browser** removes the browser connection without deleting the world.\n\n## Choose the person and workplace\n\nUse the patient directory in each workplace. Home Health and patient Messages have resident selectors. Changing the person changes the records on screen. Use the same SIM identifier when comparing a patient's GP, hospital, pharmacy, community, and home records.\n\nReturn through **Neighbourhood map** to change workplaces. A service sees the records shared with it; absence in one workplace does not mean the record is absent everywhere.\n\n## Advance time and inspect activity\n\nOpen **Simulation controls** in the fixed bottom bar. **Pause** stops automatic time progression, and **Run** resumes it. The advance buttons pause the clock, move it forward, and process due activity.\n\nA saved request can remain pending until its scheduled time. A hospital laboratory order is due after 120 minutes for collection now or 240 minutes for the next round. A home visit is due after 90 minutes. A newly connected watch schedules its first reading after 10 minutes. Supplier orders show their own due times.\n\nRead **Activity trail** to see recent actions, their acting team or simulator actor, and simulation time. Open individual record details for that record's authorship and version history. The trail shows the most recent 20 events.\n\n## Connect an agent\n\nUse the same key as `Authorization: Bearer YOUR_TEAM_KEY`. The <a href=\"/docs/explorer/\">API explorer</a> lets you authorize requests and try them against this instance. <a href=\"/api/openapi.json\">Download the OpenAPI JSON</a> for tools that import API definitions.\n\nUse [Make your first API call](./quickstart.mdx) for a complete request sequence. Team keys, organiser tokens, and [Staff Identity staff tokens](./identity.mdx) have separate purposes."
    },
    {
      "title": "Reception Calls",
      "url": "/docs/telephony/",
      "format": "mdx",
      "content": "# Take reception calls with your team\n\nOpen **Reception Calls** from the Riverside Practice desktop. Connect to the same team world as your colleagues, then enter your receptionist name. Each open call desk appears as a separate team member.\n\n1. Choose a caller from the queue and select **Answer call**.\n2. Enable caller voices to hear the caller through your browser's speech engine. Use **Replay voice** to listen again. The transcript remains available when audio is unavailable.\n3. Select **Open in GP Records** to open that caller's patient record in another tab. Keep the call desk open to remain connected.\n4. Hold the call, transfer it to an available colleague, or record a note before ending it.\n5. Use **End call & next** to finish the call and answer the next waiting caller in one action.\n\nThe team list shows who is available, away or handling a call. Another receptionist cannot answer a call that you own. A disconnected receptionist's call returns to the queue. Completed calls retain the receptionist's name and closing note.\n\n## Verify a shared call desk\n\nRun `node scripts/verify-telephony.mjs` against a running application. The check opens two receptionists in one world and another in a separate world. It tests simultaneous answers, ownership, transfers, hold and resume, end-and-next, disconnect recovery, and patient record lookup. `pnpm smoke` includes this check.\n\nRun `node scripts/verify-telephony.mjs --restart` to also restart the local Compose application and verify that completed calls and notes survive in PostgreSQL.\n\n## Connect a client\n\nThe WebSocket endpoint is `/api/telephony/live` on the application's existing origin and port. The first message authenticates with a GP-scoped team key and a receptionist name. Do not place the API key in the URL.\n\nThe executable message schemas are `TelephonyClientMessage` and `TelephonyServerMessage` in the <a href=\"/api/openapi.json\">API catalogue</a>. Each command has a request ID. Call changes use the latest call ID and version from a snapshot. Acknowledgements and errors identify the request. Snapshots describe the committed queue and connected team members.\n\nCall audio uses the browser's installed speech voices. Voice selection and audio availability vary by browser and operating system. Calls and transcripts are generated within NHS-SIM and do not connect to a phone network."
    },
    {
      "title": "Explore the ten-year plan",
      "url": "/docs/ten-year-plan/",
      "format": "mdx",
      "content": "# Explore the ten-year plan\n\nRead the [ten-year health plan for England](https://www.gov.uk/government/publications/10-year-health-plan-for-england-fit-for-the-future/fit-for-the-future-10-year-health-plan-for-england-executive-summary), then bring a problem that interests you into the <a href=\"/control/?explore=plan\">virtual neighbourhood</a>.\n\nThe plan describes three shifts: hospital to community, analogue to digital, and sickness to prevention. The map offers places to investigate questions related to those shifts. You choose the problem and what to build. There are no assigned challenges, scores or completion requirements.\n\n## Hospital to community\n\nWhat information and support does a person need when their care moves between services?\n\nAmira Khan (`SIM-000001`) has records across the hospital, practice and pharmacy. Open the same patient in each workplace. Compare the information each service can see, who recorded it, and the work still waiting. The community visit board offers another view of support at home.\n\nUse the ordinary record actions and APIs if you want to test an idea. Opening the plan explorer or a workplace does not add scenario records or change your team's world.\n\n## Analogue to digital\n\nDoes information reach the person who needs it, with enough context to use it?\n\nEleanor Chen (`SIM-000006`) has a home dashboard, a practice record and community care records. Compare the home readings with the information available to staff. Inspect record visibility and the activity trail. The CIS2 emulator lets you explore staff sign-in and role selection separately from the team's API access.\n\nRecord sharing in this simulator is a simplified visibility model. It does not model NHS legal bases for processing or a production consent service.\n\n### Suggested project: Understood\n\nCould patients speak naturally to a GP receptionist in their own accent and get things done? Reports of callers struggling with an AI receptionist in Yorkshire offer a starting point. Read the [Guardian report](https://www.theguardian.com/society/2026/aug/20/yorkshire-rotherham-ai-gp-receptionist-cannot-understand-accent).\n\nThe long-term ambition is a proprietary UK healthcare voice model that understands regional accents, dialect and conversational nuance. For a hackathon, connect a voice prototype to the <a href=\"/gp/\">virtual GP practice</a> through the [simulator API](./api.mdx). Demonstrate a fictional caller booking an appointment, requesting a repeat prescription or leaving an attributed message, then show the resulting record in GP Records. Participants supply the voice model and calling interface; the simulator supplies the synthetic records and operational APIs.\n\nUse consenting speakers and fictional scenarios. Evaluate on speakers excluded from training, including different accents, interruptions and poor telephone audio. Compare task completion, critical transcription errors, repetition requests and latency against a baseline, including performance for each accent. Treat understanding every nuance as a research ambition, not a demonstrated capability.\n\nConfirm important details, ask useful clarification questions and provide a human handover when requested or when understanding fails. This is an optional project suggestion related to digital access, not an assigned challenge or a proposal attributed to the plan.\n\n## Sickness to prevention\n\nWhat might reveal an unmet need before someone asks for help? Who might the available data miss?\n\nExplore Eleanor's history, appointments and home readings. Consider missing context and who would need to follow up. Search the wider synthetic population if a different patient is more useful for the question you chose.\n\nThe simulator models records and operational actions. It does not determine clinical eligibility, recommend treatment or predict the health effects of an intervention.\n\n## Work in your own team world\n\nThe explorer links into existing patient records and preserves the destination when you create or connect a team. Use the same team key across workplaces. Changes made through a portal or the API are visible in that team's world, subject to each service's record visibility.\n\nFor navigation and record actions, see [patient workflows](./challenges.mdx). For requests, authentication and failure responses, see the [API reference](./api.mdx). Use Simulation controls inside a workplace to observe delayed results and the activity trail.\n\nThese exploration questions are interpretations of selected themes in the plan. Read the source for its proposals and wider context."
    }
  ]
}