Integration API

Version 1.0.0

LastMileRCM provides a webhook-based integration API that allows case management systems, EMRs, and other healthcare platforms to submit patient demographics, provider information, insurance coverage, and claims data directly into LastMileRCM for processing. The tenant is automatically resolved from your API key — no tenant identification is needed in the request body.

Base URL: https://lastmilercm.com

Authentication

All API requests must include an API key in the x-api-key header. API keys are provisioned per integration partner during onboarding.

Header:x-api-key
Format:lmr_xxxxxxxx...(min 32 characters)

Rate Limiting

Requests are rate-limited per partner. Two limits apply: 60 requests per minute and 10,000 requests per day (rolling from midnight UTC). Exceeding either limit returns 429 Too Many Requests. The daily limit is configurable per partner.

Idempotency

To prevent duplicate processing, include a unique idempotency_key in the metadata object. If a payload with the same key has already been processed, the API returns 200 with a message confirming it was already processed. No entities are re-created. If the payload is still being processed, the API returns 202.

Demo Tenants

Demo tenants have configurable limits on patient counts, claim counts, and feature access. If a limit would be exceeded by the incoming batch, the entire request is blocked and the API returns 403 Forbidden with a descriptive message indicating the current usage, limit, and how many slots remain.

Limits are checked against the batch size before processing begins — partial batches are not processed. For example, if 20/25 patients are used and a batch of 10 is submitted, the entire batch is rejected.

POST/api/webhooks/cms/inbound

Ingest Patients, Providers, Coverage & Claims

Receives patient and claim data from external case management systems. The endpoint validates the API key, processes each patient record, and creates the corresponding entities in the tenant's account.

