Learn MindBill
Add billing to your product
In this tutorial, you will create one draft bill, attach its payer packet, and put the complete review and billing lifecycle inside your product.
Choose your client
REST is the contract. The Node, React, and Angular packages remove boilerplate but are optional.
# Pick only what your app uses
npm install @mindbill/node@0.8
npm install @mindbill/react@0.14
npm install @mindbill/angular@0.2
# Server environment — never expose this value to browser code
MINDBILL_API_KEY=mb_test_...
MINDBILL_ORG_ID=org_...// Any server runtime with fetch
export async function mindbill(path, init = {}) {
return fetch(`https://app.mindbill.org/api/partner/v2${path}`, {
...init,
headers: {
Authorization: `Bearer ${process.env.MINDBILL_API_KEY}`,
"Content-Type": "application/json",
...init.headers,
},
});
}
// Prefer typed calls? @mindbill/node wraps the same HTTP API.
// const mindbill = new MindBillClient({ apiKey, organizationId });Your API key appears only in server code. Do not prefix it with NEXT_PUBLIC_.
Create one bill snapshot
Send the exact values that should print on this bill. Reusable provider and location profiles are optional.
// Create the draft on your server.
const bill = await mindbill.createBill({
externalId: evaluation.id,
billingMode: "med_legal",
patient: {
externalId: patient.id,
firstName: patient.firstName,
lastName: patient.lastName,
dateOfBirth: patient.dateOfBirth,
address: patient.address,
},
claim: {
externalId: claim.id,
claimNumber: claim.number,
adjNumber: claim.adjNumber,
employer: claim.employer,
dateOfInjury: claim.dateOfInjury,
claimsAdministrator: { name: claim.payerName },
},
service: { date: evaluation.examDate },
billingProvider,
renderingProvider,
serviceLocation,
diagnoses: report.diagnosisCodes,
serviceLines: [
{ code: "ML201", modifiers: ["95"], units: 1 },
],
}, `bill:${evaluation.id}`);
// This is the only new identifier your application must retain.
await db.evaluation.update({ mindbillBillId: bill.id });// IME, treatment, malpractice, and other professional billing
const bill = await mindbill.createBill({
externalId: encounter.id,
billingMode: "professional",
patient,
claim,
service: { date: encounter.date },
billingProvider,
renderingProvider,
serviceLocation,
diagnoses: ["M25.512"],
serviceLines: [
{ code: "99205", units: 1, charge: 475.00 },
{ code: "99080", units: 1, charge: 125.00 },
],
}, `bill:${encounter.id}`);Your patient, case, report, and provider records can remain canonical.
Use bill.id for review, status, EORs, payments, appeals, and close.
Reuse the same idempotency key when retrying the same logical write.
Attach the payer packet
Documents are part of the bill, but inclusion is always explicit and reversible before submission.
// Add only documents intended for the payer packet.
await mindbill.uploadBillDocument(bill.id, {
file: finalReport,
filename: "final-report.pdf",
documentType: "final_report",
externalId: report.id,
}, `final-report:${report.id}`);
// Repeat for proof_of_service, form_122, w9, or other support.
// Never add medical_records unless the user intentionally selects them.Final report, proof of service, Form 122, W-9, letter of attestation, and required forms.
Medical records and arbitrary supporting documents.
The attorney report-service packet is not the payer billing packet.
Mint a browser session
This is the only server route the connected component needs after bill creation.
// Express example. The same four steps work in any server framework.
app.post("/api/mindbill/session", async (request, response) => {
const user = await requireUser(request);
const { billId } = request.body;
// Use your own authorization model here.
await assertUserCanAccessBill(user, billId);
const session = await mindbill.createBrowserSession({
component: "bill-review",
billId,
allowedOrigin: "https://your-product.example",
expiresIn: 900,
});
response.json({
token: session.token,
expiresAt: session.expiresAt,
});
});The token is scoped to one bill, bound to the exact browser origin, and expires after 15 minutes. Your permanent key never reaches the browser.
Render the billing lifecycle
The component loads the draft, saves edits, searches payers, manages attachments, submits, and shows the next valid action.
// components/Billing.tsx
"use client";
import { ConnectedBillLifecycle } from "@mindbill/react";
import "@mindbill/react/styles.css";
export function Billing({ billId }: { billId: string }) {
return (
<ConnectedBillLifecycle
billId={billId}
sessionEndpoint="/api/mindbill/session"
appearance={{
accentColor: "#17666b",
fontFamily: "inherit",
}}
/>
);
}import { Component } from "@angular/core";
import { MindBillBillLifecycleComponent } from "@mindbill/angular";
@Component({
standalone: true,
imports: [MindBillBillLifecycleComponent],
template: `
<mindbill-bill-lifecycle
[billId]="billId"
sessionEndpoint="/api/mindbill/session"
[appearance]="{ preset: 'clinical-blue' }"
/>
`,
})
export class BillingComponent {
billId = "bill_...";
}What you should see
A prefilled, editable bill—not another onboarding flow
Known patient, claim, provider, location, diagnosis, and service values are already present. Users fill only missing fields, verify the payer packet, and submit.
See React and Angular examples →Read status and act
One bill ID follows the claim from draft through payment, denial, resubmission, and close.
// Server-side reconciliation or your own reporting UI
const { data: status } = await mindbill.getBillStatus(billId);
console.log(status.state, status.balanceDue, status.availableActions);
// In React, ConnectedBillLifecycle already refreshes status and renders
// only the actions valid for the current bill state.const review = await mindbill.createBillReview(billId, {
type: "second_review",
reason: "The report satisfies the documented criteria.",
attachmentIds: supportingAttachmentIds,
}, `second-review:${yourReviewId}`);
await mindbill.submitBillReview(
billId,
review.data.id,
`submit-review:${yourReviewId}`,
);// 1. Verify MindBill-Signature against the exact raw body.
// 2. Deduplicate on event.id.
// 3. Re-read authoritative state instead of trusting cached UI state.
if (event.type === "bill.status_changed") {
const { data: status } = await mindbill.getBillStatus(event.data.billId);
await db.billingStatus.upsert(status);
}Where to go next
Choose the level of control you need
API reference
Every operation
Use the SDK for typed application code. Use these operation pages for request fields, response schemas, errors, and raw HTTP examples.
Bills
GETList bills/billsPOSTCreate a bill/billsGETGet a bill/bills/{billId}PATCHUpdate a bill/bills/{billId}Documents
GETList payer-packet documents/bills/{billId}/documentsPOSTAttach a PDF/bills/{billId}/documentsGETDownload a PDF/bills/{billId}/documents/{documentId}DELETERemove a PDF/bills/{billId}/documents/{documentId}Lifecycle
POSTSubmit a bill/bills/{billId}/submissionsGETGet bill status/bills/{billId}/statusGETGet EOR and payment details/bills/{billId}/eorPOSTPerform the next bill action/bills/{billId}/actionsGETList bill reviews/bills/{billId}/reviewsPOSTCreate a bill review/bills/{billId}/reviewsGETGet a bill review/bills/{billId}/reviews/{reviewId}POSTSubmit a bill review/bills/{billId}/reviews/{reviewId}/submissionsBrowser sessions
POSTMint an origin-bound browser session/browser-sessions