API quickstart

Create and manage bills with the API

Submit your first sandbox bill, check its status, and build your own billing workflow.

1. Get an API key

Create a sandbox organization in the developer console, then copy its key from API keys.

Server environment · .env.local
MINDBILL_API_KEY=your_sandbox_key\nAPP_ORIGIN=http://localhost:3000

For Next.js, create .env.local in the project root, alongside package.json, even if you use src/app. Use the exact name MINDBILL_API_KEY in both your environment and server code. Set APP_ORIGIN to your frontend’s exact origin, including its port. Restart your dev server after setting these values.

Keep the key on your server; do not add a NEXT_PUBLIC_ prefix or put it in Angular environment.ts. Use invented patient data in sandbox; sandbox submissions never reach payers.

2. Install the SDK

Terminal
npm install @mindbill/node@latest

Create the client on your server, with MINDBILL_API_KEY loaded in its environment. The examples use Node.js with TypeScript.

mindbill.ts
import { MindBillClient } from "@mindbill/node";

export const mindbill = new MindBillClient({
  apiKey: process.env.MINDBILL_API_KEY!,
});
Shared helper for direct API requests

Save this beside your client. The directory, search, and some action examples use api() for endpoints the SDK does not yet wrap.

mindbill-api.ts
// Server-side helper for endpoints not yet wrapped by the Node SDK.
export async function api(path: string, method = "GET", body?: unknown, key?: string) {
  const response = await fetch(`https://app.mindbill.org/partner/v2${path}`, {
    method,
    headers: {
      Authorization: `Bearer ${process.env.MINDBILL_API_KEY}`,
      ...(body === undefined ? {} : { "Content-Type": "application/json" }),
      ...(key ? { "Idempotency-Key": key } : {}),
    },
    ...(body === undefined ? {} : { body: JSON.stringify(body) }),
  });
  if (!response.ok) throw new Error(`MindBill request failed: ${response.status}`);
  return response.json();
}

These examples use a key for one organization. Multi-organization partners also set the SDK’s organizationId and the API’s x-mindbill-org-id header.

3. Create a bill

Download the example bill.json, replace its administrator placeholders with a directory entry for your test claim, and put a synthetic PDF beside it as synthetic-final-report.pdf.

Find a supported claims administrator

Use this when you do not already have a directory ID. Search by name, review the matching entry and its payer options, and copy its ID and name into your bill. Use offset to page through the results.

Directory lookup · uses the helper from step 2
import { api } from "./mindbill-api";

const directory = await api("/claims-administrators?q=Sedgwick&limit=20");
console.log(directory.results);
// Review the matching administrator and its payer options.
// Set bill.json → claim.claimsAdministrator to its id and name.

The response has a top-level results array and total. See the directory reference for routing fields.

See the example bill fields
bill.json · synthetic data
{
  "externalId": "report_demo_001",
  "billingMode": "med_legal",
  "patient": {
    "firstName": "Taylor",
    "lastName": "Example",
    "dateOfBirth": "1984-04-12",
    "address": {
      "line1": "100 Example Avenue",
      "city": "Los Angeles",
      "state": "CA",
      "postalCode": "90012"
    }
  },
  "claim": {
    "claimNumber": "DEMO-12345",
    "employer": "Example Manufacturing",
    "dateOfInjury": "2026-06-20",
    "injuryState": "CA",
    "claimsAdministrator": {
      "id": "REPLACE_WITH_DIRECTORY_ID",
      "name": "REPLACE_WITH_DIRECTORY_NAME"
    }
  },
  "service": {
    "date": "2026-08-25"
  },
  "billingProvider": {
    "name": "Example Evaluations Medical Group, Inc.",
    "taxId": "12-3456789",
    "npi": "1234567893",
    "phone": "213-555-0100",
    "address": {
      "line1": "100 Example Avenue",
      "city": "Los Angeles",
      "state": "CA",
      "postalCode": "90012"
    }
  },
  "renderingProvider": {
    "name": "Avery Example, MD",
    "npi": "1234567893",
    "taxonomy": "208D00000X"
  },
  "serviceLocation": {
    "name": "Main office",
    "address": {
      "line1": "100 Example Avenue",
      "city": "Los Angeles",
      "state": "CA",
      "postalCode": "90012"
    },
    "placeOfServiceCode": "11"
  },
  "diagnoses": [
    "M25.512"
  ],
  "serviceLines": [
    {
      "code": "ML201",
      "units": 1
    }
  ]
}
Optional: check EAMS before submitting