Processing Flow

  1. 1Authentication — Validate API key and check IP allowlist
  2. 2Rate Limiting — Check per-minute and per-day quotas
  3. 3Idempotency — Skip if payload was already processed
  4. 4Tenant Resolution — Resolve tenant from the partner's default_tenant_id
  5. 5Demo Limits — Check patient/claim caps for demo tenants
  6. 6
    Entity Processing — For each patient:
    • Match by external_id — create new or update existing patient
    • Validate provider via NPI Registry (NPPES) lookup and enrich from registry data
    • Match payer against the Stedi payer directory
    • Create or update coverage (matched by patient + payer + coverage type)
    • Create draft provider-payer enrollment (if pair doesn't exist)
    • Create or update claim (matched by patient + service date) with diagnoses and service lines

Upsert Behavior: Patients, coverages, and claims support upsert. If a matching record exists and the incoming data differs, the record is updated. If the data is identical, it is reported as "existing" with no DB write. See the section for matching keys.

Per-Item Tracking: The response includes an items array showing what happened to each patient in the batch — which entities were created, updated, or matched, plus any item-level errors with standardized codes.

Draft Enrollments: When a provider-payer pair is seen for the first time, a draft enrollment is automatically created. This appears in the provider's detail page and can be activated once the provider is registered with the clearinghouse.

Request Body

patientsarrayRequired

Array of patient records to process. Always use an array, even for a single patient.

See Patient schema below

metadataobject

Optional metadata about the submission

Patient

external_idstring

Your system's unique ID for this patient. Used for deduplication — if a patient with this external_id already exists for the tenant, the existing record is reused.

Example: CMS-PAT-001

first_namestringRequired

Example: Jane

last_namestringRequired

Example: Doe

date_of_birthstringdateRequired

Example: 1985-03-15

genderstring

Normalized to M/F internally

MFMaleFemale
phonestring

Example: 603-555-0100

emailstringemail

Example: jane.doe@example.com

addressobject

Patient mailing address

Plus , , and objects (see below)

Provider

The NPI is validated against the CMS NPI Registry (NPPES). If valid, the provider record is enriched with official registry data (name, address, taxonomy, credentials). If the NPI already exists for this tenant, the existing provider is reused.

npistringRequired

National Provider Identifier (10 digits). Must be a valid, active NPI in the CMS registry.

Example: 1234567890

tax_idstring

EIN or SSN (9 digits). Encrypted at rest.

Example: 123456789

tax_id_typestring

Type of tax identifier

EINSSN
contact_emailstringemail

Contact email for enrollment communications. Encrypted at rest.

Example: billing@provider.com

Coverage

The payer name is matched against the Stedi payer directory. Only recognized payers are accepted. Matching tries exact payer_id, RPC search, and fuzzy display name matching.

payer_namestringRequired

Insurance company name. Matched against the payer directory.

Example: Blue Cross Blue Shield

payer_idstring

Optional Stedi payer ID or primary payer ID for exact matching. Takes priority over name matching.

Example: BCBS

member_idstringRequired

Insurance member/subscriber ID. Encrypted at rest with a searchable last-4 index.

Example: XYZ123456789

group_numberstring

Example: GRP-001

plan_namestring

Example: PPO Gold

coverage_typestring

Insurance coverage tier

primarysecondarytertiary

Default: primary

effective_datestringdate

Example: 2024-01-01

termination_datestringdate

Example: 2024-12-31

Claim

Requires both a provider and coverage to be successfully resolved. Claims are created in draft status.

service_datestringdateRequired

Date of service

Example: 2024-06-15

place_of_servicestring

CMS Place of Service code (e.g., 11=Office, 02=Telehealth)

Default: 11

prior_auth_numberstring

Prior authorization number if applicable

Example: AUTH-12345

total_chargenumber

Total charge amount. If omitted, calculated as sum of service line charges.

diagnosesarrayRequired

ICD-10 diagnosis codes. Decimal points are stripped automatically.

service_linesarrayRequired

CPT/HCPCS procedure codes with charges

Deduplication & Upsert

The API supports re-submitting data with changes. When a matching record exists, the API compares fields and only writes to the database if something actually changed. Identical re-submissions are reported as "existing" with no database write.

Patients

Match key: external_id (per tenant)

Behavior: If a patient with the same external_id exists, the API compares first_name, last_name, date_of_birth, gender, phone, email, and address fields. Only changed fields are updated.

Action values: created updated existing

Coverages

Match key: patient_id + payer_id + coverage_type

Behavior: If a coverage with the same composite key exists, the API compares member_id, group_number, plan_name, effective_date, and termination_date. Only changed fields are updated.

Action values: created updated existing

Claims

Match key: patient_id + statement_from_date (service_date)

Behavior: If a claim with the same patient and service date exists, the API performs a deep comparison of claim fields (place_of_service, prior_auth_number, total_charge_amount), diagnosis codes, and all service line details (procedure codes, units, charges, modifiers). If anything differs, the claim is updated with the new data. Diagnoses and service lines are fully replaced on update.

Action values: created updated existing

Providers

Match key: NPI (per tenant)

Behavior: If a provider with the same NPI exists, the existing record is reused. No updates are performed. Provider data is enriched from the NPPES registry on initial creation.

Action values: created existing

Responses

Every response includes a top-level code field with a machine-readable status code, and a message field with a human-readable summary. See the section for the complete reference.

Success Response Schema

successboolean

true when all items processed without fatal errors

codestring

Machine-readable status code (e.g., OK, COMPLETED_WITH_ERRORS)

messagestring

Human-readable summary of what was processed

webhook_idstringuuid

Unique ID for this invocation — use for support inquiries

processedobject

Aggregate counts for each entity type

patientsobject

Patient processing counts

claimsobject

Claim processing counts

providersobject

Provider processing counts

payersobject

Payer resolution counts

coveragesobject

Coverage processing counts

enrollmentsobject

Provider-payer enrollment counts

itemsarray

Per-item detail — one entry per patient in the batch

indexinteger

Zero-based position in the input patients array

identifierstring

The patient's external_id (or 'unknown' if missing)

statusstring

Overall outcome for this item

successpartialfailed
patientobject

Patient outcome (present if patient was processed)

providerobject

Provider outcome (present if provider was resolved)

payerobject

Payer outcome (present if payer was resolved)

coverageobject

Coverage outcome (present if coverage was processed)

claimobject

Claim outcome (present if claim was processed)

enrollmentobject

Enrollment outcome (present if enrollment was processed)

errorsarray

Item-level errors (present only if this item had issues)

errorsarray

Legacy error summary array — flat list of human-readable error strings. Present only if there were item-level issues.

HTTP Status Codes

200Success / Completed with Errors

Payload processed. Code is OK if all items succeeded, or COMPLETED_WITH_ERRORS if some items had non-fatal issues (e.g., payer not found). Also used for idempotent responses (IDEMPOTENCY_COMPLETED).

202In Progress

Idempotency key matched a payload still being processed. Code: IDEMPOTENCY_PENDING.

400Bad Request

Invalid payload structure or missing required fields. Codes: PAYLOAD_INVALID, PAYLOAD_MISSING_PATIENTS, TENANT_NOT_CONFIGURED.

401Unauthorized

Missing or invalid API key. Codes: AUTH_MISSING_KEY, AUTH_INVALID_FORMAT, AUTH_INVALID_KEY, AUTH_INACTIVE.

403Forbidden

IP not in allowlist or demo limit exceeded. Codes: IP_NOT_ALLOWED, LIMIT_PATIENTS, LIMIT_CLAIMS.

429Rate Limited

Exceeded rate limits. Codes: RATE_LIMIT_MINUTE, RATE_LIMIT_DAILY.

500Server Error

Internal processing failure. Codes: INTERNAL_ERROR, STORAGE_FAILED.

Error Codes Reference

All errors include a machine-readable code field. Request-level codes appear in the top-level response. Item-level codes appear in items[].errors[].code.

Request-Level Codes

CodeHTTPDescription
OK200All items processed successfully
COMPLETED_WITH_ERRORS200Some items had non-fatal errors (e.g., payer not found)
IDEMPOTENCY_COMPLETED200Payload was already processed (idempotency key matched)
IDEMPOTENCY_PENDING202Payload is still being processed
AUTH_MISSING_KEY401x-api-key header is missing
AUTH_INVALID_FORMAT401API key does not start with lmr_ or is too short
AUTH_INVALID_KEY401API key not found in the system
AUTH_INACTIVE401API key exists but is deactivated
IP_NOT_ALLOWED403Request IP not in partner's allowlist
LIMIT_PATIENTS403Batch would exceed demo tenant patient limit
LIMIT_CLAIMS403Batch would exceed demo tenant claim limit
RATE_LIMIT_MINUTE429Exceeded 60 requests per minute
RATE_LIMIT_DAILY429Exceeded 10,000 requests per day
PAYLOAD_INVALID400Request body is not valid JSON
PAYLOAD_MISSING_PATIENTS400patients array is missing or empty
TENANT_NOT_CONFIGURED400Partner does not have a default tenant assigned
INTERNAL_ERROR500Unexpected server error during processing
STORAGE_FAILED500Failed to store raw payload for audit trail

Item-Level Codes

These codes appear in items[].errors[].code and indicate what went wrong for a specific patient in the batch. Item-level errors do not fail the entire request.

CodeDescription
PATIENT_MISSING_FIELDSPatient is missing required fields (first_name, last_name, date_of_birth)
PATIENT_CREATE_FAILEDDatabase error creating the patient record
PATIENT_UPDATE_FAILEDDatabase error updating an existing patient
PATIENT_DEDUP_FAILEDError during deduplication check (external_id lookup)
NPI_INVALID_FORMATNPI is not a valid 10-digit number
NPI_NOT_FOUNDNPI not found in the CMS NPPES registry
PROVIDER_CREATE_FAILEDDatabase error creating the provider record
PAYER_NOT_FOUNDPayer name could not be matched in the payer directory
COVERAGE_CREATE_FAILEDDatabase error creating the coverage record
COVERAGE_UPDATE_FAILEDDatabase error updating an existing coverage
CLAIM_MISSING_FIELDSClaim is missing required fields (diagnoses, service_lines)
CLAIM_MISSING_PROVIDERCannot create claim without a valid provider
CLAIM_MISSING_COVERAGECannot create claim without valid coverage (payer not found)
CLAIM_DUPLICATEClaim with same patient + service date already exists (legacy)
CLAIM_CREATE_FAILEDDatabase error creating the claim record
CLAIM_UPDATE_FAILEDDatabase error updating an existing claim

Need Help?

Contact ananth@thehealthcolab.com for API key provisioning, integration support, or questions.

Code Examples

REQUEST
curl --request POST \
  --url "https://lastmilercm.com/api/webhooks/cms/inbound" \
  --header "x-api-key: lmr_your_api_key_here" \
  --header "Content-Type: application/json" \
  --data '{
  "patients": [
    {
      "external_id": "CMS-PAT-001",
      "first_name": "Jane",
      "last_name": "Doe",
      "date_of_birth": "1985-03-15",
      "gender": "F",
      "phone": "603-555-0100",
      "email": "jane.doe@example.com",
      "address": {
        "line1": "123 Main St",
        "line2": "Apt 4B",
        "city": "Manchester",
        "state": "NH",
        "postal_code": "03101"
      },
      "provider": {
        "npi": "1003000126",
        "tax_id": "061234567",
        "tax_id_type": "EIN",
        "contact_email": "billing@provider.com"
      },
      "coverage": {
        "payer_name": "Blue Cross Blue Shield",
        "member_id": "XYZ123456789",
        "group_number": "GRP-001",
        "plan_name": "PPO Gold",
        "coverage_type": "primary",
        "effective_date": "2024-01-01"
      },
      "claim": {
        "service_date": "2024-06-15",
        "place_of_service": "11",
        "prior_auth_number": "AUTH-12345",
        "diagnoses": [
          { "code": "F32.1", "type": "principal" },
          { "code": "F41.1", "type": "secondary" }
        ],
        "service_lines": [
          {
            "procedure_code": "90837",
            "description": "Psychotherapy, 60 min",
            "units": 1,
            "charge_amount": 150.00,
            "modifier_1": "95"
          }
        ]
      }
    }
  ],
  "metadata": {
    "source_system": "YourCMS",
    "idempotency_key": "batch-20240615-001"
  }
}'
RESPONSE — Single Patient200
{
  "success": true,
  "code": "OK",
  "message": "Processed: 1 new patient(s), 1 new claim(s), 1 new provider(s)",
  "webhook_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "processed": {
    "patients":    { "created": 1, "updated": 0, "existing": 0, "failed": 0 },
    "claims":      { "created": 1, "updated": 0, "existing": 0, "skipped": 0, "failed": 0 },
    "providers":   { "created": 1, "existing": 0, "failed": 0 },
    "payers":      { "resolved": 1, "not_found": 0 },
    "coverages":   { "created": 1, "updated": 0, "existing": 0, "failed": 0 },
    "enrollments": { "created": 1, "existing": 0 }
  },
  "items": [
    {
      "index": 0,
      "identifier": "CMS-PAT-001",
      "status": "success",
      "patient":    { "id": "f1a2...", "action": "created" },
      "provider":   { "id": "b3c4...", "npi": "1003000126", "action": "created" },
      "payer":      { "id": "d5e6...", "name": "Blue Cross Blue Shield", "action": "resolved" },
      "coverage":   { "id": "a7b8...", "action": "created" },
      "claim":      { "id": "c9d0...", "pcn": "WH-JD-M1K2N3-0", "action": "created" },
      "enrollment": { "action": "created" }
    }
  ]
}
RESPONSE — Batch (mixed)200
{
  "success": true,
  "code": "COMPLETED_WITH_ERRORS",
  "message": "Processed: 2 new patient(s), 1 updated patient(s), 1 new claim(s), 1 updated claim(s)",
  "webhook_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "processed": {
    "patients":    { "created": 2, "updated": 1, "existing": 0, "failed": 0 },
    "claims":      { "created": 1, "updated": 1, "existing": 0, "skipped": 0, "failed": 0 },
    "providers":   { "created": 0, "existing": 1, "failed": 0 },
    "payers":      { "resolved": 2, "not_found": 1 },
    "coverages":   { "created": 1, "updated": 1, "existing": 0, "failed": 0 },
    "enrollments": { "created": 0, "existing": 1 }
  },
  "items": [
    {
      "index": 0, "identifier": "CMS-PAT-001", "status": "success",
      "patient": { "id": "f1a2...", "action": "updated" },
      "claim":   { "id": "c9d0...", "pcn": "WH-JD-M1K2N3-0", "action": "updated" }
    },
    {
      "index": 1, "identifier": "CMS-PAT-002", "status": "success",
      "patient": { "id": "e3f4...", "action": "created" },
      "claim":   { "id": "g5h6...", "pcn": "WH-AB-M1K2N3-1", "action": "created" }
    },
    {
      "index": 2, "identifier": "CMS-PAT-ERR", "status": "partial",
      "patient": { "id": "i7j8...", "action": "created" },
      "errors": [
        { "code": "PAYER_NOT_FOUND", "message": "Payer \"Totally Made Up Insurance\" not found..." }
      ]
    }
  ],
  "errors": [
    "CMS-PAT-ERR: Payer \"Totally Made Up Insurance\" not found in payer directory..."
  ]
}
RESPONSE — Idempotent200
{
  "success": true,
  "code": "IDEMPOTENCY_COMPLETED",
  "message": "Payload already processed (idempotency key matched)",
  "webhook_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
ERROR — Bad Request400
{
  "success": false,
  "code": "PAYLOAD_MISSING_PATIENTS",
  "message": "Invalid payload. Expected \"patients\" array with at least one patient."
}
ERROR — No Tenant Configured400
{
  "success": false,
  "code": "TENANT_NOT_CONFIGURED",
  "message": "Partner does not have a tenant configured. Contact support to set up your integration."
}
ERROR — Unauthorized401
{
  "success": false,
  "code": "AUTH_INVALID_KEY",
  "message": "Invalid API key"
}
ERROR — Demo Limit403
{
  "success": false,
  "code": "LIMIT_PATIENTS",
  "message": "Demo limit exceeded. You have 45 of 50 patients used and this request would add 10 more.",
  "webhook_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
ERROR — Rate Limited (per minute)429
{
  "success": false,
  "code": "RATE_LIMIT_MINUTE",
  "message": "Rate limit exceeded. Maximum 60 requests per minute."
}
ERROR — Rate Limited (daily)429
{
  "success": false,
  "code": "RATE_LIMIT_DAILY",
  "message": "Daily rate limit exceeded. 10000/10000 requests today. Resets at midnight UTC."
}