Integration Recipes
Last updated: 2026-02-18
Step-by-step guides for the most common enterprise integration scenarios. Each recipe is self-contained — complete it start to finish and you have a working integration.
Prerequisites for all recipes: Complete the Quickstart first (you need an API key).
Recipe 1: HRIS Roster Sync via SCIM
Goal: Automatically provision and deprovision employees from your identity provider (Okta, Azure AD, OneLogin) into Bounded Health.
Time: ~20 minutes
1.1 Enable SCIM for your employer
curl -X POST "$MT_HOST/api/employer/sso" \
-H "x-api-key: $MT_API_KEY" \
-H "x-api-version: 2026-02-01" \
-H "content-type: application/json" \
-d '{
"scimEnabled": true
}'
Response includes your SCIM bearer token (shown once):
{
"scimEnabled": true,
"scimBearerToken": "scim_tok_a1b2c3..."
}
Save this token — your IdP will use it.
export SCIM_TOKEN="scim_tok_a1b2c3..."
1.2 Configure your IdP
In your identity provider (Okta, Azure AD, etc.):
- SCIM endpoint:
https://<YOUR_HOST>/api/scim/v2 - Auth method: Bearer token
- Token: The
scimBearerTokenfrom step 1.1
1.3 Provision a user manually (for testing)
curl -X POST "$MT_HOST/api/scim/v2/Users" \
-H "authorization: Bearer $SCIM_TOKEN" \
-H "content-type: application/scim+json" \
-d '{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "jane.doe@acme.com",
"name": { "givenName": "Jane", "familyName": "Doe" },
"emails": [{ "value": "jane.doe@acme.com", "primary": true }],
"active": true
}'
Expected response (201 Created):
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"id": "user_xyz789",
"userName": "jane.doe@acme.com",
"active": true,
"meta": { "resourceType": "User", "created": "2026-02-18T12:00:00Z" }
}
1.4 Deactivate a user
curl -X PATCH "$MT_HOST/api/scim/v2/Users/user_xyz789" \
-H "authorization: Bearer $SCIM_TOKEN" \
-H "content-type: application/scim+json" \
-d '{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [{ "op": "replace", "path": "active", "value": false }]
}'
This soft-deactivates the user and revokes their API keys and sessions.
1.5 Create a directory group
curl -X POST "$MT_HOST/api/scim/v2/Groups" \
-H "authorization: Bearer $SCIM_TOKEN" \
-H "content-type: application/scim+json" \
-d '{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"displayName": "Engineering Team",
"members": [{ "value": "user_xyz789" }]
}'
Recipe 2: Bulk FHIR Patient Export
Goal: Export all patient data as FHIR-compliant NDJSON for loading into your data warehouse or analytics platform.
Time: ~10 minutes
2.1 Start the export
curl -s "$MT_HOST/api/fhir/\$export" \
-H "x-api-key: $MT_API_KEY" \
-H "x-api-version: 2026-02-01" \
-H "accept: application/fhir+json" \
-H "prefer: respond-async" \
-D - -o /dev/null
Expected response (202 Accepted):
HTTP/2 202
content-location: /api/fhir/bulk-export/exp_abc123
retry-after: 2
x-progress: in-progress
Copy the content-location URL.
2.2 Poll for completion
curl -s "$MT_HOST/api/fhir/bulk-export/exp_abc123" \
-H "x-api-key: $MT_API_KEY" \
-H "x-api-version: 2026-02-01"
While running:
{ "status": "running", "progress": "in-progress" }
When complete:
{
"status": "succeeded",
"output": [
{
"type": "Patient",
"url": "/api/fhir/bulk-export/exp_abc123/download",
"count": 1250
}
]
}
2.3 Download the NDJSON
curl -s "$MT_HOST/api/fhir/bulk-export/exp_abc123/download" \
-H "x-api-key: $MT_API_KEY" \
-H "x-api-version: 2026-02-01" \
-o patients.ndjson
Each line is a complete FHIR Patient JSON resource.
2.4 Group-level export (optional)
Export only patients in a specific SCIM group:
curl -s "$MT_HOST/api/fhir/Group/grp_abc123/\$export" \
-H "x-api-key: $MT_API_KEY" \
-H "x-api-version: 2026-02-01" \
-H "accept: application/fhir+json" \
-H "prefer: respond-async"
Recipe 3: Real-Time Event Processing (Webhooks)
Goal: React to platform events (cohort uploads, simulation completions, export readiness) in real time.
Time: ~15 minutes
3.1 Create your webhook receiver
Node.js / Express:
import crypto from "crypto";
import express from "express";
const app = express();
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!;
// Track processed event IDs for idempotency
const processedEvents = new Set<string>();
app.post(
"/webhooks/Bounded Health",
express.raw({ type: "application/json" }),
(req, res) => {
const rawBody = req.body.toString("utf8");
// Step 1: Verify signature
const signature = req.headers["x-webhook-signature"] as string;
const expected = `sha256=${crypto
.createHmac("sha256", WEBHOOK_SECRET)
.update(rawBody)
.digest("hex")}`;
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).send("Invalid signature");
}
// Step 2: Check idempotency
const webhookId = req.headers["x-webhook-id"] as string;
if (processedEvents.has(webhookId)) {
return res.status(200).send("Already processed");
}
// Step 3: Process the event
const event = JSON.parse(rawBody);
console.log(`[${event.event}]`, JSON.stringify(event.data, null, 2));
switch (event.event) {
case "cohort.uploaded":
console.log("New cohort data available — trigger ETL pipeline");
break;
case "simulation.completed":
console.log("Simulation finished — update dashboards");
break;
case "export.ready":
console.log("Export ready for download — notify ops team");
break;
default:
console.log("Unhandled event type:", event.event);
}
// Step 4: Mark as processed and respond quickly
processedEvents.add(webhookId);
res.status(200).send("OK");
}
);
app.listen(3001, () => console.log("Webhook receiver running on :3001"));
3.2 Register the webhook
curl -X POST "$MT_HOST/api/employer/webhooks" \
-H "x-api-key: $MT_API_KEY" \
-H "x-api-version: 2026-02-01" \
-H "content-type: application/json" \
-d '{
"url": "https://your-server.example.com/webhooks/Bounded Health",
"events": ["cohort.uploaded", "simulation.completed", "export.ready"]
}'
3.3 Test it
curl -X POST "$MT_HOST/api/employer/webhooks/<WEBHOOK_ID>/test" \
-H "x-api-key: $MT_API_KEY" \
-H "x-api-version: 2026-02-01"
Watch your terminal for the verified event.
Recipe 4: SSO Setup (SAML with Okta)
Goal: Allow your employees to sign in to Bounded Health using their Okta credentials.
Time: ~30 minutes
4.1 Create a SAML app in Okta
In your Okta admin panel:
- Go to Applications → Create App Integration → SAML 2.0
- Configure:
- Single sign-on URL (ACS):
https://<YOUR_MT_HOST>/api/auth/saml/callback/<EMPLOYER_ID> - Audience URI (SP Entity ID):
https://<YOUR_MT_HOST>/api/auth/saml/metadata/<EMPLOYER_ID> - Name ID format:
EmailAddress - Attribute statements:
email→user.emailfirstName→user.firstNamelastName→user.lastName
- Single sign-on URL (ACS):
- Download the IdP metadata URL from Okta
4.2 Configure SAML in Bounded Health
curl -X POST "$MT_HOST/api/employer/sso" \
-H "x-api-key: $MT_API_KEY" \
-H "x-api-version: 2026-02-01" \
-H "content-type: application/json" \
-d '{
"samlEnabled": true,
"samlMetadataUrl": "https://your-org.okta.com/app/abc123/sso/saml/metadata",
"samlIssuer": "https://your-org.okta.com",
"samlNameIdFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
"samlEmailAttribute": "email",
"samlFirstNameAttribute": "firstName",
"samlLastNameAttribute": "lastName"
}'
4.3 Test the login flow
Open in a browser:
https://<YOUR_MT_HOST>/api/auth/saml/start/<EMPLOYER_ID>
This should redirect you to Okta → you sign in → you land back in Bounded Health authenticated.
4.4 Azure AD configuration
For Azure AD, the steps are similar:
- Reply URL: Same ACS URL as above
- Identifier: Same SP Entity ID as above
- User Attributes: Map
email,givenname,surname - Use the App Federation Metadata URL as
samlMetadataUrl
Recipe 5: SDK Installation and First Call
Goal: Use the typed TypeScript SDK instead of raw HTTP calls.
Time: ~5 minutes
5.1 Install the SDK
npm install @Bounded Health/enterprise-sdk
5.2 Initialize the client
import { Bounded HealthClient } from "@Bounded Health/enterprise-sdk";
const client = new Bounded HealthClient({
apiKey: process.env.MT_API_KEY!,
baseUrl: process.env.MT_HOST!,
apiVersion: "2026-02-01",
});
5.3 Make API calls
// List patients
const patients = await client.fhir.searchPatients({ _count: 10 });
console.log(`Found ${patients.total} patients`);
// List webhook subscriptions
const webhooks = await client.webhooks.list();
console.log(`Active webhooks: ${webhooks.length}`);
// Trigger an export
const exportJob = await client.exports.createCsvExport({
exportType: "simulation_results",
});
console.log(`Export job queued: ${exportJob.exportId}`);
5.4 Verify webhook signatures with SDK helpers
import { validateInboundWebhook } from "@Bounded Health/enterprise-sdk/webhooks";
// In your webhook handler:
const result = validateInboundWebhook({
rawBody: req.body.toString("utf8"),
signature: req.headers["x-webhook-signature"] as string,
secret: process.env.WEBHOOK_SECRET!,
webhookId: req.headers["x-webhook-id"] as string,
event: req.headers["x-webhook-event"] as string,
version: req.headers["x-webhook-version"] as string,
timestamp: req.headers["x-webhook-timestamp"] as string,
});
if (!result.valid) {
console.error("Webhook validation failed:", result.error);
return res.status(401).send("Invalid");
}