For a claim with an ADJ number, save a partner-linked claim first, then request an EAMS check. Run this after loading billInput and before submitting it; import api from the helper in step 2.

Before createAndSubmitBill
const savedClaim = await api("/claims", "POST", {
  patient: { ...billInput.patient, externalId: "patient_demo_001" },
  claim: { ...billInput.claim, externalId: "claim_demo_001", adjNumber: "ADJ1234567" },
}, "demo-provision-claim-001");
const preflight = await api(
  `/claims/${encodeURIComponent(savedClaim.claimId)}/eams-preflight`, "POST", {},
);
console.log(preflight.status, preflight.candidates);
// Review candidate claims administrators before choosing bill.claim.claimsAdministrator.

EAMS returns candidate administrators for review, not a verified current adjuster. Sandbox returns not_performed. Review the candidates and update your bill’s administrator before submission.

quickstart.ts · run on your server
import { readFile } from "node:fs/promises";
import { mindbill } from "./mindbill";

const billInput = JSON.parse(await readFile("./bill.json", "utf8"));
const report = await readFile("./synthetic-final-report.pdf");

const bill = await mindbill.createAndSubmitBill({
  bill: billInput,
  submission: { route: "ebill" },
  documents: [{
    filename: "synthetic-final-report.pdf",
    documentType: "final_report",
    contentBase64: report.toString("base64"),
  }],
}, "demo-create-report-001");

console.log(bill.id);

This creates and submits the bill; it does not save a draft. Keep bill.id for the steps below. Reuse the idempotency key only when retrying this exact submission; use a new key for a new operation. Sandbox submissions never reach payers.

4. Get the bill’s status

Continue in quickstart.ts
const status = await mindbill.getBillStatus(bill.id);
console.log(status);

// Full bill details and the actions currently available:
const currentBill = await mindbill.getBill(bill.id);
const lifecycle = await mindbill.getBillLifecycle(bill.id);

Use status for a summary and lifecycle for the bill’s history and available actions. For automatic updates, add webhooks.

5. Perform an action

Choose an action allowed by the current lifecycle. These are separate examples for eligible bills.

Correct a rejected bill by sending the complete corrected bill snapshot.

Correct and resubmit
import { api } from "./mindbill-api";

await api(`/bills/${encodeURIComponent(bill.id)}/actions`, "POST", {
  action: "resubmit", reason: "Corrected the claim number.",
  bill: { ...billInput, claim: { ...billInput.claim, claimNumber: "DEMO-12345-CORRECTED" } },
  submission: { route: "ebill" },
}, "demo-resubmit-001");

Direct request examples use api() from step 2. See bill actions for the full action list, required evidence, and eligibility rules.

7. Save organization and provider data Optional

Save your practice profile once, then use savedProviderId in later bills. The profile write requires orgs:write; reading it requires orgs:read.

Save and reference a billing provider
import { api } from "./mindbill-api";

const profile = await api("/organization/billing-profile", "PUT", {
  billingProviders: [{
    externalId: "practice_demo_001",
    name: "Example Evaluations Medical Group, Inc.",
    taxId: "12-3456789", npi: "1234567893", billType: "Professional",
    phone: "213-555-0100", billingStreet: "100 Example Avenue",
    billingCity: "Los Angeles", billingState: "CA", billingZip: "90012",
  }],
}, "demo-save-provider-001");
const provider = profile.data.billingProviders.find(
  (item: { externalId?: string }) => item.externalId === "practice_demo_001",
);
if (!provider) throw new Error("Saved provider was not returned.");
const nextBillInput = { ...billInput, billingProvider: { savedProviderId: provider.id } };

Updates match providers by ID or external ID and do not delete existing entries. Continue sending rendering-provider and service-location fields in each bill snapshot. See the API reference for organization provisioning and saved profiles.

Before using a live key, complete the sandbox checks. For treatment services, follow the treatment billing quickstart.