Documentation Index
Fetch the complete documentation index at: https://docs.cds.marmar.life/llms.txt
Use this file to discover all available pages before exploring further.
Start Here
The TypeScript SDK provides typed helpers for interacting with the Marmar CDS
API. It wraps the most common workflows:
- Managing patients
- Creating and retrieving medication safety assessments
- Configuring and verifying webhooks
- Working with fully typed request/response and FHIR models
Install it from your private registry or directly from npm:
npm install @marmarteam/sdk
1. Initialize the client
import { createMarmarClient } from '@marmarteam/sdk';
const client = createMarmarClient({
baseUrl: 'https://cds.marmar.life/v1',
apiKey: process.env.MARMAR_API_KEY!,
});
apiKey is your tenant API key from the Dashboard.
- The API key is scoped to your tenant, so no separate tenant code is needed.
- The SDK automatically sends
Authorization: Bearer <apiKey> headers.
2. Create or update a patient
const patient = await client.createOrUpdatePatient({
externalId: 'mrn-123',
demographics: {
firstName: 'Ada',
lastName: 'Lovelace',
sex: 'FEMALE',
dateOfBirth: '1815-12-10',
},
});
console.log(
patient.created ? 'Created patient' : 'Updated patient',
patient.patientId,
);
The patient.patientId is a stable Marmar identifier that you can use in
subsequent requests.
To list patients for the current tenant:
const roster = await client.listPatients();
console.log(`Loaded ${roster.patients.length} patients`);
3. Create a medication safety assessment
const assessmentQueued = await client.createAssessment({
patient: {
patientId: patient.patientId,
},
medications: [
{
name: 'Metformin',
dosage: { amount: 500, unit: 'mg', frequency: 'BID' },
},
],
});
console.log(
`Assessment ${assessmentQueued.assessmentId} queued at ${assessmentQueued.createdAt}`,
);
Assessments are processed asynchronously. You can either:
- Receive an
assessment.completed webhook, or
- Poll the
getAssessment endpoint.
5. Retrieve an assessment
const assessment = await client.getAssessment(assessmentQueued.assessmentId);
console.log(assessment.assessmentId, assessment.summary?.text);
Use configureTenantWebhook to register where Marmar should send outbound events:
await client.configureTenantWebhook({
url: 'https://example.org/webhooks/marmar',
secret: process.env.MARMAR_WEBHOOK_SECRET!,
events: ['assessment.completed'],
});
The secret is used to sign webhook payloads. You will use the same value to
verify incoming webhooks on your server.
7. Verify webhook signatures
Use verifyWebhookSignature to authenticate webhook deliveries and guard
against tampering and replay attacks.
import { verifyWebhookSignature } from '@marmarteam/sdk';
const webhookSecret = process.env.MARMAR_WEBHOOK_SECRET!;
// Example using Express with a raw JSON body parser:
app.post(
'/webhooks/marmar',
express.raw({ type: 'application/json' }),
(req, res) => {
const signature = req.header('X-Marmar-Signature')!;
const timestamp = req.header('X-Marmar-Timestamp')!;
const payload = req.body; // Buffer or string
const result = verifyWebhookSignature({
payload,
signature,
secret: webhookSecret,
timestamp,
// Optional: toleranceSeconds (defaults to 300 seconds)
// toleranceSeconds: 600,
});
if (!result.valid) {
console.warn('Invalid Marmar webhook:', result.reason);
return res.sendStatus(400);
}
const eventJson = payload.toString('utf-8');
const event = JSON.parse(eventJson);
// Handle AssessmentCompletedEvent here
// ...
return res.sendStatus(204);
},
);
For tests or local tooling, you can generate signatures using
signWebhookPayload:
import { signWebhookPayload } from '@marmarteam/sdk';
const secret = 'test-secret';
const timestamp = Math.floor(Date.now() / 1000).toString();
const payload = { type: 'assessment.completed' };
const signature = signWebhookPayload(secret, payload, timestamp);
8. Error handling
All client methods throw an ApiError when the Marmar API returns a non‑2xx
status code.
import { ApiError } from '@marmarteam/sdk';
try {
const assessment = await client.getAssessment('non-existent-id');
console.log(assessment);
} catch (error) {
if (error instanceof ApiError) {
console.error('Marmar API error', {
status: error.status,
statusText: error.statusText,
body: error.body,
});
} else {
console.error('Unexpected error', error);
}
}
9. Advanced: using generated types and FHIR
The SDK exposes the OpenAPI types for all endpoints, including FHIR operations.
You can use these types even when making custom HTTP calls:
import type { components, operations } from '@marmarteam/sdk';
type PatientRequest = components['schemas']['PatientRequest'];
type AssessmentQueuedResponse =
operations['createAssessment']['responses'][202]['content']['application/json'];
type FhirBundle = components['schemas']['FhirBundle'];
type FhirSubmitResponse =
operations['submitFhirBundle']['responses'][200]['content']['application/json'];
For example, to submit a FHIR bundle using your own HTTP client:
const bundle: FhirBundle = {
resourceType: 'Bundle',
type: 'collection',
entry: [
// Patient, MedicationStatement, Condition, etc.
],
};
const response = await fetch('https://cds.marmar.life/v1/fhir/$submit', {
method: 'POST',
headers: {
'Content-Type': 'application/fhir+json',
'Authorization': `Bearer ${process.env.MARMAR_API_KEY!}`,
},
body: JSON.stringify(bundle),
});
const data: FhirSubmitResponse = await response.json();
For more on FHIR ingestion and search, see the dedicated FHIR guide in this
documentation.