> ## 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.

# SDK

# 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:

```bash theme={null}
npm install @marmarteam/sdk
```

## 1. Initialize the client

```ts theme={null}
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](https://portal.marmar.life).
* 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

```ts theme={null}
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:

```ts theme={null}
const roster = await client.listPatients();

console.log(`Loaded ${roster.patients.length} patients`);
```

## 3. Create a medication safety assessment

```ts theme={null}
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

```ts theme={null}
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:

```ts theme={null}
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.

```ts theme={null}
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`:

```ts theme={null}
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.

```ts theme={null}
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:

```ts theme={null}
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:

```ts theme={null}
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.
