Skip to main content

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);

6. Configure webhooks

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.