Product EngineeringFeaturesOrder SplitBackend

API Reference

Endpoint contract, request and response format, operation modes, and validation gates for the Order Split API.

👤 Sachin Sharma📅 Updated: May 2, 2026🏷️ feature🏷️ backend🏷️ api🏷️ validation

Request flow


Endpoint

POST /api-v3/finance/bill/split/{bill_id}

Request body

KeyTypeRequiredDescription
test_idsnumber[]YesBillingInfo.id values to move to the split bill
new_sourcestring | nullNoDestination source for the split bill. Accepted values: "Org Pay", "Self Pay"
is_validatebooleanNoDry-run validation: runs all checks and returns result without writing any data
is_calculatebooleanNoDry-run calculation: returns financial projections for both bills without writing any data

Operation modes

The same endpoint serves three modes. This lets callers check eligibility and preview financials before committing, without needing separate endpoints.

Mode 1 — Validate only (is_validate=true)

Runs the full validation pipeline and returns the result. Always returns HTTP 200, regardless of whether validation passed or failed — the caller must inspect status and failed_validation to determine outcome.

Validation passed:

{
  "status": "success",
  "failed_validation": [],
  "message": "Validation results only"
}

Validation failed:

{
  "status": "failed",
  "failed_validation": [
    {
      "type": "INVALID_BILL_STATE",
      "message": "Bills which are Locked, Cancelled, Written Off, Complete or Refunded are not allowed to split"
    }
  ],
  "message": "Bill split validation failed"
}

Mode 2 — Calculate only (is_calculate=true)

Computes financial values for both bills in memory and returns them. No data is written.

{
  "status": "success",
  "message": "Billing calculations",
  "bill": {
    "vat": 18.0,
    "TDSAmount": 0.0,
    "vat_percent": 1.5,
    "billAdvance": 0.0,
    "co_pay_amount": 0.0,
    "billConcession": 0.0,
    "billTotalAmount": 1200.0,
    "deductible_amount": 0.0,
    "billAdditionalAmount": 50.0,
    "patientPayableAmount": 0.0
  },
  "split_bill": {
    "vat": 6.0,
    "TDSAmount": 0.0,
    "vat_percent": 1.5,
    "billAdditionalAmount": 16.67,
    "billTotalAmount": 400.0,
    "billConcession": 0.0,
    "billAdvance": 0.0,
    "co_pay_amount": 0.0,
    "deductible_amount": 0.0,
    "patientPayableAmount": 0.0
  }
}

bill contains updated values for the parent bill after split. split_bill contains projected values for the new bill.

Mode 3 — Execute (default, no flags)

Runs the full atomic split transaction.

Success (HTTP 200):

{
  "status": "success",
  "message": "Bill split successfully",
  "data": {
    "original_bill": { "id": 101, "labBillId": 4501, "total_amount": 1200.0 },
    "new_bill":      { "id": 102, "labBillId": 4502, "total_amount": 400.0 }
  }
}

Validation failure (HTTP 400):

{
  "status": "failed",
  "failed_validation": [
    { "type": "RELATION_BLOCKED", "message": "finance_insurance_claim" }
  ],
  "message": "Bill split validation failed"
}

HTTP status code summary

ScenarioHTTP status
is_validate=true — any result (pass or fail)200
is_calculate=true — success200
Execute — success200
Execute — validation failed400
Payload validation error (lab_id, bill_id, test_ids shape)400 (raises ValidationError)

Validation flow

Key behaviours:

  • A fails fast — raises immediately, no manager instantiated, no DB read
  • B and C always run — for all three modes; errors accumulate into failed_validation, not raised
  • D and E only run on execute — skipped entirely for is_validate and is_calculate
  • is_validate always returns HTTP 200 — caller must inspect status and failed_validation
  • Execute returns HTTP 400 when any error exists in failed_validation after D+E

Validation gates

A) Request payload (view layer)

Validated before the manager is instantiated:

1. lab_id must exist in session
2. bill_id must be a numeric value
3. test_ids must be a non-empty list or tuple
4. test_ids must contain only unique entries
5. every entry in test_ids must be an integer

B) Bill-level eligibility (manager validate_bill)

Runs during manager __init__. Errors accumulate into failed_validation.

Error typeCondition
BILL_NOT_FOUNDNo Billing record for (lab_id, bill_id)
INVALID_BILL_SOURCEbill.source is not insurance
INVALID_BILL_STATEBill is locked, cancelled, refunded, written off, complete, or invoiced
BILL_IS_ABDM_LINKEDA LinkedBills row exists for this bill

C) BillingInfo-level eligibility (manager validate_to_split_test_ids)

Runs during manager __init__. Note: profile children are auto-included via a subquery — if a parent profile is in test_ids, its child tests are automatically resolved.

Error typeCondition
TO_SPLIT_TESTS_NOT_FOUNDNo BillingInfo rows found for the given IDs on this bill
TO_SPLIT_TESTS_NOT_FROM_SAME_LABAny selected row belongs to a different lab
TO_SPLIT_TESTS_DISMISSED_OR_REFUNDEDAny selected row is dismissed or refunded
INVALID_TO_SPLIT_TESTA child test-of-profile ID was submitted without its parent profile ID

D) Split eligibility (manager validate_split)

Called explicitly after __init__. Adds to the same failed_validation list.

Error typeCondition
BILL_ADVANCE_NOT_ALLOWEDbill.billAdvance != 0
ZERO_TEST_COUNT_AFTER_SPLITAll non-dismissed, non-profile-child rows would move, leaving parent empty
UNSUPPORTED_ATTACHMENTS_FOUNDActive attachments exist outside SmartReport or Image categories
RELATION_BLOCKEDRows exist in a guarded table (message contains the DB table name)

E) Guarded tables

These models are checked as blockers. RELATION_BLOCKED is returned with the table's DB name.

ModelDB table (approx.)
InsuranceClaimfinance_insurance_claim
BillClaimRelationfinance_bill_claim_relation
ClaimTestRelationfinance_claim_test_relation
ClaimProviderInfofinance_claim_provider_info
HomeCollectionpatient_home_collection
EmrAppointmentspatient_emr_appointments
PaymentGatewayTransactionspayments_payment_gateway_transactions
Kitcrm_kit
ShippingDetailsintegration_shipping_details
LinkedBillpatient_linked_bill
ProcessedFileaccount_processed_file
B2BPatientpatient_b2b_patient
TestClinicalInfointegration_test_clinical_info
BillClassifierTagsfinance_bill_classifier_tags

On this page