Palamond Docs
Building Patient-Facing Apps

Reading and Writing

Read the record with FHIR search; write only what the patient authors.

The record plane (/v1/fhir) is where your app reads everything and writes almost nothing. The SDK's helpers are typed against @palamond/sdk/fhir, so what you get back matches what you asked for.

Read a single resource

Read by type and id, or follow a reference from another resource.

import type { Patient, Questionnaire } from '@palamond/sdk/fhir';

const patient = await palamond.readResource('Patient', patientId);

// Follow a Reference found on another resource.
const questionnaire = await palamond.readReference<Questionnaire>(task.focus);

searchResources returns a typed array. search returns the full Bundle (for totals and paging). searchOne returns the first match or undefined.

// The patient's observations, newest first.
const observations = await palamond.searchResources('Observation', {
  category: 'vital-signs',
  _sort: '-date',
  _count: 50,
});

// Just the latest weight.
const latestWeight = await palamond.searchOne('Observation', {
  code: '29463-7',
  _sort: '-date',
});

Every search is automatically scoped to the signed-in patient's record; you do not need to filter by patient yourself.

Common search parameters

  • _sort: sort field, prefix with - for descending (-date).
  • _count and _offset: page size and offset.
  • _lastUpdated: filter by change time (gt2026-01-01).
  • status, category, code: filter clinical resources by their coded fields.
  • date: filter by clinical date, with prefixes like ge and lt.

Fetch related resources in one round trip: _include pulls resources this one points to, _revinclude pulls resources pointing back at it. Included entries carry search.mode: 'include' in the Bundle.

const bundle = await palamond.search('Encounter', {
  _include: 'Encounter:appointment',
});

The write matrix

The record accepts writes only where the patient is the author of record:

ResourceMethodsUsed for
PatientPUT (own resource)The patient's demographics and contact details.
QuestionnaireResponsePOST, PUTThe patient's questionnaire answers, including drafts.

Everything else changes through a workflow endpoint, and a write attempt answers 405 naming the endpoint that owns the change. That makes wrong turns self-correcting: the error tells you where to go.

// Update the patient's own contact details.
const me = await palamond.readResource('Patient', palamond.getProfile()!.id!);
me.telecom = [{ system: 'email', value: '[email protected]' }];
await palamond.updateResource(me);
// Author questionnaire answers (a draft the patient can come back to).
import type { QuestionnaireResponse } from '@palamond/sdk/fhir';

const draft = await palamond.createResource<QuestionnaireResponse>({
  resourceType: 'QuestionnaireResponse',
  status: 'in-progress',
  questionnaire: `${questionnaire.url}|${questionnaire.version}`,
  subject: { reference: `Patient/${patientId}` },
  item: [],
});

Wondering why you cannot create an Observation directly? Measurements are recorded by completing their task (POST /v1/tasks/{id}/complete), so the platform validates values and coding once, for every app. The same logic applies across the record: the workflow endpoint is the write path.

Pagination

Use _count with _offset to walk pages, or read the Bundle's total with _total=accurate when you need a count.

const page = await palamond.searchResources('Observation', {
  _sort: '-date',
  _count: 20,
  _offset: 40,
});

Next: Querying for keeping these reads fast and fresh in a UI.

On this page