HealthcareExtract

Explanation of Benefits (EOB) Extractor

Extracts claim processing summaries and benefit explanations from insurance statements.

Ship it with Extend

Live pipeline

a real document, processed end to end · view only
Source documenteob-sample copy.pdf

Step-by-step

An Explanation of Benefits (EOB) is a document issued by a health insurance plan that details how a submitted medical claim was processed, including the charges billed, amounts allowed, benefits applied, and the patient's financial responsibility. This template takes in EOBs and outputs JSON (.json) with structured claim and member data including service line items, financial totals, deductible and out-of-pocket status, and benefit period details per the extraction schema by using Extend's Extract primitives.

Input
EOBs
Compatible document types (full list)
.pdf.docx.xlsx.png.jpg.jpeg.tiff.tif.svg.heic.heif.bmp.gif.webp.psd.xls.xltm.xltx.ods.doc.wpd.dotx.odt.pptx.ppt.ppm.csv.txt.html.xml.rtf.lis.md.eml.pcx
Step 1

Extract

Pulls a defined set of fields from the document and returns them as structured JSON matching a schema.

InputDocument file, or markdown from a prior Parse step
Config
schemacustom schema — 20 fieldschanged
additionalPropertiesfalsechanged
properties.amount_allowed.additionalPropertiesfalsechanged
properties.amount_allowed.description"The total amount allowed by the health plan for the services provided, after discounts and reductions. This is the maximum amount considered for payment."changed
properties.amount_allowed.extend:type"currency"changed
properties.amount_allowed.properties.amount.type["number","null"]changed
properties.amount_allowed.properties.iso_4217_currency_code.type["string","null"]changed
properties.amount_allowed.required["amount","iso_4217_currency_code"]changed
properties.amount_allowed.type"object"changed
properties.amount_billed.additionalPropertiesfalsechanged
properties.amount_billed.description"The total amount billed by the provider for all services included in this claim. This is the original charge before any discounts, reductions, or insurance adj…changed
properties.amount_billed.extend:type"currency"changed
properties.amount_billed.properties.amount.type["number","null"]changed
properties.amount_billed.properties.iso_4217_currency_code.type["string","null"]changed
properties.amount_billed.required["amount","iso_4217_currency_code"]changed
properties.amount_billed.type"object"changed
properties.benefit_period_end.description"The end date of the benefit period relevant to this EOB or claim. This is the end of the coverage period for deductibles and out-of-pocket calculations."changed
properties.benefit_period_end.extend:type"date"changed
properties.benefit_period_end.type["string","null"]changed
properties.benefit_period_start.description"The start date of the benefit period relevant to this EOB or claim. This is the beginning of the coverage period for deductibles and out-of-pocket calculations…changed
properties.benefit_period_start.extend:type"date"changed
properties.benefit_period_start.type["string","null"]changed
properties.claim_line_items.description"The individual service lines or procedures included in this claim. Each item represents a specific service, procedure, or charge, and includes details about da…changed
properties.claim_line_items.items.additionalPropertiesfalsechanged
properties.claim_line_items.items.properties.amount_allowed.description"The amount allowed by the health plan for this service line after discounts and reductions."changed
properties.claim_line_items.items.properties.amount_allowed.type["number","null"]changed
properties.claim_line_items.items.properties.amount_billed.description"The amount billed by the provider for this specific service line, before any discounts or reductions."changed
properties.claim_line_items.items.properties.amount_billed.type["number","null"]changed
properties.claim_line_items.items.properties.amount_not_covered.description"The amount for this service line that is not covered by the health plan and is the patient's responsibility."changed
properties.claim_line_items.items.properties.amount_not_covered.type["number","null"]changed
properties.claim_line_items.items.properties.coinsurance_amount.description"The coinsurance amount the patient is responsible for on this service line."changed
properties.claim_line_items.items.properties.coinsurance_amount.type["number","null"]changed
properties.claim_line_items.items.properties.copay_amount.description"The copayment amount the patient is responsible for on this service line."changed
properties.claim_line_items.items.properties.copay_amount.type["number","null"]changed
properties.claim_line_items.items.properties.deductible_amount.description"The portion of this service line applied to the patient's deductible."changed
properties.claim_line_items.items.properties.deductible_amount.type["number","null"]changed
properties.claim_line_items.items.properties.discounts_and_reductions.description"The amount of discounts, reductions, or adjustments applied to this service line by the health plan."changed
properties.claim_line_items.items.properties.discounts_and_reductions.type["number","null"]changed
properties.claim_line_items.items.properties.health_plan_responsibility.description"The amount paid or payable by the health plan for this service line."changed
properties.claim_line_items.items.properties.health_plan_responsibility.type["number","null"]changed
properties.claim_line_items.items.properties.notes.description"Any notes, remarks, or explanations related to this service line, such as denial reasons, bundling, or special instructions. May be referenced by footnotes or …changed
properties.claim_line_items.items.properties.notes.type["string","null"]changed
properties.claim_line_items.items.properties.patient_responsibility.description"The total amount the patient is responsible for on this service line, including deductible, copay, coinsurance, and non-covered amounts."changed
properties.claim_line_items.items.properties.patient_responsibility.type["number","null"]changed
properties.claim_line_items.items.properties.service_description.description"A description of the medical service, procedure, or supply provided. May include procedure names, codes, or other identifying information."changed
properties.claim_line_items.items.properties.service_description.type["string","null"]changed
properties.claim_line_items.items.properties.service_end_date.description"The date on which the service or procedure ended, if applicable. May be omitted if service occurred on a single day."changed
properties.claim_line_items.items.properties.service_end_date.extend:type"date"changed
properties.claim_line_items.items.properties.service_end_date.type["string","null"]changed
properties.claim_line_items.items.properties.service_start_date.description"The date on which the service or procedure began or was rendered. May be labeled as 'Service Date', 'Date of Service', or similar."changed
properties.claim_line_items.items.properties.service_start_date.extend:type"date"changed
properties.claim_line_items.items.properties.service_start_date.type["string","null"]changed
properties.claim_line_items.items.required["notes","copay_amount","amount_billed","amount_allowed","service_end_date","deductible_amount","amount_not_covered","coinsurance_amount","service_start_date","…changed
properties.claim_line_items.items.type"object"changed
properties.claim_line_items.type"array"changed
properties.date_processed.description"The date on which this claim or EOB was processed by the health plan. May be labeled as 'Date Processed', 'Processed On', or similar."changed
properties.date_processed.extend:type"date"changed
properties.date_processed.type["string","null"]changed
properties.deductible_maximum.additionalPropertiesfalsechanged
properties.deductible_maximum.description"The total deductible maximum for the benefit period for the patient or family, as specified by the plan."changed
properties.deductible_maximum.extend:type"currency"changed
properties.deductible_maximum.properties.amount.type["number","null"]changed
properties.deductible_maximum.properties.iso_4217_currency_code.type["string","null"]changed
properties.deductible_maximum.required["amount","iso_4217_currency_code"]changed
properties.deductible_maximum.type"object"changed
properties.deductible_met_to_date.additionalPropertiesfalsechanged
properties.deductible_met_to_date.description"The cumulative amount of deductible met by the patient for the current benefit period as of this EOB. This reflects progress toward the annual deductible."changed
properties.deductible_met_to_date.extend:type"currency"changed
properties.deductible_met_to_date.properties.amount.type["number","null"]changed
properties.deductible_met_to_date.properties.iso_4217_currency_code.type["string","null"]changed
properties.deductible_met_to_date.required["amount","iso_4217_currency_code"]changed
properties.deductible_met_to_date.type"object"changed
properties.discounts_and_reductions.additionalPropertiesfalsechanged
properties.discounts_and_reductions.description"The total amount of discounts, reductions, or adjustments applied to the billed charges by the health plan. This may include network discounts, negotiated rate…changed
properties.discounts_and_reductions.extend:type"currency"changed
properties.discounts_and_reductions.properties.amount.type["number","null"]changed
properties.discounts_and_reductions.properties.iso_4217_currency_code.type["string","null"]changed
properties.discounts_and_reductions.required["amount","iso_4217_currency_code"]changed
properties.discounts_and_reductions.type"object"changed
properties.eob_number.description"The unique identifier for this Explanation of Benefits (EOB) document or claim. This is the primary reference number used to track this EOB. May include number…changed
properties.eob_number.type["string","null"]changed
properties.group_number.description"The group or employer number associated with the health plan. This identifies the employer or group plan under which the member is covered. Commonly labeled as…changed
properties.group_number.type["string","null"]changed
properties.health_plan_responsibility.additionalPropertiesfalsechanged
properties.health_plan_responsibility.description"The total amount paid or payable by the health plan for this claim. This is the insurer's share of the allowed amount."changed
properties.health_plan_responsibility.extend:type"currency"changed
properties.health_plan_responsibility.properties.amount.type["number","null"]changed
properties.health_plan_responsibility.properties.iso_4217_currency_code.type["string","null"]changed
properties.health_plan_responsibility.required["amount","iso_4217_currency_code"]changed
properties.health_plan_responsibility.type"object"changed
properties.member_id.description"The unique identification number assigned to the insured member or subscriber. This is used by the health plan to identify the covered individual. May be label…changed
properties.member_id.type["string","null"]changed
properties.out_of_pocket_maximum.additionalPropertiesfalsechanged
properties.out_of_pocket_maximum.description"The total out-of-pocket maximum for the benefit period for the patient or family, as specified by the plan."changed
properties.out_of_pocket_maximum.extend:type"currency"changed
properties.out_of_pocket_maximum.properties.amount.type["number","null"]changed
properties.out_of_pocket_maximum.properties.iso_4217_currency_code.type["string","null"]changed
properties.out_of_pocket_maximum.required["amount","iso_4217_currency_code"]changed
properties.out_of_pocket_maximum.type"object"changed
properties.out_of_pocket_met_to_date.additionalPropertiesfalsechanged
properties.out_of_pocket_met_to_date.description"The cumulative amount of out-of-pocket expenses met by the patient for the current benefit period as of this EOB. This includes deductibles, copays, and coinsu…changed
properties.out_of_pocket_met_to_date.extend:type"currency"changed
properties.out_of_pocket_met_to_date.properties.amount.type["number","null"]changed
properties.out_of_pocket_met_to_date.properties.iso_4217_currency_code.type["string","null"]changed
properties.out_of_pocket_met_to_date.required["amount","iso_4217_currency_code"]changed
properties.out_of_pocket_met_to_date.type"object"changed
properties.patient_name.description"The full name of the individual who received the medical services. May be labeled as 'Patient', 'Covered Person', or similar."changed
properties.patient_name.type["string","null"]changed
properties.patient_responsibility.additionalPropertiesfalsechanged
properties.patient_responsibility.description"The total amount the patient may owe to the provider for these services, after insurance payments and adjustments. This may include deductibles, copays, coinsu…changed
properties.patient_responsibility.extend:type"currency"changed
properties.patient_responsibility.properties.amount.type["number","null"]changed
properties.patient_responsibility.properties.iso_4217_currency_code.type["string","null"]changed
properties.patient_responsibility.required["amount","iso_4217_currency_code"]changed
properties.patient_responsibility.type"object"changed
properties.plan_name.description"The name of the health insurance plan or product under which the claim was processed. May appear as 'Plan Name', 'Product', or similar."changed
properties.plan_name.type["string","null"]changed
properties.provider_name.description"The name of the healthcare provider, facility, or organization that rendered the services for this claim. May be labeled as 'Provider', 'Facility', or similar.…changed
properties.provider_name.type["string","null"]changed
properties.subscriber_name.description"The name of the primary insured individual or policyholder. This is the person who holds the insurance policy, sometimes labeled as 'Subscriber', 'Participant'…changed
properties.subscriber_name.type["string","null"]changed
required["member_id","plan_name","eob_number","group_number","patient_name","amount_billed","provider_name","amount_allowed","date_processed","subscriber_name","claim_l…changed
type"object"changed
baseProcessor"extraction_performance"
OutputJSON shaped to the extraction schema, with per-field confidence scores and citations grounding each value to its source location

You can learn more about Extract configuration in Extend's Extract documentation.

Example code

{
  "name": "Explanation of Benefits Processing Pipeline",
  "steps": [
    {
      "name": "startTrigger1",
      "type": "TRIGGER",
      "next": [
        {
          "step": "parse1"
        }
      ]
    },
    {
      "name": "parse1",
      "type": "PARSE",
      "config": {
        "parseConfig": {
          "blockOptions": {
            "text": {
              "agentic": {
                "enabled": true
              },
              "signatureDetectionEnabled": true
            },
            "tables": {
              "agentic": {
                "enabled": true
              },
              "tableHeaderContinuationEnabled": true
            },
            "figures": {
              "enabled": true
            }
          },
          "chunkingStrategy": {
            "type": "page",
            "options": {}
          }
        }
      },
      "next": [
        {
          "step": "extraction2"
        }
      ]
    },
    {
      "name": "extraction2",
      "type": "EXTRACT",
      "config": {
        "extractorConfig": {
          "schema": {
            "type": "object",
            "required": [
              "member_id",
              "plan_name",
              "eob_number",
              "group_number",
              "patient_name",
              "amount_billed",
              "provider_name",
              "amount_allowed",
              "date_processed",
              "subscriber_name",
              "claim_line_items",
              "benefit_period_end",
              "deductible_maximum",
              "benefit_period_start",
              "out_of_pocket_maximum",
              "deductible_met_to_date",
              "patient_responsibility",
              "discounts_and_reductions",
              "out_of_pocket_met_to_date",
              "health_plan_responsibility"
            ],
            "properties": {
              "member_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The unique identification number assigned to the insured member or subscriber. This is used by the health plan to identify the covered individual. May be labeled as 'Member ID', 'Subscriber ID', or similar."
              },
              "plan_name": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The name of the health insurance plan or product under which the claim was processed. May appear as 'Plan Name', 'Product', or similar."
              },
              "eob_number": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The unique identifier for this Explanation of Benefits (EOB) document or claim. This is the primary reference number used to track this EOB. May include numbers, letters, or special characters. Common labels include 'Claim #', 'EOB #', or similar."
              },
              "group_number": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The group or employer number associated with the health plan. This identifies the employer or group plan under which the member is covered. Commonly labeled as 'Group #', 'Group Number', etc."
              },
              "patient_name": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The full name of the individual who received the medical services. May be labeled as 'Patient', 'Covered Person', or similar."
              },
              "amount_billed": {
                "type": "object",
                "required": [
                  "amount",
                  "iso_4217_currency_code"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "iso_4217_currency_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "The total amount billed by the provider for all services included in this claim. This is the original charge before any discounts, reductions, or insurance adjustments.",
                "extend:type": "currency",
                "additionalProperties": false
              },
              "provider_name": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The name of the healthcare provider, facility, or organization that rendered the services for this claim. May be labeled as 'Provider', 'Facility', or similar."
              },
              "amount_allowed": {
                "type": "object",
                "required": [
                  "amount",
                  "iso_4217_currency_code"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "iso_4217_currency_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "The total amount allowed by the health plan for the services provided, after discounts and reductions. This is the maximum amount considered for payment.",
                "extend:type": "currency",
                "additionalProperties": false
              },
              "date_processed": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The date on which this claim or EOB was processed by the health plan. May be labeled as 'Date Processed', 'Processed On', or similar.",
                "extend:type": "date"
              },
              "subscriber_name": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The name of the primary insured individual or policyholder. This is the person who holds the insurance policy, sometimes labeled as 'Subscriber', 'Participant', or 'Member'."
              },
              "claim_line_items": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "notes",
                    "copay_amount",
                    "amount_billed",
                    "amount_allowed",
                    "service_end_date",
                    "deductible_amount",
                    "amount_not_covered",
                    "coinsurance_amount",
                    "service_start_date",
                    "service_description",
                    "patient_responsibility",
                    "discounts_and_reductions",
                    "health_plan_responsibility"
                  ],
                  "properties": {
                    "notes": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Any notes, remarks, or explanations related to this service line, such as denial reasons, bundling, or special instructions. May be referenced by footnotes or symbols."
                    },
                    "copay_amount": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "The copayment amount the patient is responsible for on this service line."
                    },
                    "amount_billed": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "The amount billed by the provider for this specific service line, before any discounts or reductions."
                    },
                    "amount_allowed": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "The amount allowed by the health plan for this service line after discounts and reductions."
                    },
                    "service_end_date": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "The date on which the service or procedure ended, if applicable. May be omitted if service occurred on a single day.",
                      "extend:type": "date"
                    },
                    "deductible_amount": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "The portion of this service line applied to the patient's deductible."
                    },
                    "amount_not_covered": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "The amount for this service line that is not covered by the health plan and is the patient's responsibility."
                    },
                    "coinsurance_amount": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "The coinsurance amount the patient is responsible for on this service line."
                    },
                    "service_start_date": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "The date on which the service or procedure began or was rendered. May be labeled as 'Service Date', 'Date of Service', or similar.",
                      "extend:type": "date"
                    },
                    "service_description": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "A description of the medical service, procedure, or supply provided. May include procedure names, codes, or other identifying information."
                    },
                    "patient_responsibility": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "The total amount the patient is responsible for on this service line, including deductible, copay, coinsurance, and non-covered amounts."
                    },
                    "discounts_and_reductions": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "The amount of discounts, reductions, or adjustments applied to this service line by the health plan."
                    },
                    "health_plan_responsibility": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "The amount paid or payable by the health plan for this service line."
                    }
                  },
                  "additionalProperties": false
                },
                "description": "The individual service lines or procedures included in this claim. Each item represents a specific service, procedure, or charge, and includes details about dates, amounts, and benefit application. Format and labeling may vary widely across EOBs."
              },
              "benefit_period_end": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The end date of the benefit period relevant to this EOB or claim. This is the end of the coverage period for deductibles and out-of-pocket calculations.",
                "extend:type": "date"
              },
              "deductible_maximum": {
                "type": "object",
                "required": [
                  "amount",
                  "iso_4217_currency_code"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "iso_4217_currency_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "The total deductible maximum for the benefit period for the patient or family, as specified by the plan.",
                "extend:type": "currency",
                "additionalProperties": false
              },
              "benefit_period_start": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The start date of the benefit period relevant to this EOB or claim. This is the beginning of the coverage period for deductibles and out-of-pocket calculations.",
                "extend:type": "date"
              },
              "out_of_pocket_maximum": {
                "type": "object",
                "required": [
                  "amount",
                  "iso_4217_currency_code"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "iso_4217_currency_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "The total out-of-pocket maximum for the benefit period for the patient or family, as specified by the plan.",
                "extend:type": "currency",
                "additionalProperties": false
              },
              "deductible_met_to_date": {
                "type": "object",
                "required": [
                  "amount",
                  "iso_4217_currency_code"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "iso_4217_currency_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "The cumulative amount of deductible met by the patient for the current benefit period as of this EOB. This reflects progress toward the annual deductible.",
                "extend:type": "currency",
                "additionalProperties": false
              },
              "patient_responsibility": {
                "type": "object",
                "required": [
                  "amount",
                  "iso_4217_currency_code"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "iso_4217_currency_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "The total amount the patient may owe to the provider for these services, after insurance payments and adjustments. This may include deductibles, copays, coinsurance, and non-covered amounts.",
                "extend:type": "currency",
                "additionalProperties": false
              },
              "discounts_and_reductions": {
                "type": "object",
                "required": [
                  "amount",
                  "iso_4217_currency_code"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "iso_4217_currency_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "The total amount of discounts, reductions, or adjustments applied to the billed charges by the health plan. This may include network discounts, negotiated rates, or other reductions.",
                "extend:type": "currency",
                "additionalProperties": false
              },
              "out_of_pocket_met_to_date": {
                "type": "object",
                "required": [
                  "amount",
                  "iso_4217_currency_code"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "iso_4217_currency_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "The cumulative amount of out-of-pocket expenses met by the patient for the current benefit period as of this EOB. This includes deductibles, copays, and coinsurance.",
                "extend:type": "currency",
                "additionalProperties": false
              },
              "health_plan_responsibility": {
                "type": "object",
                "required": [
                  "amount",
                  "iso_4217_currency_code"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "iso_4217_currency_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "The total amount paid or payable by the health plan for this claim. This is the insurer's share of the allowed amount.",
                "extend:type": "currency",
                "additionalProperties": false
              }
            },
            "additionalProperties": false
          },
          "baseProcessor": "extraction_performance"
        }
      }
    }
  ]
}
# Explanation of Benefits Processing — Extend AI Skill

## What this pipeline does

This pipeline extracts comprehensive structured data from Explanation of Benefits (EOB) documents, capturing member identification, plan details, claim summary, benefit period information, deductible and out-of-pocket tracking, and a complete array of service line items with per-line financial breakdowns. The workflow parses the EOB into enriched markdown, then extracts all 20 required fields and nested line-item objects into a validated JSON structure suitable for downstream claims processing, patient billing, or compliance systems.

## When to use this

- **Insurance claim intake systems**: Automatically capture member, provider, and financial data from incoming EOBs for database ingestion or claims reconciliation.
- **Patient billing portals**: Extract patient responsibility amounts, service descriptions, and deductible/out-of-pocket progress to generate patient-facing statements.
- **Compliance & appeals workflows**: Preserve line-item detail, notes, and denial reasons to support appeal and audit processes.
- **Healthcare cost analytics**: Gather multi-EOB data (billed, allowed, paid, patient-owes) for trend analysis and provider contract validation.
- **High-accuracy scenarios**: When accuracy and reviewability outweigh latency; this config uses `extraction_performance` processor for tables, complex layouts, and footnote handling.

## Processor pipeline

### Step 1: Parse (agentic OCR)
**Processor**: `agentic_ocr` with enhanced block options  
**Purpose**: Convert EOB PDF (digital or scanned) into structured markdown chunks, preserving tables, text, and figures.  
**Key config**:
- `blockOptions.text.agentic.enabled: true` — enables AI-driven text region detection for complex EOB layouts (multi-column, floating headers, footnotes).
- `blockOptions.text.signatureDetectionEnabled: true` — detects signature blocks (not needed for extraction but useful for routing).
- `blockOptions.tables.agentic.enabled: true` — AI-powered table detection and header continuation (critical for EOBs, which often have multi-page line-item tables).
- `blockOptions.tables.tableHeaderContinuationEnabled: true` — repeats table headers on split pages, preserving column meaning.
- `blockOptions.figures.enabled: true` — extracts embedded charts (rare in EOBs but safe to enable).
- `chunkingStrategy: "page"` — chunks by page, allowing extraction to see full page context for each line item and summary section.

**Why this config**: EOBs have dense tables, repeating headers, and columns labeled inconsistently across carriers. Agentic parsing and table continuation ensure the extractor sees clean, properly-attributed line items even on pages 2–5 of a 10-page EOB.

### Step 2: Extract (structured fields)
**Processor**: `extraction_performance` (not light; accuracy over speed)  
**Purpose**: Pull 20 top-level fields (member ID, plan, dates, totals) and an array of service line items, each with 13 sub-fields (copay, deductible, coinsurance, notes, dates, etc.).  
**Key config**:
- Schema is `type: "object"` with required array containing all 20 field names.
- Currency fields (`amount_billed`, `amount_allowed`, `patient_responsibility`, etc.) are objects with `{ amount: number|null, iso_4217_currency_code: string|null }`.
- Date fields (`benefit_period_start`, `service_start_date`, etc.) are typed `string|null` with `extend:type: "date"` hint for ISO parsing.
- `claim_line_items` is a required non-nullable array; each item is an object with 13 required fields including amounts, dates, description, and notes.
- `baseProcessor: "extraction_performance"` prioritizes accuracy for complex multi-line tables and conditional logic (e.g., "is this line denied or applied to deductible?").

**Why this config**: EOBs contain financial reconciliation logic (billed ≠ allowed ≠ paid), footnotes tied to specific lines, and per-line deductible/coinsurance splits. `extraction_performance` is tuned for this complexity and will correctly attribute each amount to the right field.

---

## TypeScript implementation

<SDK_CODE>
import { ExtendClient, extendDate, extendCurrency } from "extend-ai";
import { z } from "zod";
import fs from "fs";

const client = new ExtendClient({ token: process.env.EXTEND_API_KEY });

// Define the line item schema for individual service lines.
const LineItemSchema = z.object({
  notes: z.string().nullable().describe("Any notes, remarks, or explanations related to this service line, such as denial reasons, bundling, or special instructions. May be referenced by footnotes or symbols."),
  copay_amount: z.number().nullable().describe("The copayment amount the patient is responsible for on this service line."),
  amount_billed: z.number().nullable().describe("The amount billed by the provider for this specific service line, before any discounts or reductions."),
  amount_allowed: z.number().nullable().describe("The amount allowed by the health plan for this service line after discounts and reductions."),
  service_end_date: extendDate().describe("The date on which the service or procedure ended, if applicable. May be omitted if service occurred on a single day."),
  deductible_amount: z.number().nullable().describe("The portion of this service line applied to the patient's deductible."),
  amount_not_covered: z.number().nullable().describe("The amount for this service line that is not covered by the health plan and is the patient's responsibility."),
  coinsurance_amount: z.number().nullable().describe("The coinsurance amount the patient is responsible for on this service line."),
  service_start_date: extendDate().describe("The date on which the service or procedure began or was rendered. May be labeled as 'Service Date', 'Date of Service', or similar."),
  service_description: z.string().nullable().describe("A description of the medical service, procedure, or supply provided. May include procedure names, codes, or other identifying information."),
  patient_responsibility: z.number().nullable().describe("The total amount the patient is responsible for on this service line, including deductible, copay, coinsurance, and non-covered amounts."),
  discounts_and_reductions: z.number().nullable().describe("The amount of discounts, reductions, or adjustments applied to this service line by the health plan."),
  health_plan_responsibility: z.number().nullable().describe("The amount paid or payable by the health plan for this service line."),
});

// Define the main EOB schema with all required fields and nested line items.
const EOBSchema = z.object({
  member_id: z.string().nullable().describe("The unique identification number assigned to the insured member or subscriber. This is used by the health plan to identify the covered individual. May be labeled as 'Member ID', 'Subscriber ID', or similar."),
  plan_name: z.string().nullable().describe("The name of the health insurance plan or product under which the claim was processed. May appear as 'Plan Name', 'Product', or similar."),
  eob_number: z.string().nullable().describe("The unique identifier for this Explanation of Benefits (EOB) document or claim. This is the primary reference number used to track this EOB. May include numbers, letters, or special characters. Common labels include 'Claim #', 'EOB #', or similar."),
  group_number: z.string().nullable().describe("The group or employer number associated with the health plan. This identifies the employer or group plan under which the member is covered. Commonly labeled as 'Group #', 'Group Number', etc."),
  patient_name: z.string().nullable().describe("The full name of the individual who received the medical services. May be labeled as 'Patient', 'Covered Person', or similar."),
  amount_billed: extendCurrency().describe("The total amount billed by the provider for all services included in this claim. This is the original charge before any discounts, reductions, or insurance adjustments."),
  provider_name: z.string().nullable().describe("The name of the healthcare provider, facility, or organization that rendered the services for this claim. May be labeled as 'Provider', 'Facility', or similar."),
  amount_allowed: extendCurrency().describe("The total amount allowed by the health plan for the services provided, after discounts and reductions. This is the maximum amount considered for payment."),
  date_processed: extendDate().describe("The date on which this claim or EOB was processed by the health plan. May be labeled as 'Date Processed', 'Processed On', or similar."),
  subscriber_name: z.string().nullable().describe("The name of the primary insured individual or policyholder. This is the person who holds the insurance policy, sometimes labeled as 'Subscriber', 'Participant', or 'Member'."),
  claim_line_items: z.array(LineItemSchema).describe("The individual service lines or procedures included in this claim. Each item represents a specific service, procedure, or charge, and includes details about dates, amounts, and benefit application. Format and labeling may vary widely across EOBs."),
  benefit_period_end: extendDate().describe("The end date of the benefit period relevant to this EOB or claim. This is the end of the coverage period for deductibles and out-of-pocket calculations."),
  deductible_maximum: extendCurrency().describe("The total deductible maximum for the benefit period for the patient or family, as specified by the plan."),
  benefit_period_start: extendDate().describe("The start date of the benefit period relevant to this EOB or claim. This is the beginning of the coverage period for deductibles and out-of-pocket calculations."),
  out_of_pocket_maximum: extendCurrency().describe("The total out-of-pocket maximum for the benefit period for the patient or family, as specified by the plan."),
  deductible_met_to_date: extendCurrency().describe("The cumulative amount of deductible met by the patient for the current benefit period as of this EOB. This reflects progress toward the annual deductible."),
  patient_responsibility: extendCurrency().describe("The total amount the patient may owe to the provider for these services, after insurance payments and adjustments. This may include deductibles, copays, coinsurance, and non-covered amounts."),
  discounts_and_reductions: extendCurrency().describe("The total amount of discounts, reductions, or adjustments applied to the billed charges by the health plan. This may include network discounts, negotiated rates, or other reductions."),
  out_of_pocket_met_to_date: extendCurrency().describe("The cumulative amount of out-of-pocket expenses met by the patient for the current benefit period as of this EOB. This includes deductibles, copays, and coinsurance."),
  health_plan_responsibility: extendCurrency().describe("The total amount paid or payable by the health plan for this claim. This is the insurer's share of the allowed amount."),
});

/**
 * Main function to process an Explanation of Benefits EOB document.
 * Accepts a local file path, converts it to a data URL, and extracts structured fields.
 */
export async function processExplanationofBenefits(filePath: string) {
  console.log(`Processing EOB from: ${filePath}`);

  // Read the local file and convert to a base64 data URL.
  // This allows the Extend SDK to process the file without needing a public URL.
  const fileBuffer = fs.readFileSync(filePath);
  const base64Data = fileBuffer.toString("base64");
  const dataUrl = `data:application/octet-stream;base64,${base64Data}`;

  try {
    // Step 1: Parse the EOB document into markdown with agentic OCR.
    // This enables sophisticated text and table detection for complex EOB layouts.
    console.log("Parsing EOB document...");
    const parseRun = await client.parseRuns.createAndPoll({
      file: { url: dataUrl },
      config: {
        blockOptions: {
          text: {
            agentic: { enabled: true },
            signatureDetectionEnabled: true,
          },
          tables: {
            agentic: { enabled: true },
            tableHeaderContinuationEnabled: true,
          },
          figures: { enabled: true },
        },
        chunkingStrategy: {
          type: "page",
          options: {},
        },
      },
    });

    if (parseRun.status !== "PROCESSED") {
      throw new Error(`Parse failed with status: ${parseRun.status}`);
    }

    console.log(`Parse complete. Generated ${parseRun.output.chunks.length} chunks.`);

    // Step 2: Extract structured EOB data using the Zod schema.
    // Uses extraction_performance processor for high accuracy on complex tables and financial data.
    console.log("Extracting structured EOB fields...");
    const extractRun = await client.extractRuns.createAndPoll({
      file: { url: dataUrl },
      config: {
        schema: EOBSchema,
      },
    });

    if (extractRun.status !== "PROCESSED") {
      throw new Error(`Extraction failed with status: ${extractRun.status}`);
    }

    const eobData = extractRun.output.value;

    // Validate and output the extracted EOB.
    console.log("\n=== EXTRACTED EOB DATA ===\n");
    console.log(`Member ID: ${eobData.member_id}`);
    console.log(`Plan Name: ${eobData.plan_name}`);
    console.log(`EOB Number: ${eobData.eob_number}`);
    console.log(`Group Number: ${eobData.group_number}`);
    console.log(`Patient Name: ${eobData.patient_name}`);
    console.log(`Subscriber Name: ${eobData.subscriber_name}`);
    console.log(`Provider Name: ${eobData.provider_name}`);
    console.log(`Date Processed: ${eobData.date_processed}`);

    console.log("\n--- Benefit Period ---");
    console.log(`Start: ${eobData.benefit_period_start}`);
    console.log(`End: ${eobData.benefit_period_end}`);

    console.log("\n--- Financial Summary ---");
    console.log(
      `Amount Billed: ${eobData.amount_billed.amount} ${eobData.amount_billed.iso_4217_currency_code}`
    );
    console.log(
      `Amount Allowed: ${eobData.amount_allowed.amount} ${eobData.amount_allowed.iso_4217_currency_code}`
    );
    console.log(
      `Discounts & Reductions: ${eobData.discounts_and_reductions.amount} ${eobData.discounts_and_reductions.iso_4217_currency_code}`
    );
    console.log(
      `Health Plan Responsibility: ${eobData.health_plan_responsibility.amount} ${eobData.health_plan_responsibility.iso_4217_currency_code}`
    );
    console.log(
      `Patient Responsibility: ${eobData.patient_responsibility.amount} ${eobData.patient_responsibility.iso_4217_currency_code}`
    );

    console.log("\n--- Deductible ---");
    console.log(
      `Maximum: ${eobData.deductible_maximum.amount} ${eobData.deductible_maximum.iso_4217_currency_code}`
    );
    console.log(
      `Met to Date: ${eobData.deductible_met_to_date.amount} ${eobData.deductible_met_to_date.iso_4217_currency_code}`
    );

    console.log("\n--- Out-of-Pocket ---");
    console.log(
      `Maximum: ${eobData.out_of_pocket_maximum.amount} ${eobData.out_of_pocket_maximum.iso_4217_currency_code}`
    );
    console.log(
      `Met to Date: ${eobData.out_of_pocket_met_to_date.amount} ${eobData.out_of_pocket_met_to_date.iso_4217_currency_code}`
    );

    console.log("\n--- Claim Line Items ---");
    console.log(`Total Line Items: ${eobData.claim_line_items.length}`);

    eobData.claim_line_items.forEach((item, idx) => {
      console.log(`\nLine Item ${idx + 1}:`);
      console.log(`  Service: ${item.service_description}`);
      console.log(`  Service Date: ${item.service_start_date}
import { ExtendClient, extendDate, extendCurrency } from "extend-ai";
import { z } from "zod";
import fs from "fs";

const client = new ExtendClient({ token: process.env.EXTEND_API_KEY });

/**
 * Process an Explanation of Benefits (EOB) document.
 * Extracts member info, claim details, line items, and benefit period accumulators.
 */
export async function processExplanationOfBenefits(filePath: string) {
  // Convert local file to data URL (base64)
  const fileBuffer = fs.readFileSync(filePath);
  const dataUrl = `data:application/octet-stream;base64,${fileBuffer.toString("base64")}`;

  // Define the extraction schema with Zod
  const eobSchema = z.object({
    // Identifiers
    member_id: z.string().nullable().describe(
      "The unique identification number assigned to the insured member or subscriber. Used by the health plan to identify the covered individual."
    ),
    eob_number: z.string().nullable().describe(
      "The unique identifier for this Explanation of Benefits document. Primary reference number to track this EOB."
    ),
    plan_name: z.string().nullable().describe(
      "The name of the health insurance plan or product under which the claim was processed."
    ),
    group_number: z.string().nullable().describe(
      "The group or employer number associated with the health plan."
    ),

    // Party information
    subscriber_name: z.string().nullable().describe(
      "The name of the primary insured individual or policyholder who holds the insurance policy."
    ),
    patient_name: z.string().nullable().describe(
      "The full name of the individual who received the medical services."
    ),
    provider_name: z.string().nullable().describe(
      "The name of the healthcare provider, facility, or organization that rendered the services."
    ),

    // Claim dates
    date_processed: extendDate().describe(
      "The date on which this claim or EOB was processed by the health plan. Format: ISO yyyy-mm-dd."
    ),
    benefit_period_start: extendDate().describe(
      "The start date of the benefit period relevant to this EOB. Beginning of coverage period for deductibles and out-of-pocket calculations. Format: ISO yyyy-mm-dd."
    ),
    benefit_period_end: extendDate().describe(
      "The end date of the benefit period relevant to this EOB. End of coverage period for deductibles and out-of-pocket calculations. Format: ISO yyyy-mm-dd."
    ),

    // Claim-level amounts
    amount_billed: extendCurrency().describe(
      "The total amount billed by the provider for all services in this claim before any discounts or insurance adjustments."
    ),
    amount_allowed: extendCurrency().describe(
      "The total amount allowed by the health plan for the services provided, after discounts and reductions. Maximum amount considered for payment."
    ),
    discounts_and_reductions: extendCurrency().describe(
      "The total amount of discounts, reductions, or adjustments applied to billed charges by the health plan. Includes network discounts and negotiated rates."
    ),
    health_plan_responsibility: extendCurrency().describe(
      "The total amount paid or payable by the health plan for this claim. The insurer's share of the allowed amount."
    ),
    patient_responsibility: extendCurrency().describe(
      "The total amount the patient may owe to the provider for these services, after insurance payments. Includes deductibles, copays, coinsurance, and non-covered amounts."
    ),

    // Benefit period accumulators
    deductible_maximum: extendCurrency().describe(
      "The total deductible maximum for the benefit period as specified by the plan."
    ),
    deductible_met_to_date: extendCurrency().describe(
      "The cumulative amount of deductible met by the patient for the current benefit period as of this EOB. Reflects progress toward annual deductible."
    ),
    out_of_pocket_maximum: extendCurrency().describe(
      "The total out-of-pocket maximum for the benefit period as specified by the plan."
    ),
    out_of_pocket_met_to_date: extendCurrency().describe(
      "The cumulative amount of out-of-pocket expenses met by the patient for the current benefit period. Includes deductibles, copays, and coinsurance."
    ),

    // Line items (individual services)
    claim_line_items: z.array(
      z.object({
        service_description: z.string().nullable().describe(
          "Description of the medical service, procedure, or supply provided. May include procedure names, codes, or other identifying information."
        ),
        service_start_date: extendDate().describe(
          "The date on which the service or procedure began or was rendered. Format: ISO yyyy-mm-dd."
        ),
        service_end_date: extendDate().describe(
          "The date on which the service or procedure ended, if applicable. May be omitted if service occurred on a single day. Format: ISO yyyy-mm-dd."
        ),
        amount_billed: z.number().nullable().describe(
          "The amount billed by the provider for this specific service line, before any discounts or reductions."
        ),
        amount_allowed: z.number().nullable().describe(
          "The amount allowed by the health plan for this service line after discounts and reductions."
        ),
        discounts_and_reductions: z.number().nullable().describe(
          "The amount of discounts, reductions, or adjustments applied to this service line by the health plan."
        ),
        deductible_amount: z.number().nullable().describe(
          "The portion of this service line applied to the patient's deductible."
        ),
        copay_amount: z.number().nullable().describe(
          "The copayment amount the patient is responsible for on this service line."
        ),
        coinsurance_amount: z.number().nullable().describe(
          "The coinsurance amount the patient is responsible for on this service line."
        ),
        amount_not_covered: z.number().nullable().describe(
          "The amount for this service line that is not covered by the health plan and is the patient's responsibility."
        ),
        patient_responsibility: z.number().nullable().describe(
          "The total amount the patient is responsible for on this service line, including deductible, copay, coinsurance, and non-covered amounts."
        ),
        health_plan_responsibility: z.number().nullable().describe(
          "The amount paid or payable by the health plan for this service line."
        ),
        notes: z.string().nullable().describe(
          "Any notes, remarks, or explanations related to this service line, such as denial reasons, bundling, or special instructions."
        ),
      })
    ).describe(
      "The individual service lines or procedures included in this claim. Each item represents a specific service, procedure, or charge with dates, amounts, and benefit application details."
    ),
  });

  // Run extraction
  console.log(`\n📄 Extracting EOB from: ${filePath}`);
  const extractRun = await client.extractRuns.createAndPoll({
    file: { url: dataUrl },
    config: {
      schema: eobSchema,
    },
  });

  // Handle result
  if (extractRun.status === "PROCESSED") {
    const eobData = extractRun.output.value;
    console.log("\n✅ Extraction successful.\n");
    console.log("=== EXPLANATION OF BENEFITS SUMMARY ===\n");

    // Member & Claim Info
    console.log("📋 MEMBER & CLAIM INFO");
    console.log(`  Member ID: ${eobData.member_id}`);
    console.log(`  Subscriber: ${eobData.subscriber_name}`);
    console.log(`  Patient: ${eobData.patient_name}`);
    console.log(`  Plan: ${eobData.plan_name}`);
    console.log(`  Group #: ${eobData.group_number}`);
    console.log(`  EOB #: ${eobData.eob_number}`);
    console.log(`  Processed: ${eobData.date_processed}\n`);

    // Benefit Period
    console.log("📅 BENEFIT PERIOD");
    console.log(`  Start: ${eobData.benefit_period_start}`);
    console.log(`  End: ${eobData.benefit_period_end}\n`);

    // Provider & Service Summary
    console.log("🏥 PROVIDER & SERVICE SUMMARY");
    console.log(`  Provider: ${eobData.provider_name}`);
    console.log(`  Amount Billed: ${eobData.amount_billed?.amount} ${eobData.amount_billed?.iso_4217_currency_code}`);
    console.log(`  Amount Allowed: ${eobData.amount_allowed?.amount} ${eobData.amount_allowed?.iso_4217_currency_code}`);
    console.log(`  Discounts & Reductions: ${eobData.discounts_and_reductions?.amount} ${eobData.discounts_and_reductions?.iso_4217_currency_code}\n`);

    // Financial Responsibility
    console.log("💰 FINANCIAL RESPONSIBILITY");
    console.log(`  Plan Pays: ${eobData.health_plan_responsibility?.amount} ${eobData.health_plan_responsibility?.iso_4217_currency_code}`);
    console.log(`  You Owe: ${eobData.patient_responsibility?.amount} ${eobData.patient_responsibility?.iso_4217_currency_code}\n`);

    // Benefit Period Accumulators
    console.log("📊 BENEFIT PERIOD ACCUMULATORS");
    console.log(`  Deductible Maximum: ${eobData.deductible_maximum?.amount} ${eobData.deductible_maximum?.iso_4217_currency_code}`);
    console.log(`  Deductible Met to Date: ${eobData.deductible_met_to_date?.amount} ${eobData.deductible_met_to_date?.iso_4217_currency_code}`);
    console.log(`  Out-of-Pocket Maximum: ${eobData.out_of_pocket_maximum?.amount} ${eobData.out_of_pocket_maximum?.iso_4217_currency_code}`);
    console.log(`  Out-of-Pocket Met to Date: ${eobData.out_of_pocket_met_to_date?.amount} ${eobData.out_of_pocket_met_to_date?.iso_4217_currency_code}\n`);

    // Line Items
    if (eobData.claim_line_items && eobData.claim_line_items.length > 0) {
      console.log("📝 CLAIM LINE ITEMS\n");
      eobData.claim_line_items.forEach((item, idx) => {
        console.log(`  Line ${idx + 1}: ${item.service_description}`);
        console.log(`    Service Date: ${item.service_start_date}${item.service_end_date ? ` to ${item.service_end_date}` : ""}`);
        console.log(`    Billed: ${item.amount_billed} | Allowed: ${item.amount_allowed}`);
        console.log(`    Plan Pays: ${item.health_plan_responsibility} | You Pay: ${item.patient_responsibility}`);
        if (item.notes) console.log(`    Notes: ${item.notes}`);
        console.log();
      });
    }

    return eobData;
  } else {
    console.error(`❌ Extraction failed with status: ${extractRun.status}`);
    throw new Error(`Extraction did not complete successfully: ${extractRun.status}`);
  }
}

// Run if invoked directly
processExplanationOfBenefits(process.argv[2] || "./eob.pdf").catch(console.error);
import os
import base64
from typing import Any
from extend_ai import Extend
from pydantic import BaseModel, Field
import sys


class ClaimLineItem(BaseModel):
    service_description: str | None = Field(
        None,
        description="Description of the medical service, procedure, or supply provided. May include procedure names, codes, or other identifying information."
    )
    service_start_date: str = Field(
        ...,
        description="The date on which the service or procedure began or was rendered. Format: ISO yyyy-mm-dd."
    )
    service_end_date: str = Field(
        ...,
        description="The date on which the service or procedure ended, if applicable. May be omitted if service occurred on a single day. Format: ISO yyyy-mm-dd."
    )
    amount_billed: float | None = Field(
        None,
        description="The amount billed by the provider for this specific service line, before any discounts or reductions."
    )
    amount_allowed: float | None = Field(
        None,
        description="The amount allowed by the health plan for this service line after discounts and reductions."
    )
    discounts_and_reductions: float | None = Field(
        None,
        description="The amount of discounts, reductions, or adjustments applied to this service line by the health plan."
    )
    deductible_amount: float | None = Field(
        None,
        description="The portion of this service line applied to the patient's deductible."
    )
    copay_amount: float | None = Field(
        None,
        description="The copayment amount the patient is responsible for on this service line."
    )
    coinsurance_amount: float | None = Field(
        None,
        description="The coinsurance amount the patient is responsible for on this service line."
    )
    amount_not_covered: float | None = Field(
        None,
        description="The amount for this service line that is not covered by the health plan and is the patient's responsibility."
    )
    patient_responsibility: float | None = Field(
        None,
        description="The total amount the patient is responsible for on this service line, including deductible, copay, coinsurance, and non-covered amounts."
    )
    health_plan_responsibility: float | None = Field(
        None,
        description="The amount paid or payable by the health plan for this service line."
    )
    notes: str | None = Field(
        None,
        description="Any notes, remarks, or explanations related to this service line, such as denial reasons, bundling, or special instructions."
    )


class ExplanationOfBenefits(BaseModel):
    member_id: str | None = Field(
        ...,
        description="The unique identification number assigned to the insured member or subscriber. Used by the health plan to identify the covered individual."
    )
    eob_number: str | None = Field(
        ...,
        description="The unique identifier for this Explanation of Benefits document. Primary reference number to track this EOB."
    )
    plan_name: str | None = Field(
        ...,
        description="The name of the health insurance plan or product under which the claim was processed."
    )
    group_number: str | None = Field(
        ...,
        description="The group or employer number associated with the health plan."
    )
    subscriber_name: str | None = Field(
        ...,
        description="The name of the primary insured individual or policyholder who holds the insurance policy."
    )
    patient_name: str | None = Field(
        ...,
        description="The full name of the individual who received the medical services."
    )
    provider_name: str | None = Field(
        ...,
        description="The name of the healthcare provider, facility, or organization that rendered the services."
    )
    date_processed: str = Field(
        ...,
        description="The date on which this claim or EOB was processed by the health plan. Format: ISO yyyy-mm-dd."
    )
    benefit_period_start: str = Field(
        ...,
        description="The start date of the benefit period relevant to this EOB. Beginning of coverage period for deductibles and out-of-pocket calculations. Format: ISO yyyy-mm-dd."
    )
    benefit_period_end: str = Field(
        ...,
        description="The end date of the benefit period relevant to this EOB. End of coverage period for deductibles and out-of-pocket calculations. Format: ISO yyyy-mm-dd."
    )
    amount_billed: dict[str, Any] = Field(
        ...,
        description="The total amount billed by the provider for all services in this claim before any discounts or insurance adjustments."
    )
    amount_allowed: dict[str, Any] = Field(
        ...,
        description="The total amount allowed by the health plan for the services provided, after discounts and reductions. Maximum amount considered for payment."
    )
    discounts_and_reductions: dict[str, Any] = Field(
        ...,
        description="The total amount of discounts, reductions, or adjustments applied to billed charges by the health plan. Includes network discounts and negotiated rates."
    )
    health_plan_responsibility: dict[str, Any] = Field(
        ...,
        description="The total amount paid or payable by the health plan for this claim. The insurer's share of the allowed amount."
    )
    patient_responsibility: dict[str, Any] = Field(
        ...,
        description="The total amount the patient may owe to the provider for these services, after insurance payments. Includes deductibles, copays, coinsurance, and non-covered amounts."
    )
    deductible_maximum: dict[str, Any] = Field(
        ...,
        description="The total deductible maximum for the benefit period as specified by the plan."
    )
    deductible_met_to_date: dict[str, Any] = Field(
        ...,
        description="The cumulative amount of deductible met by the patient for the current benefit period as of this EOB. Reflects progress toward annual deductible."
    )
    out_of_pocket_maximum: dict[str, Any] = Field(
        ...,
        description="The total out-of-pocket maximum for the benefit period as specified by the plan."
    )
    out_of_pocket_met_to_date: dict[str, Any] = Field(
        ...,
        description="The cumulative amount of out-of-pocket expenses met by the patient for the current benefit period. Includes deductibles, copays, and coinsurance."
    )
    claim_line_items: list[ClaimLineItem] = Field(
        ...,
        description="The individual service lines or procedures included in this claim. Each item represents a specific service, procedure, or charge with dates, amounts, and benefit application details."
    )


def process_explanation_of_benefits(file_path: str) -> ExplanationOfBenefits:
    """
    Process an Explanation of Benefits (EOB) document.
    Extracts member info, claim details, line items, and benefit period accumulators.
    """
    client = Extend(token=os.environ["EXTEND_API_KEY"])

    with open(file_path, "rb") as f:
        file_buffer = f.read()
    data_url = f"data:application/octet-stream;base64,{base64.b64encode(file_buffer).decode('utf-8')}"

    eob_schema = {
        "type": "object",
        "properties": {
            "member_id": {
                "type": ["string", "null"],
                "description": "The unique identification number assigned to the insured member or subscriber. Used by the health plan to identify the covered individual."
            },
            "eob_number": {
                "type": ["string", "null"],
                "description": "The unique identifier for this Explanation of Benefits document. Primary reference number to track this EOB."
            },
            "plan_name": {
                "type": ["string", "null"],
                "description": "The name of the health insurance plan or product under which the claim was processed."
            },
            "group_number": {
                "type": ["string", "null"],
                "description": "The group or employer number associated with the health plan."
            },
            "subscriber_name": {
                "type": ["string", "null"],
                "description": "The name of the primary insured individual or policyholder who holds the insurance policy."
            },
            "patient_name": {
                "type": ["string", "null"],
                "description": "The full name of the individual who received the medical services."
            },
            "provider_name": {
                "type": ["string", "null"],
                "description": "The name of the healthcare provider, facility, or organization that rendered the services."
            },
            "date_processed": {
                "type": ["string", "null"],
                "description": "The date on which this claim or EOB was processed by the health plan. Format: ISO yyyy-mm-dd.",
                "extend:type": "date"
            },
            "benefit_period_start": {
                "type": ["string", "null"],
                "description": "The start date of the benefit period relevant to this EOB. Beginning of coverage period for deductibles and out-of-pocket calculations. Format: ISO yyyy-mm-dd.",
                "extend:type": "date"
            },
            "benefit_period_end": {
                "type": ["string", "null"],
                "description": "The end date of the benefit period relevant to this EOB. End of coverage period for deductibles and out-of-pocket calculations. Format: ISO yyyy-mm-dd.",
                "extend:type": "date"
            },
            "amount_billed": {
                "type": "object",
                "properties": {
                    "amount": {"type": ["number", "null"]},
                    "iso_4217_currency_code": {"type": ["string", "null"]}
                },
                "description": "The total amount billed by the provider for all services in this claim before any discounts or insurance adjustments.",
                "extend:type": "currency"
            },
            "amount_allowed": {
                "type": "object",
                "properties": {
                    "amount": {"type": ["number", "null"]},
                    "iso_4217_currency_code": {"type": ["string", "null"]}
                },
                "description": "The total amount allowed by the health plan for the services provided, after discounts and reductions. Maximum amount considered for payment.",
                "extend:type": "currency"
            },
            "discounts_and_reductions": {
                "type": "object",
                "properties": {
                    "amount": {"type": ["number", "null"]},
                    "iso_4217_currency_code": {"type": ["string", "null"]}
                },
                "description": "The total amount of discounts, reductions, or adjustments applied to billed charges by the health plan. Includes network discounts and negotiated rates.",
                "extend:type": "currency"
            },
            "health_plan_responsibility": {
                "type": "object",
                "properties": {
                    "amount": {"type": ["number", "null"]},
                    "iso_4217_currency_code": {"type": ["string", "null"]}
                },
                "description": "The total amount paid or payable by the health plan for this claim. The insurer's share of the allowed amount.",
                "extend:type": "currency"
            },
            "patient_responsibility": {
                "type": "object",
                "properties": {
                    "amount": {"type": ["number", "null"]},
                    "iso_4217_currency_code": {"type": ["string", "null"]}
                },
                "description": "The total amount the patient may owe to the provider for these services, after insurance payments. Includes deductibles, copays, coinsurance, and non-covered amounts.",
                "extend:type": "currency"
            },
            "deductible_maximum": {
                "type": "object",
                "properties": {
                    "amount": {"type": ["number", "null"]},
                    "iso_4217_currency_code": {"type": ["string", "null"]}
                },
                "description": "The total deductible maximum for the benefit period as specified by the plan.",
                "extend:type": "currency"
            },
            "deductible_met_to_date": {
                "type": "object",
                "properties": {
                    "amount": {"type": ["number", "null"]},
                    "iso_4217_currency_code": {"type": ["string", "null"]}
                },
                "description": "The cumulative amount of deductible met by the patient for the current benefit period as of this EOB. Reflects progress toward annual deductible.",
                "extend:type": "currency"
            },
            "out_of_pocket_maximum": {
                "type": "object",
                "properties": {
                    "amount": {"type": ["number", "null"]},
                    "iso_4217_currency_code": {"type": ["string", "null"]}
                },
                "description": "The total out-of-pocket maximum for the benefit period as specified by the plan.",
                "extend:type": "currency"
            },
            "out_of_pocket_met_to_date": {
                "type": "object",
                "properties": {
                    "amount": {"type": ["number", "null"]},
                    "iso_4217_currency_code": {"type": ["string", "null"]}
                },
                "description": "The cumulative amount of out-of-pocket expenses met by the patient for the current benefit period. Includes deductibles, copays, and coinsurance.",
                "extend:type": "currency"
            },
            "claim_line_items": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "service_description": {
                            "type": ["string", "null"],
                            "description": "Description of the medical service, procedure, or supply provided. May include procedure names, codes, or other identifying information."
                        },
                        "service_start_date": {
                            "type": ["string", "null"],
                            "description": "The date on which the service or procedure began or was rendered. Format: ISO yyyy-mm-dd.",
                            "extend:type": "date"
                        },
                        "service_end_date": {
                            "type": ["string", "null"],
                            "description": "The date on which the service or procedure ended, if applicable. May be omitted if service occurred on a single day. Format: ISO yyyy-mm-dd.",
                            "extend:type": "date"
                        },
                        "amount_billed": {
                            "type": ["number", "null"],
                            "description": "The amount billed by the provider for this specific service line, before any discounts or reductions."
                        },
                        "amount_allowed": {
                            "type": ["number", "null"],
                            "description": "The amount allowed by the health plan for this service line after discounts and reductions."
                        },
                        "discounts_and_reductions": {
                            "type": ["number", "null"],
                            "description": "The amount of discounts, reductions, or adjustments applied to this service line by the health plan."
                        },
                        "deductible_amount": {
                            "type": ["number", "null"],
                            "description": "The portion of this service line applied to the patient's deductible."
                        },
                        "copay_amount": {
                            "type": ["number", "null"],
                            "description": "The copayment amount the patient is responsible for on this service line."
                        },
                        "coinsurance_amount": {
                            "type": ["number", "null"],
                            "description": "The coinsurance amount the patient is responsible for on this service line."
                        },
                        "amount_not_covered": {
                            "type": ["number", "null"],
                            "description": "The amount for this service line that is not covered by the health plan and is the patient's responsibility."
                        },
                        "patient_responsibility": {
                            "type": ["number", "null"],
                            "description": "The total amount the patient is responsible for on this service line, including deductible, copay, coinsurance, and non-covered amounts."
                        },
                        "health_plan_responsibility": {
                            "type": ["number", "null"],
                            "description": "The amount paid or payable by the health plan for this service line."
                        },
                        "notes": {
                            "type": ["string", "null"],
                            "description": "Any notes, remarks, or explanations related to this service line, such as denial reasons, bundling, or special instructions."
                        }
                    }
                },
                "description": "The individual service lines or procedures included in this claim. Each item represents a specific service, procedure, or charge with dates, amounts, and benefit application details."
            }
        },
        "required": [
            "member_id",
            "eob_number",
            "plan_name",
            "group_number",
            "subscriber_name",
            "patient_name",
            "provider_name",
            "date_processed",
            "benefit_period_start",
            "benefit_period_end",
            "amount_billed",
            "amount_allowed",
            "discounts_and_reductions",
            "health_plan_responsibility",
            "patient_responsibility",
            "deductible_maximum",
            "deductible_met_to_date",
            "out_of_pocket_maximum",
            "out_of_pocket_met_to_date",
            "claim_line_items"
        ]
    }

    print(f"\n📄 Extracting EOB from: {file_path}")
    extract_run = client.extract_runs.create_and_poll(
        file={"url": data_url},
        config={
            "schema": eob_schema,
        },
    )

    if extract_run.status == "PROCESSED":
        eob_data = extract_run.output.value
        print("\n✅ Extraction successful.\n")
        print("=== EXPLANATION OF BENEFITS SUMMARY ===\n")

        print("📋 MEMBER & CLAIM INFO")
        print(f"  Member ID: {eob_data['member_id']}")
        print(f"  Subscriber: {eob_data['subscriber_name']}")
        print(f"  Patient: {eob_data['patient_name']}")
        print(f"  Plan: {eob_data['plan_name']}")
        print(f"  Group #: {eob_data['group_number']}")
        print(f"  EOB #: {eob_data['eob_number']}")
        print(f"  Processed: {eob_data['date_processed']}\n")

        print("📅 BENEFIT PERIOD")
        print(f"  Start: {eob_data['benefit_period_start']}")
        print(f"  End: {eob_data['benefit_period_end']}\n")

        print("🏥 PROVIDER & SERVICE SUMMARY")
        print(f"  Provider: {eob_data['provider_name']}")
        amount_billed = eob_data['amount_billed']
        print(f"  Amount Billed: {amount_billed['amount']} {amount_billed['iso_4217_currency_code']}")
        amount_allowed = eob_data['amount_allowed']
        print(f"  Amount Allowed: {amount_allowed['amount']} {amount_allowed['iso_4217_currency_code']}")
        discounts = eob_data['discounts_and_reductions']
        print(f"  Discounts & Reductions: {discounts['amount']} {discounts['iso_4217_currency_code']}\n")

        print("💰 FINANCIAL RESPONSIBILITY")
        plan_resp = eob_data['health_plan_responsibility']
        print(f"  Plan Pays: {plan_resp['amount']} {plan_resp['iso_4217_currency_code']}")
        patient_resp = eob_data['patient_responsibility']
        print(f"  You Owe: {patient_resp['amount']} {patient_resp['iso_4217_currency_code']}\n")

        print("📊 BENEFIT PERIOD ACCUMULATORS")
        ded_max = eob_data['deductible_maximum']
        print(f"  Deductible Maximum: {ded_max['amount']} {ded_max['iso_4217_currency_code']}")
        ded_met = eob_data['deductible_met_to_date']
        print(f"  Deductible Met to Date: {ded_met['amount']} {ded_met['iso_4217_currency_code']}")
        oop_max = eob_data['out_of_pocket_maximum']
        print(f"  Out-of-Pocket Maximum: {oop_max['amount']} {oop_max['iso_4217_currency_code']}")
        oop_met = eob_data['out_of_pocket_met_to_date']
        print(f"  Out-of-Pocket Met to Date: {oop_met['amount']} {oop_met['iso_4217_currency_code']}\n")

        if eob_data['claim_line_items'] and len(eob_data['claim_line_items']) > 0:
            print("📝 CLAIM LINE ITEMS\n")
            for idx, item in enumerate(eob_data['claim_line_items']):
                print(f"  Line {idx + 1}: {item['service_description']}")
                service_dates = f"{item['service_start_date']}"
                if item['service_end_date']:
                    service_dates += f" to {item['service_end_date']}"
                print(f"    Service Date: {service_dates}")
                print(f"    Billed: {item['amount_billed']} | Allowed: {item['amount_allowed']}")
                print(f"    Plan Pays: {item['health_plan_responsibility']} | You Pay: {item['patient_responsibility']}")
                if item['notes']:
                    print(f"    Notes: {item['notes']}")
                print()

        return eob_data
    else:
        print(f"❌ Extraction failed with status: {extract_run.status}")
        raise Exception(f"Extraction did not complete successfully: {extract_run.status}")


if __name__ == "__main__":
    file_to_process = sys.argv[1] if len(sys.argv) > 1 else "./eob.pdf"
    process_explanation_of_benefits(file_to_process)
/*
 * Extend AI Java example: REST API directly (no official SDK yet).
 * Uses java.net.http.HttpClient and java.lang built-ins only.
 */

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.List;
import java.util.Map;

public class ExplanationOfBenefitsExtractor {

  private static final String API_BASE_URL = "https://api.extend.ai";
  private static final String API_KEY = System.getenv("EXTEND_API_KEY");
  private static final HttpClient httpClient = HttpClient.newHttpClient();

  /**
   * Process an Explanation of Benefits (EOB) document.
   * Extracts member info, claim details, line items, and benefit period accumulators.
   */
  public static Map<String, Object> processExplanationOfBenefits(String filePath)
      throws IOException, InterruptedException {

    // Read file and convert to data URL (base64)
    byte[] fileBuffer = Files.readAllBytes(Paths.get(filePath));
    String base64Data = Base64.getEncoder().encodeToString(fileBuffer);
    String dataUrl = "data:application/octet-stream;base64," + base64Data;

    // Define the extraction schema as JSON
    String schemaJson =
        "{\n"
            + "  \"type\": \"object\",\n"
            + "  \"properties\": {\n"
            + "    \"member_id\": { \"type\": [\"string\", \"null\"] },\n"
            + "    \"eob_number\": { \"type\": [\"string\", \"null\"] },\n"
            + "    \"plan_name\": { \"type\": [\"string\", \"null\"] },\n"
            + "    \"group_number\": { \"type\": [\"string\", \"null\"] },\n"
            + "    \"subscriber_name\": { \"type\": [\"string\", \"null\"] },\n"
            + "    \"patient_name\": { \"type\": [\"string\", \"null\"] },\n"
            + "    \"provider_name\": { \"type\": [\"string\", \"null\"] },\n"
            + "    \"date_processed\": { \"type\": [\"string\", \"null\"], \"extend:type\": \"date\" },\n"
            + "    \"benefit_period_start\": { \"type\": [\"string\", \"null\"], \"extend:type\": \"date\" },\n"
            + "    \"benefit_period_end\": { \"type\": [\"string\", \"null\"], \"extend:type\": \"date\" },\n"
            + "    \"amount_billed\": {\n"
            + "      \"type\": \"object\",\n"
            + "      \"properties\": {\n"
            + "        \"amount\": { \"type\": [\"number\", \"null\"] },\n"
            + "        \"iso_4217_currency_code\": { \"type\": [\"string\", \"null\"] }\n"
            + "      },\n"
            + "      \"extend:type\": \"currency\"\n"
            + "    },\n"
            + "    \"amount_allowed\": {\n"
            + "      \"type\": \"object\",\n"
            + "      \"properties\": {\n"
            + "        \"amount\": { \"type\": [\"number\", \"null\"] },\n"
            + "        \"iso_4217_currency_code\": { \"type\": [\"string\", \"null\"] }\n"
            + "      },\n"
            + "      \"extend:type\": \"currency\"\n"
            + "    },\n"
            + "    \"discounts_and_reductions\": {\n"
            + "      \"type\": \"object\",\n"
            + "      \"properties\": {\n"
            + "        \"amount\": { \"type\": [\"number\", \"null\"] },\n"
            + "        \"iso_4217_currency_code\": { \"type\": [\"string\", \"null\"] }\n"
            + "      },\n"
            + "      \"extend:type\": \"currency\"\n"
            + "    },\n"
            + "    \"health_plan_responsibility\": {\n"
            + "      \"type\": \"object\",\n"
            + "      \"properties\": {\n"
            + "        \"amount\": { \"type\": [\"number\", \"null\"] },\n"
            + "        \"iso_4217_currency_code\": { \"type\": [\"string\", \"null\"] }\n"
            + "      },\n"
            + "      \"extend:type\": \"currency\"\n"
            + "    },\n"
            + "    \"patient_responsibility\": {\n"
            + "      \"type\": \"object\",\n"
            + "      \"properties\": {\n"
            + "        \"amount\": { \"type\": [\"number\", \"null\"] },\n"
            + "        \"iso_4217_currency_code\": { \"type\": [\"string\", \"null\"] }\n"
            + "      },\n"
            + "      \"extend:type\": \"currency\"\n"
            + "    },\n"
            + "    \"deductible_maximum\": {\n"
            + "      \"type\": \"object\",\n"
            + "      \"properties\": {\n"
            + "        \"amount\": { \"type\": [\"number\", \"null\"] },\n"
            + "        \"iso_4217_currency_code\": { \"type\": [\"string\", \"null\"] }\n"
            + "      },\n"
            + "      \"extend:type\": \"currency\"\n"
            + "    },\n"
            + "    \"deductible_met_to_date\": {\n"
            + "      \"type\": \"object\",\n"
            + "      \"properties\": {\n"
            + "        \"amount\": { \"type\": [\"number\", \"null\"] },\n"
            + "        \"iso_4217_currency_code\": { \"type\": [\"string\", \"null\"] }\n"
            + "      },\n"
            + "      \"extend:type\": \"currency\"\n"
            + "    },\n"
            + "    \"out_of_pocket_maximum\": {\n"
            + "      \"type\": \"object\",\n"
            + "      \"properties\": {\n"
            + "        \"amount\": { \"type\": [\"number\", \"null\"] },\n"
            + "        \"iso_4217_currency_code\": { \"type\": [\"string\", \"null\"] }\n"
            + "      },\n"
            + "      \"extend:type\": \"currency\"\n"
            + "    },\n"
            + "    \"out_of_pocket_met_to_date\": {\n"
            + "      \"type\": \"object\",\n"
            + "      \"properties\": {\n"
            + "        \"amount\": { \"type\": [\"number\", \"null\"] },\n"
            + "        \"iso_4217_currency_code\": { \"type\": [\"string\", \"null\"] }\n"
            + "      },\n"
            + "      \"extend:type\": \"currency\"\n"
            + "    },\n"
            + "    \"claim_line_items\": {\n"
            + "      \"type\": \"array\",\n"
            + "      \"items\": {\n"
            + "        \"type\": \"object\",\n"
            + "        \"properties\": {\n"
            + "          \"service_description\": { \"type\": [\"string\", \"null\"] },\n"
            + "          \"service_start_date\": { \"type\": [\"string\", \"null\"], \"extend:type\": \"date\" },\n"
            + "          \"service_end_date\": { \"type\": [\"string\", \"null\"], \"extend:type\": \"date\" },\n"
            + "          \"amount_billed\": { \"type\": [\"number\", \"null\"] },\n"
            + "          \"amount_allowed\": { \"type\": [\"number\", \"null\"] },\n"
            + "          \"discounts_and_reductions\": { \"type\": [\"number\", \"null\"] },\n"
            + "          \"deductible_amount\": { \"type\": [\"number\", \"null\"] },\n"
            + "          \"copay_amount\": { \"type\": [\"number\", \"null\"] },\n"
            + "          \"coinsurance_amount\": { \"type\": [\"number\", \"null\"] },\n"
            + "          \"amount_not_covered\": { \"type\": [\"number\", \"null\"] },\n"
            + "          \"patient_responsibility\": { \"type\": [\"number\", \"null\"] },\n"
            + "          \"health_plan_responsibility\": { \"type\": [\"number\", \"null\"] },\n"
            + "          \"notes\": { \"type\": [\"string\", \"null\"] }\n"
            + "        }\n"
            + "      }\n"
            + "    }\n"
            + "  }\n"
            + "}\n";

    // Build request body
    String requestBodyJson =
        "{\n"
            + "  \"file\": { \"url\": \""
            + escapeJson(dataUrl)
            + "\" },\n"
            + "  \"config\": { \"schema\": "
            + schemaJson
            + " }\n"
            + "}\n";

    System.out.println("\n📄 Extracting EOB from: " + filePath);

    // Create extract run
    String extractRunId = createExtractRun(requestBodyJson);
    System.out.println("   Extract run ID: " + extractRunId);

    // Poll for completion
    Map<String, Object> extractRun = pollExtractRun(extractRunId);

    // Handle result
    String status = (String) extractRun.get("status");
    if ("PROCESSED".equals(status)) {
      Map<String, Object> output = (Map<String, Object>) extractRun.get("output");
      Map<String, Object> eobData = (Map<String, Object>) output.get("value");

      System.out.println("\n✅ Extraction successful.\n");
      System.out.println("=== EXPLANATION OF BENEFITS SUMMARY ===\n");

      // Member & Claim Info
      System.out.println("📋 MEMBER & CLAIM INFO");
      System.out.println("  Member ID: " + eobData.get("member_id"));
      System.out.println("  Subscriber: " + eobData.get("subscriber_name"));
      System.out.println("  Patient: " + eobData.get("patient_name"));
      System.out.println("  Plan: " + eobData.get("plan_name"));
      System.out.println("  Group #: " + eobData.get("group_number"));
      System.out.println("  EOB #: " + eobData.get("eob_number"));
      System.out.println("  Processed: " + eobData.get("date_processed") + "\n");

      // Benefit Period
      System.out.println("📅 BENEFIT PERIOD");
      System.out.println("  Start: " + eobData.get("benefit_period_start"));
      System.out.println("  End: " + eobData.get("benefit_period_end") + "\n");

      // Provider & Service Summary
      System.out.println("🏥 PROVIDER & SERVICE SUMMARY");
      System.out.println("  Provider: " + eobData.get("provider_name"));
      printCurrencyField(eobData, "amount_billed", "  Amount Billed: ");
      printCurrencyField(eobData, "amount_allowed", "  Amount Allowed: ");
      printCurrencyField(eobData, "discounts_and_reductions", "  Discounts & Reductions: ");
      System.out.println();

      // Financial Responsibility
      System.out.println("💰 FINANCIAL RESPONSIBILITY");
      printCurrencyField(eobData, "health_plan_responsibility", "  Plan Pays: ");
      printCurrencyField(eobData, "patient_responsibility", "  You Owe: ");
      System.out.println();

      // Benefit Period Accumulators
      System.out.println("📊 BENEFIT PERIOD ACCUMULATORS");
      printCurrencyField(eobData, "deductible_maximum", "  Deductible Maximum: ");
      printCurrencyField(eobData, "deductible_met_to_date", "  Deductible Met to Date: ");
      printCurrencyField(eobData, "out_of_pocket_maximum", "  Out-of-Pocket Maximum: ");
      printCurrencyField(
          eobData, "out_of_pocket_met_to_date", "  Out-of-Pocket Met to Date: ");
      System.out.println();

      // Line Items
      List<Map<String, Object>> lineItems =
          (List<Map<String, Object>>) eobData.get("claim_line_items");
      if (lineItems != null && !lineItems.isEmpty()) {
        System.out.println("📝 CLAIM LINE ITEMS\n");
        for (int idx = 0; idx < lineItems.size(); idx++) {
          Map<String, Object> item = lineItems.get(idx);
          System.out.println("  Line " + (idx + 1) + ": " + item.get("service_description"));
          String endDate = (String) item.get("service_end_date");
          String dateRange =
              endDate != null && !endDate.isEmpty()
                  ? " to " + endDate
                  : "";
          System.out.println("    Service Date: " + item.get("service_start_date") + dateRange);
          System.out.println(
              "    Billed: "
                  + item.get("amount_billed")
                  + " | Allowed: "
                  + item.get("amount_allowed"));
          System.out.println(
              "    Plan Pays: "
                  + item.get("health_plan_responsibility")
                  + " | You Pay: "
                  + item.get("patient_responsibility"));
          String notes = (String) item.get("notes");
          if (notes != null && !notes.isEmpty()) {
            System.out.println("    Notes: " + notes);
          }
          System.out.println();
        }
      }

      return eobData;
    } else {
      System.err.println("❌ Extraction failed with status: " + status);
      throw new RuntimeException("Extraction did not complete successfully: " + status);
    }
  }

  private static String createExtractRun(String requestBody)
      throws IOException, InterruptedException {
    HttpRequest request =
        HttpRequest.newBuilder()
            .uri(URI.create(API_BASE_URL + "/extraction/runs"))
            .header("Authorization", "Bearer " + API_KEY)
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(requestBody))
            .build();

    HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

    if (response.statusCode() != 200 && response.statusCode() != 201) {
      throw new RuntimeException(
          "Failed to create extract run. Status: "
              + response.statusCode()
              + ", Body: "
              + response.body());
    }

    // Parse JSON response to extract ID
    String responseBody = response.body();
    int idIndex = responseBody.indexOf("\"id\"");
    if (idIndex != -1) {
      int quoteStart = responseBody.indexOf("\"", idIndex + 5);
      int quoteEnd = responseBody.indexOf("\"", quoteStart + 1);
      return responseBody.substring(quoteStart + 1, quoteEnd);
    }
    throw new RuntimeException("Could not parse extract run ID from response");
  }

  private static Map<String, Object> pollExtractRun(String runId)
      throws IOException, InterruptedException {
    int maxAttempts = 120;
    int attemptCount = 0;

    while (attemptCount < maxAttempts) {
      HttpRequest request =
          HttpRequest.newBuilder()
              .uri(URI.create(API_BASE_URL + "/extraction/runs/" + runId))
              .header("Authorization", "Bearer " + API_KEY)
              .GET()
              .build();

      HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

      if (response.statusCode() != 200) {
        throw new RuntimeException(
            "Failed to poll extract run. Status: "
                + response.statusCode()
                + ", Body: "
                + response.body());
      }

      String responseBody = response.body();
      Map<String, Object> result = parseJsonResponse(responseBody);
      String status = (String) result.get("status");

      if ("PROCESSED".equals(status) || "FAILED".equals(status)) {
        return result;
      }

      attemptCount++;
      if (attemptCount < maxAttempts) {
        Thread.sleep(1000); // Wait 1 second before next poll
      }
    }

    throw new RuntimeException("Extract run polling timed out after " + maxAttempts + " attempts");
  }

  private static Map<String, Object> parseJsonResponse(String json) {
    // Simple JSON parsing for extract run response
    Map<String, Object> result = new java.util.HashMap<>();

    // Extract status
    int statusIndex = json.indexOf("\"status\"");
    if (statusIndex != -1) {
      int quoteStart = json.indexOf("\"", statusIndex + 10);
      int quoteEnd = json.indexOf("\"", quoteStart + 1);
      result.put("status", json.substring(quoteStart + 1, quoteEnd));
    }

    // Extract output if present
    int outputIndex = json.indexOf("\"output\"");
    if (outputIndex != -1) {
      int valueIndex = json.indexOf("\"value\"", outputIndex);
      if (valueIndex != -1) {
        // Extract the value object (simplified: assumes it's a valid JSON object)
        int openBrace = json.indexOf("{", valueIndex);
        int closeBrace = findMatchingBrace(json, openBrace);
        String valueJson = json.substring(openBrace, closeBrace + 1);
        Map<String, Object> output = new java.util.HashMap<>();
        output.put("value", parseComplexJson(valueJson));
        result.put("output", output);
      }
    }

    return result;
  }

  private static Map<String, Object> parseComplexJson(String json) {
    // Simplified JSON object parser for EOB data
    Map<String, Object> map = new java.util.HashMap<>();
    int pos = 0;

    while (pos < json.length()) {
      // Find next key
      int keyStart = json.indexOf("\"", pos);
      if (keyStart == -1) break;

      int keyEnd = json.indexOf("\"", keyStart + 1);
      String key = json.substring(keyStart + 1, keyEnd);

      // Find colon
      int colonPos = json.indexOf(":", keyEnd);
      if (colonPos == -1) break;

      // Parse value
      int valueStart = colonPos + 1;
      while (valueStart < json.length() && Character.isWhitespace(json.charAt(valueStart))) {
        valueStart++;
      }

      Object value = null;
      if (json.charAt(valueStart) == '"') {
        // String value
        int stringEnd = json.indexOf("\"", valueStart + 1);
        value = json.substring(valueStart + 1, stringEnd);
        pos = stringEnd + 1;
      } else if (json.charAt(valueStart) == '{') {
        // Object value
        int objectEnd = findMatchingBrace(json, valueStart);
        value = parseComplexJson(json.substring(valueStart, objectEnd + 1));
        pos = objectEnd + 1;
      } else if (json.charAt(valueStart) == '[') {
        // Array value
        int arrayEnd = findMatchingBracket(json, valueStart);
        value = parseJsonArray(json.substring(valueStart, arrayEnd + 1));
        pos = arrayEnd + 1;
      } else if (json.startsWith("null", valueStart)) {
        value = null;
        pos = valueStart + 4;
      } else {
        // Number or boolean
        int endPos = valueStart;
        while (endPos < json.length()
            && !Character.isWhitespace(json.charAt(endPos))
            && json.charAt(endPos) != ','
            && json.charAt(endPos) != '}') {
          endPos++;
        }
        String numStr = json.substring(valueStart, endPos).trim();
        try {
          if (numStr.contains(".")) {
            value = Double.parseDouble(numStr);
          } else {
            value = Long.parseLong(numStr);
          }
        } catch (NumberFormatException e) {
          value = numStr;
        }
        pos = endPos;
      }

      map.put(key, value);

      // Skip to next comma or end
      while (pos < json.length() && json.charAt(pos) != ',' && json.charAt(pos) != '}') {
        pos++;
      }
      if (pos < json.length() && json.charAt(pos) == ',') {
        pos++;
      }
    }

    return map;
  }

  private static List<Map<String, Object>> parseJsonArray(String json) {
    List<Map<String, Object>> list = new java.util.ArrayList<>();
    int pos = 1; // Skip opening bracket

    while (pos < json.length() && json.charAt(pos) != ']') {
      while (pos < json.length() && Character.isWhitespace(json.charAt(pos))) {
        pos++;
      }

      if (json.charAt(pos) == '{') {
        int end = findMatchingBrace(json, pos);
        list.add(parseComplexJson(json.substring(pos, end + 1)));
        pos = end + 1;
      }

      while (pos < json.length() && json.charAt(pos) != ',' && json.charAt(pos) != ']') {
        pos++;
      }
      if (pos < json.length() && json.charAt(pos) == ',') {
        pos++;
      }
    }

    return list;
  }

  private static int findMatchingBrace(String str, int start) {
    int count = 1;
    int pos = start + 1;
    while (pos < str.length() && count > 0) {
      if (str.charAt(pos) == '{') count++;
      else if (str.charAt(pos) == '}') count--;
      pos++;
    }
    return pos - 1;
  }

  private static int findMatchingBracket(String str, int start) {
    int count = 1;
    int pos = start + 1;
    while (pos < str.length() && count > 0) {
      if (str.charAt(pos) == '[') count++;
      else if (str.charAt(pos) == ']') count--;
      pos++;
    }
    return pos - 1;
  }

  private static void printCurrencyField(
      Map<String, Object> data, String fieldName, String label) {
    Object fieldValue = data.get(fieldName);
    if (fieldValue instanceof Map) {
      Map<String, Object> currencyObj = (Map<String, Object>) fieldValue;
      Object amount = currencyObj.get("amount");
      Object currency = currencyObj.get("iso_4217_currency_code");
      System.out.println(label + amount + " " + currency);
    } else {
      System.out.println(label + fieldValue);
    }
  }

  private static String escapeJson(String str) {
    return str.replace("\\", "\\\\")
        .replace("\"", "\\\"")
        .replace("\n", "\\n")
        .replace("\r", "\\r")
        .replace("\t", "\\t");
  }

  public static void main(String[] args) {
    try {
      String filePath = args.length > 0 ? args[0] : "./eob.pdf";
      processExplanationOfBenefits(filePath);
    } catch (Exception e) {
      System.err.println("Error: " + e.getMessage());
      e.printStackTrace();
    }
  }
}
package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"time"
)

// NOTE: This code uses the Extend REST API directly because Extend has no official Go SDK.
// The API endpoints and request/response formats match those used by the TypeScript SDK.

type CurrencyValue struct {
	Amount             *float64 `json:"amount"`
	ISO4217CurrencyCode *string `json:"iso_4217_currency_code"`
}

type ClaimLineItem struct {
	ServiceDescription      *string  `json:"service_description"`
	ServiceStartDate        *string  `json:"service_start_date"`
	ServiceEndDate          *string  `json:"service_end_date"`
	AmountBilled            *float64 `json:"amount_billed"`
	AmountAllowed           *float64 `json:"amount_allowed"`
	DiscountsAndReductions  *float64 `json:"discounts_and_reductions"`
	DeductibleAmount        *float64 `json:"deductible_amount"`
	CopayAmount             *float64 `json:"copay_amount"`
	CoinsuranceAmount       *float64 `json:"coinsurance_amount"`
	AmountNotCovered        *float64 `json:"amount_not_covered"`
	PatientResponsibility   *float64 `json:"patient_responsibility"`
	HealthPlanResponsibility *float64 `json:"health_plan_responsibility"`
	Notes                   *string  `json:"notes"`
}

type ExplanationOfBenefits struct {
	MemberID                    *string           `json:"member_id"`
	EOBNumber                   *string           `json:"eob_number"`
	PlanName                    *string           `json:"plan_name"`
	GroupNumber                 *string           `json:"group_number"`
	SubscriberName              *string           `json:"subscriber_name"`
	PatientName                 *string           `json:"patient_name"`
	ProviderName                *string           `json:"provider_name"`
	DateProcessed               *string           `json:"date_processed"`
	BenefitPeriodStart          *string           `json:"benefit_period_start"`
	BenefitPeriodEnd            *string           `json:"benefit_period_end"`
	AmountBilled                *CurrencyValue    `json:"amount_billed"`
	AmountAllowed               *CurrencyValue    `json:"amount_allowed"`
	DiscountsAndReductions      *CurrencyValue    `json:"discounts_and_reductions"`
	HealthPlanResponsibility    *CurrencyValue    `json:"health_plan_responsibility"`
	PatientResponsibility       *CurrencyValue    `json:"patient_responsibility"`
	DeductibleMaximum           *CurrencyValue    `json:"deductible_maximum"`
	DeductibleMetToDate         *CurrencyValue    `json:"deductible_met_to_date"`
	OutOfPocketMaximum          *CurrencyValue    `json:"out_of_pocket_maximum"`
	OutOfPocketMetToDate        *CurrencyValue    `json:"out_of_pocket_met_to_date"`
	ClaimLineItems              []ClaimLineItem   `json:"claim_line_items"`
}

type ExtractSchema struct {
	Type       string                 `json:"type"`
	Properties map[string]interface{} `json:"properties"`
	Required   []string               `json:"required"`
}

type ExtractRunRequest struct {
	File   FileInput     `json:"file"`
	Config ExtractConfig `json:"config"`
}

type FileInput struct {
	URL string `json:"url"`
}

type ExtractConfig struct {
	Schema ExtractSchema `json:"schema"`
}

type ExtractRunResponse struct {
	ID     string          `json:"id"`
	Status string          `json:"status"`
	Output *OutputValue    `json:"output"`
	Error  *ErrorDetails   `json:"error"`
}

type OutputValue struct {
	Value ExplanationOfBenefits `json:"value"`
}

type ErrorDetails struct {
	Message string `json:"message"`
}

func getExtractionSchema() ExtractSchema {
	return ExtractSchema{
		Type: "object",
		Properties: map[string]interface{}{
			"member_id": map[string]interface{}{
				"type":        []string{"string", "null"},
				"description": "The unique identification number assigned to the insured member or subscriber.",
			},
			"eob_number": map[string]interface{}{
				"type":        []string{"string", "null"},
				"description": "The unique identifier for this Explanation of Benefits document.",
			},
			"plan_name": map[string]interface{}{
				"type":        []string{"string", "null"},
				"description": "The name of the health insurance plan or product.",
			},
			"group_number": map[string]interface{}{
				"type":        []string{"string", "null"},
				"description": "The group or employer number associated with the health plan.",
			},
			"subscriber_name": map[string]interface{}{
				"type":        []string{"string", "null"},
				"description": "The name of the primary insured individual or policyholder.",
			},
			"patient_name": map[string]interface{}{
				"type":        []string{"string", "null"},
				"description": "The full name of the individual who received the medical services.",
			},
			"provider_name": map[string]interface{}{
				"type":        []string{"string", "null"},
				"description": "The name of the healthcare provider, facility, or organization.",
			},
			"date_processed": map[string]interface{}{
				"type":          []string{"string", "null"},
				"description":   "The date on which this claim or EOB was processed.",
				"extend:type":   "date",
			},
			"benefit_period_start": map[string]interface{}{
				"type":          []string{"string", "null"},
				"description":   "The start date of the benefit period.",
				"extend:type":   "date",
			},
			"benefit_period_end": map[string]interface{}{
				"type":          []string{"string", "null"},
				"description":   "The end date of the benefit period.",
				"extend:type":   "date",
			},
			"amount_billed": map[string]interface{}{
				"type":          "object",
				"extend:type":   "currency",
				"description":   "The total amount billed by the provider.",
			},
			"amount_allowed": map[string]interface{}{
				"type":          "object",
				"extend:type":   "currency",
				"description":   "The total amount allowed by the health plan.",
			},
			"discounts_and_reductions": map[string]interface{}{
				"type":          "object",
				"extend:type":   "currency",
				"description":   "The total amount of discounts and reductions.",
			},
			"health_plan_responsibility": map[string]interface{}{
				"type":          "object",
				"extend:type":   "currency",
				"description":   "The total amount paid or payable by the health plan.",
			},
			"patient_responsibility": map[string]interface{}{
				"type":          "object",
				"extend:type":   "currency",
				"description":   "The total amount the patient may owe.",
			},
			"deductible_maximum": map[string]interface{}{
				"type":          "object",
				"extend:type":   "currency",
				"description":   "The total deductible maximum.",
			},
			"deductible_met_to_date": map[string]interface{}{
				"type":          "object",
				"extend:type":   "currency",
				"description":   "The cumulative amount of deductible met.",
			},
			"out_of_pocket_maximum": map[string]interface{}{
				"type":          "object",
				"extend:type":   "currency",
				"description":   "The total out-of-pocket maximum.",
			},
			"out_of_pocket_met_to_date": map[string]interface{}{
				"type":          "object",
				"extend:type":   "currency",
				"description":   "The cumulative amount of out-of-pocket expenses met.",
			},
			"claim_line_items": map[string]interface{}{
				"type":        "array",
				"description": "The individual service lines or procedures included in this claim.",
			},
		},
		Required: []string{
			"member_id", "plan_name", "eob_number", "group_number", "patient_name",
			"amount_billed", "provider_name", "amount_allowed", "date_processed",
			"subscriber_name", "claim_line_items", "benefit_period_end", "deductible_maximum",
			"benefit_period_start", "out_of_pocket_maximum", "deductible_met_to_date",
			"patient_responsibility", "discounts_and_reductions", "out_of_pocket_met_to_date",
			"health_plan_responsibility",
		},
	}
}

func fileToDataURL(filePath string) (string, error) {
	fileBuffer, err := os.ReadFile(filePath)
	if err != nil {
		return "", err
	}
	encoded := base64.StdEncoding.EncodeToString(fileBuffer)
	return "data:application/octet-stream;base64," + encoded, nil
}

func createExtractRun(apiKey string, dataURL string) (*ExtractRunResponse, error) {
	schema := getExtractionSchema()
	
	reqBody := ExtractRunRequest{
		File: FileInput{URL: dataURL},
		Config: ExtractConfig{Schema: schema},
	}

	jsonBody, err := json.Marshal(reqBody)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", "https://api.extend.ai/api/extract-runs", bytes.NewReader(jsonBody))
	if err != nil {
		return nil, err
	}

	req.Header.Set("Authorization", "Bearer "+apiKey)
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var result ExtractRunResponse
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, err
	}

	return &result, nil
}

func pollExtractRun(apiKey string, runID string, maxAttempts int, interval time.Duration) (*ExtractRunResponse, error) {
	for attempt := 0; attempt < maxAttempts; attempt++ {
		req, err := http.NewRequest("GET", "https://api.extend.ai/api/extract-runs/"+runID, nil)
		if err != nil {
			return nil, err
		}

		req.Header.Set("Authorization", "Bearer "+apiKey)
		req.Header.Set("Content-Type", "application/json")

		client := &http.Client{Timeout: 30 * time.Second}
		resp, err := client.Do(req)
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()

		body, err := io.ReadAll(resp.Body)
		if err != nil {
			return nil, err
		}

		var result ExtractRunResponse
		if err := json.Unmarshal(body, &result); err != nil {
			return nil, err
		}

		if result.Status == "PROCESSED" || result.Status == "FAILED" {
			return &result, nil
		}

		if attempt < maxAttempts-1 {
			time.Sleep(interval)
		}
	}

	return nil, fmt.Errorf("extraction polling timed out after %d attempts", maxAttempts)
}

func ProcessExplanationOfBenefits(filePath string) (*ExplanationOfBenefits, error) {
	apiKey := os.Getenv("EXTEND_API_KEY")
	if apiKey == "" {
		return nil, fmt.Errorf("EXTEND_API_KEY environment variable not set")
	}

	dataURL, err := fileToDataURL(filePath)
	if err != nil {
		return nil, err
	}

	fmt.Printf("\n📄 Extracting EOB from: %s\n", filePath)

	extractRun, err := createExtractRun(apiKey, dataURL)
	if err != nil {
		return nil, err
	}

	extractRun, err = pollExtractRun(apiKey, extractRun.ID, 120, 1*time.Second)
	if err != nil {
		return nil, err
	}

	if extractRun.Status == "PROCESSED" {
		eobData := &extractRun.Output.Value
		fmt.Println("\n✅ Extraction successful.\n")
		fmt.Println("=== EXPLANATION OF BENEFITS SUMMARY ===\n")

		fmt.Println("📋 MEMBER & CLAIM INFO")
		fmt.Printf("  Member ID: %v\n", eobData.MemberID)
		fmt.Printf("  Subscriber: %v\n", eobData.SubscriberName)
		fmt.Printf("  Patient: %v\n", eobData.PatientName)
		fmt.Printf("  Plan: %v\n", eobData.PlanName)
		fmt.Printf("  Group #: %v\n", eobData.GroupNumber)
		fmt.Printf("  EOB #: %v\n", eobData.EOBNumber)
		fmt.Printf("  Processed: %v\n\n", eobData.DateProcessed)

		fmt.Println("📅 BENEFIT PERIOD")
		fmt.Printf("  Start: %v\n", eobData.BenefitPeriodStart)
		fmt.Printf("  End: %v\n\n", eobData.BenefitPeriodEnd)

		fmt.Println("🏥 PROVIDER & SERVICE SUMMARY")
		fmt.Printf("  Provider: %v\n", eobData.ProviderName)
		if eobData.AmountBilled != nil {
			fmt.Printf("  Amount Billed: %v %v\n", eobData.AmountBilled.Amount, eobData.AmountBilled.ISO4217CurrencyCode)
		}
		if eobData.AmountAllowed != nil {
			fmt.Printf("  Amount Allowed: %v %v\n", eobData.AmountAllowed.Amount, eobData.AmountAllowed.ISO4217CurrencyCode)
		}
		if eobData.DiscountsAndReductions != nil {
			fmt.Printf("  Discounts & Reductions: %v %v\n\n", eobData.DiscountsAndReductions.Amount, eobData.DiscountsAndReductions.ISO4217CurrencyCode)
		}

		fmt.Println("💰 FINANCIAL RESPONSIBILITY")
		if eobData.HealthPlanResponsibility != nil {
			fmt.Printf("  Plan Pays: %v %v\n", eobData.HealthPlanResponsibility.Amount, eobData.HealthPlanResponsibility.ISO4217CurrencyCode)
		}
		if eobData.PatientResponsibility != nil {
			fmt.Printf("  You Owe: %v %v\n\n", eobData.PatientResponsibility.Amount, eobData.PatientResponsibility.ISO4217CurrencyCode)
		}

		fmt.Println("📊 BENEFIT PERIOD ACCUMULATORS")
		if eobData.DeductibleMaximum != nil {
			fmt.Printf("  Deductible Maximum: %v %v\n", eobData.DeductibleMaximum.Amount, eobData.DeductibleMaximum.ISO4217CurrencyCode)
		}
		if eobData.DeductibleMetToDate != nil {
			fmt.Printf("  Deductible Met to Date: %v %v\n", eobData.DeductibleMetToDate.Amount, eobData.DeductibleMetToDate.ISO4217CurrencyCode)
		}
		if eobData.OutOfPocketMaximum != nil {
			fmt.Printf("  Out-of-Pocket Maximum: %v %v\n", eobData.OutOfPocketMaximum.Amount, eobData.OutOfPocketMaximum.ISO4217CurrencyCode)
		}
		if eobData.OutOfPocketMetToDate != nil {
			fmt.Printf("  Out-of-Pocket Met to Date: %v %v\n\n", eobData.OutOfPocketMetToDate.Amount, eobData.OutOfPocketMetToDate.ISO4217CurrencyCode)
		}

		if len(eobData.ClaimLineItems) > 0 {
			fmt.Println("📝 CLAIM LINE ITEMS\n")
			for idx, item := range eobData.ClaimLineItems {
				fmt.Printf("  Line %d: %v\n", idx+1, item.ServiceDescription)
				endDateStr := ""
				if item.ServiceEndDate != nil {
					endDateStr = fmt.Sprintf(" to %v", *item.ServiceEndDate)
				}
				fmt.Printf("    Service Date: %v%s\n", item.ServiceStartDate, endDateStr)
				fmt.Printf("    Billed: %v | Allowed: %v\n", item.AmountBilled, item.AmountAllowed)
				fmt.Printf("    Plan Pays: %v | You Pay: %v\n", item.HealthPlanResponsibility, item.PatientResponsibility)
				if item.Notes != nil {
					fmt.Printf("    Notes: %v\n", *item.Notes)
				}
				fmt.Println()
			}
		}

		return eobData, nil
	}

	return nil, fmt.Errorf("extraction failed with status: %s", extractRun.Status)
}

func main() {
	filePath := "./eob.pdf"
	if len(os.Args) > 1 {
		filePath = os.Args[1]
	}

	_, err := ProcessExplanationOfBenefits(filePath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}
}
// Deploy the "Explanation of Benefits" pipeline to YOUR Extend account.
//
// The workflow below is fully self-contained — every EXTRACT/CLASSIFY/SPLIT
// step carries its extractor/classifier/splitter config INLINE, so this is a
// single API call. No processors to create or wire up beforehand.
// Idempotent: the created workflow id is cached in .extend/explanation-of-benefits-eob-extractor.json,
// so re-running updates the existing workflow instead of duplicating it.
//
// Usage:
//   export EXTEND_API_KEY=sk_...   (from https://dashboard.extend.ai → API Keys)
//   npx tsx provision.ts
//
// Generated by doc1 (template: explanation-of-benefits-eob-extractor).

import fs from "node:fs";
import path from "node:path";

const API = "https://api.extend.ai";
const VERSION = "2026-02-09";
const API_KEY = process.env.EXTEND_API_KEY;
if (!API_KEY) { console.error("Set EXTEND_API_KEY first."); process.exit(1); }

const STATE_DIR = path.join(process.cwd(), ".extend");
const STATE_FILE = path.join(STATE_DIR, "explanation-of-benefits-eob-extractor.json");

type State = { workflowId?: string };
const state: State = fs.existsSync(STATE_FILE)
  ? JSON.parse(fs.readFileSync(STATE_FILE, "utf8"))
  : {};
function saveState() {
  fs.mkdirSync(STATE_DIR, { recursive: true });
  fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
}

async function api(method: string, pathName: string, body?: unknown) {
  const res = await fetch(API + pathName, {
    method,
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      "x-extend-api-version": VERSION,
      ...(body ? { "Content-Type": "application/json" } : {}),
    },
    body: body ? JSON.stringify(body) : undefined,
  });
  const data = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(`${method} ${pathName} failed (${res.status}): ${JSON.stringify(data).slice(0, 300)}`);
  return data;
}

// ── Workflow definition — extractor/classifier/splitter configs inline ──────
const WORKFLOW = {
  "name": "Explanation of Benefits Processing Pipeline",
  "steps": [
    {
      "name": "startTrigger1",
      "type": "TRIGGER",
      "next": [
        {
          "step": "parse1"
        }
      ]
    },
    {
      "name": "parse1",
      "type": "PARSE",
      "config": {
        "parseConfig": {
          "blockOptions": {
            "text": {
              "agentic": {
                "enabled": true
              },
              "signatureDetectionEnabled": true
            },
            "tables": {
              "agentic": {
                "enabled": true
              },
              "tableHeaderContinuationEnabled": true
            },
            "figures": {
              "enabled": true
            }
          },
          "chunkingStrategy": {
            "type": "page",
            "options": {}
          }
        }
      },
      "next": [
        {
          "step": "extraction2"
        }
      ]
    },
    {
      "name": "extraction2",
      "type": "EXTRACT",
      "config": {
        "extractorConfig": {
          "schema": {
            "type": "object",
            "required": [
              "member_id",
              "plan_name",
              "eob_number",
              "group_number",
              "patient_name",
              "amount_billed",
              "provider_name",
              "amount_allowed",
              "date_processed",
              "subscriber_name",
              "claim_line_items",
              "benefit_period_end",
              "deductible_maximum",
              "benefit_period_start",
              "out_of_pocket_maximum",
              "deductible_met_to_date",
              "patient_responsibility",
              "discounts_and_reductions",
              "out_of_pocket_met_to_date",
              "health_plan_responsibility"
            ],
            "properties": {
              "member_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The unique identification number assigned to the insured member or subscriber. This is used by the health plan to identify the covered individual. May be labeled as 'Member ID', 'Subscriber ID', or similar."
              },
              "plan_name": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The name of the health insurance plan or product under which the claim was processed. May appear as 'Plan Name', 'Product', or similar."
              },
              "eob_number": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The unique identifier for this Explanation of Benefits (EOB) document or claim. This is the primary reference number used to track this EOB. May include numbers, letters, or special characters. Common labels include 'Claim #', 'EOB #', or similar."
              },
              "group_number": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The group or employer number associated with the health plan. This identifies the employer or group plan under which the member is covered. Commonly labeled as 'Group #', 'Group Number', etc."
              },
              "patient_name": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The full name of the individual who received the medical services. May be labeled as 'Patient', 'Covered Person', or similar."
              },
              "amount_billed": {
                "type": "object",
                "required": [
                  "amount",
                  "iso_4217_currency_code"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "iso_4217_currency_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "The total amount billed by the provider for all services included in this claim. This is the original charge before any discounts, reductions, or insurance adjustments.",
                "extend:type": "currency",
                "additionalProperties": false
              },
              "provider_name": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The name of the healthcare provider, facility, or organization that rendered the services for this claim. May be labeled as 'Provider', 'Facility', or similar."
              },
              "amount_allowed": {
                "type": "object",
                "required": [
                  "amount",
                  "iso_4217_currency_code"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "iso_4217_currency_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "The total amount allowed by the health plan for the services provided, after discounts and reductions. This is the maximum amount considered for payment.",
                "extend:type": "currency",
                "additionalProperties": false
              },
              "date_processed": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The date on which this claim or EOB was processed by the health plan. May be labeled as 'Date Processed', 'Processed On', or similar.",
                "extend:type": "date"
              },
              "subscriber_name": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The name of the primary insured individual or policyholder. This is the person who holds the insurance policy, sometimes labeled as 'Subscriber', 'Participant', or 'Member'."
              },
              "claim_line_items": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "notes",
                    "copay_amount",
                    "amount_billed",
                    "amount_allowed",
                    "service_end_date",
                    "deductible_amount",
                    "amount_not_covered",
                    "coinsurance_amount",
                    "service_start_date",
                    "service_description",
                    "patient_responsibility",
                    "discounts_and_reductions",
                    "health_plan_responsibility"
                  ],
                  "properties": {
                    "notes": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Any notes, remarks, or explanations related to this service line, such as denial reasons, bundling, or special instructions. May be referenced by footnotes or symbols."
                    },
                    "copay_amount": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "The copayment amount the patient is responsible for on this service line."
                    },
                    "amount_billed": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "The amount billed by the provider for this specific service line, before any discounts or reductions."
                    },
                    "amount_allowed": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "The amount allowed by the health plan for this service line after discounts and reductions."
                    },
                    "service_end_date": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "The date on which the service or procedure ended, if applicable. May be omitted if service occurred on a single day.",
                      "extend:type": "date"
                    },
                    "deductible_amount": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "The portion of this service line applied to the patient's deductible."
                    },
                    "amount_not_covered": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "The amount for this service line that is not covered by the health plan and is the patient's responsibility."
                    },
                    "coinsurance_amount": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "The coinsurance amount the patient is responsible for on this service line."
                    },
                    "service_start_date": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "The date on which the service or procedure began or was rendered. May be labeled as 'Service Date', 'Date of Service', or similar.",
                      "extend:type": "date"
                    },
                    "service_description": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "A description of the medical service, procedure, or supply provided. May include procedure names, codes, or other identifying information."
                    },
                    "patient_responsibility": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "The total amount the patient is responsible for on this service line, including deductible, copay, coinsurance, and non-covered amounts."
                    },
                    "discounts_and_reductions": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "The amount of discounts, reductions, or adjustments applied to this service line by the health plan."
                    },
                    "health_plan_responsibility": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "The amount paid or payable by the health plan for this service line."
                    }
                  },
                  "additionalProperties": false
                },
                "description": "The individual service lines or procedures included in this claim. Each item represents a specific service, procedure, or charge, and includes details about dates, amounts, and benefit application. Format and labeling may vary widely across EOBs."
              },
              "benefit_period_end": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The end date of the benefit period relevant to this EOB or claim. This is the end of the coverage period for deductibles and out-of-pocket calculations.",
                "extend:type": "date"
              },
              "deductible_maximum": {
                "type": "object",
                "required": [
                  "amount",
                  "iso_4217_currency_code"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "iso_4217_currency_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "The total deductible maximum for the benefit period for the patient or family, as specified by the plan.",
                "extend:type": "currency",
                "additionalProperties": false
              },
              "benefit_period_start": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The start date of the benefit period relevant to this EOB or claim. This is the beginning of the coverage period for deductibles and out-of-pocket calculations.",
                "extend:type": "date"
              },
              "out_of_pocket_maximum": {
                "type": "object",
                "required": [
                  "amount",
                  "iso_4217_currency_code"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "iso_4217_currency_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "The total out-of-pocket maximum for the benefit period for the patient or family, as specified by the plan.",
                "extend:type": "currency",
                "additionalProperties": false
              },
              "deductible_met_to_date": {
                "type": "object",
                "required": [
                  "amount",
                  "iso_4217_currency_code"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "iso_4217_currency_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "The cumulative amount of deductible met by the patient for the current benefit period as of this EOB. This reflects progress toward the annual deductible.",
                "extend:type": "currency",
                "additionalProperties": false
              },
              "patient_responsibility": {
                "type": "object",
                "required": [
                  "amount",
                  "iso_4217_currency_code"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "iso_4217_currency_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "The total amount the patient may owe to the provider for these services, after insurance payments and adjustments. This may include deductibles, copays, coinsurance, and non-covered amounts.",
                "extend:type": "currency",
                "additionalProperties": false
              },
              "discounts_and_reductions": {
                "type": "object",
                "required": [
                  "amount",
                  "iso_4217_currency_code"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "iso_4217_currency_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "The total amount of discounts, reductions, or adjustments applied to the billed charges by the health plan. This may include network discounts, negotiated rates, or other reductions.",
                "extend:type": "currency",
                "additionalProperties": false
              },
              "out_of_pocket_met_to_date": {
                "type": "object",
                "required": [
                  "amount",
                  "iso_4217_currency_code"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "iso_4217_currency_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "The cumulative amount of out-of-pocket expenses met by the patient for the current benefit period as of this EOB. This includes deductibles, copays, and coinsurance.",
                "extend:type": "currency",
                "additionalProperties": false
              },
              "health_plan_responsibility": {
                "type": "object",
                "required": [
                  "amount",
                  "iso_4217_currency_code"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "iso_4217_currency_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "The total amount paid or payable by the health plan for this claim. This is the insurer's share of the allowed amount.",
                "extend:type": "currency",
                "additionalProperties": false
              }
            },
            "additionalProperties": false
          },
          "baseProcessor": "extraction_performance"
        }
      }
    }
  ]
};

async function main() {
  console.log(`Deploying "${WORKFLOW.name}"…`);

  if (state.workflowId) {
    console.log(`✓ workflow already provisioned (${state.workflowId}) — updating steps`);
    await api("POST", `/workflows/${state.workflowId}`, { steps: WORKFLOW.steps });
  } else {
    // Reuse an existing workflow with the same name if one exists (e.g. a
    // previous run's state file was lost) instead of creating a duplicate.
    try {
      const list = await api("GET", `/workflows?name=${encodeURIComponent(WORKFLOW.name)}`);
      const items = (list.data ?? list.items ?? []) as Array<{ name?: string; id?: string }>;
      const existing = items.find((x) => x.name === WORKFLOW.name);
      if (existing?.id) {
        state.workflowId = existing.id; saveState();
        console.log(`✓ workflow "${WORKFLOW.name}" found in your account (${existing.id}) — updating steps`);
        await api("POST", `/workflows/${existing.id}`, { steps: WORKFLOW.steps });
      }
    } catch { /* lookup is best-effort; fall through to create */ }

    if (!state.workflowId) {
      const created = await api("POST", "/workflows", WORKFLOW);
      const wfId = created.id ?? created.workflow?.id;
      if (!wfId) throw new Error("Could not read created workflow id from response");
      state.workflowId = wfId; saveState();
      console.log(`+ created workflow (${wfId})`);
    }
  }

  // Deploy the current draft as a new version so the workflow is runnable —
  // best-effort: some accounts/plans may not require this explicit step.
  await api("POST", `/workflows/${state.workflowId}/versions`, {}).catch(() => {});

  console.log("\nDone. Run documents through it with:");
  console.log(`  POST ${API}/workflow_runs  { workflow: { id: "${state.workflowId}" }, file: { url: "https://…" } }`);
  console.log("Or open the workflow in the Extend dashboard to review and deploy it.");
}

main().catch((e) => { console.error(e.message ?? e); process.exit(1); });
import os
import sys
import json
import pathlib
from typing import TypedDict, Optional, Any

from extend_ai import Extend


API_KEY = os.environ.get("EXTEND_API_KEY")
if not API_KEY:
    print("Set EXTEND_API_KEY first.")
    sys.exit(1)

STATE_DIR = pathlib.Path.cwd() / ".extend"
STATE_FILE = STATE_DIR / "explanation-of-benefits-eob-extractor.json"


class State(TypedDict, total=False):
    workflow_id: Optional[str]


def load_state() -> State:
    if STATE_FILE.exists():
        return json.loads(STATE_FILE.read_text())
    return {}


def save_state(state: State) -> None:
    STATE_DIR.mkdir(parents=True, exist_ok=True)
    STATE_FILE.write_text(json.dumps(state, indent=2))


WORKFLOW = {
    "name": "Explanation of Benefits Processing Pipeline",
    "steps": [
        {
            "name": "startTrigger1",
            "type": "TRIGGER",
            "next": [{"step": "parse1"}],
        },
        {
            "name": "parse1",
            "type": "PARSE",
            "config": {
                "parseConfig": {
                    "blockOptions": {
                        "text": {
                            "agentic": {"enabled": True},
                            "signatureDetectionEnabled": True,
                        },
                        "tables": {
                            "agentic": {"enabled": True},
                            "tableHeaderContinuationEnabled": True,
                        },
                        "figures": {"enabled": True},
                    },
                    "chunkingStrategy": {"type": "page", "options": {}},
                }
            },
            "next": [{"step": "extraction2"}],
        },
        {
            "name": "extraction2",
            "type": "EXTRACT",
            "config": {
                "extractorConfig": {
                    "schema": {
                        "type": "object",
                        "required": [
                            "member_id",
                            "plan_name",
                            "eob_number",
                            "group_number",
                            "patient_name",
                            "amount_billed",
                            "provider_name",
                            "amount_allowed",
                            "date_processed",
                            "subscriber_name",
                            "claim_line_items",
                            "benefit_period_end",
                            "deductible_maximum",
                            "benefit_period_start",
                            "out_of_pocket_maximum",
                            "deductible_met_to_date",
                            "patient_responsibility",
                            "discounts_and_reductions",
                            "out_of_pocket_met_to_date",
                            "health_plan_responsibility",
                        ],
                        "properties": {
                            "member_id": {
                                "type": ["string", "null"],
                                "description": "The unique identification number assigned to the insured member or subscriber. This is used by the health plan to identify the covered individual. May be labeled as 'Member ID', 'Subscriber ID', or similar.",
                            },
                            "plan_name": {
                                "type": ["string", "null"],
                                "description": "The name of the health insurance plan or product under which the claim was processed. May appear as 'Plan Name', 'Product', or similar.",
                            },
                            "eob_number": {
                                "type": ["string", "null"],
                                "description": "The unique identifier for this Explanation of Benefits (EOB) document or claim. This is the primary reference number used to track this EOB. May include numbers, letters, or special characters. Common labels include 'Claim #', 'EOB #', or similar.",
                            },
                            "group_number": {
                                "type": ["string", "null"],
                                "description": "The group or employer number associated with the health plan. This identifies the employer or group plan under which the member is covered. Commonly labeled as 'Group #', 'Group Number', etc.",
                            },
                            "patient_name": {
                                "type": ["string", "null"],
                                "description": "The full name of the individual who received the medical services. May be labeled as 'Patient', 'Covered Person', or similar.",
                            },
                            "amount_billed": {
                                "type": "object",
                                "required": ["amount", "iso_4217_currency_code"],
                                "properties": {
                                    "amount": {"type": ["number", "null"]},
                                    "iso_4217_currency_code": {"type": ["string", "null"]},
                                },
                                "description": "The total amount billed by the provider for all services included in this claim. This is the original charge before any discounts, reductions, or insurance adjustments.",
                                "extend:type": "currency",
                                "additionalProperties": False,
                            },
                            "provider_name": {
                                "type": ["string", "null"],
                                "description": "The name of the healthcare provider, facility, or organization that rendered the services for this claim. May be labeled as 'Provider', 'Facility', or similar.",
                            },
                            "amount_allowed": {
                                "type": "object",
                                "required": ["amount", "iso_4217_currency_code"],
                                "properties": {
                                    "amount": {"type": ["number", "null"]},
                                    "iso_4217_currency_code": {"type": ["string", "null"]},
                                },
                                "description": "The total amount allowed by the health plan for the services provided, after discounts and reductions. This is the maximum amount considered for payment.",
                                "extend:type": "currency",
                                "additionalProperties": False,
                            },
                            "date_processed": {
                                "type": ["string", "null"],
                                "description": "The date on which this claim or EOB was processed by the health plan. May be labeled as 'Date Processed', 'Processed On', or similar.",
                                "extend:type": "date",
                            },
                            "subscriber_name": {
                                "type": ["string", "null"],
                                "description": "The name of the primary insured individual or policyholder. This is the person who holds the insurance policy, sometimes labeled as 'Subscriber', 'Participant', or 'Member'.",
                            },
                            "claim_line_items": {
                                "type": "array",
                                "items": {
                                    "type": "object",
                                    "required": [
                                        "notes",
                                        "copay_amount",
                                        "amount_billed",
                                        "amount_allowed",
                                        "service_end_date",
                                        "deductible_amount",
                                        "amount_not_covered",
                                        "coinsurance_amount",
                                        "service_start_date",
                                        "service_description",
                                        "patient_responsibility",
                                        "discounts_and_reductions",
                                        "health_plan_responsibility",
                                    ],
                                    "properties": {
                                        "notes": {
                                            "type": ["string", "null"],
                                            "description": "Any notes, remarks, or explanations related to this service line, such as denial reasons, bundling, or special instructions. May be referenced by footnotes or symbols.",
                                        },
                                        "copay_amount": {
                                            "type": ["number", "null"],
                                            "description": "The copayment amount the patient is responsible for on this service line.",
                                        },
                                        "amount_billed": {
                                            "type": ["number", "null"],
                                            "description": "The amount billed by the provider for this specific service line, before any discounts or reductions.",
                                        },
                                        "amount_allowed": {
                                            "type": ["number", "null"],
                                            "description": "The amount allowed by the health plan for this service line after discounts and reductions.",
                                        },
                                        "service_end_date": {
                                            "type": ["string", "null"],
                                            "description": "The date on which the service or procedure ended, if applicable. May be omitted if service occurred on a single day.",
                                            "extend:type": "date",
                                        },
                                        "deductible_amount": {
                                            "type": ["number", "null"],
                                            "description": "The portion of this service line applied to the patient's deductible.",
                                        },
                                        "amount_not_covered": {
                                            "type": ["number", "null"],
                                            "description": "The amount for this service line that is not covered by the health plan and is the patient's responsibility.",
                                        },
                                        "coinsurance_amount": {
                                            "type": ["number", "null"],
                                            "description": "The coinsurance amount the patient is responsible for on this service line.",
                                        },
                                        "service_start_date": {
                                            "type": ["string", "null"],
                                            "description": "The date on which the service or procedure began or was rendered. May be labeled as 'Service Date', 'Date of Service', or similar.",
                                            "extend:type": "date",
                                        },
                                        "service_description": {
                                            "type": ["string", "null"],
                                            "description": "A description of the medical service, procedure, or supply provided. May include procedure names, codes, or other identifying information.",
                                        },
                                        "patient_responsibility": {
                                            "type": ["number", "null"],
                                            "description": "The total amount the patient is responsible for on this service line, including deductible, copay, coinsurance, and non-covered amounts.",
                                        },
                                        "discounts_and_reductions": {
                                            "type": ["number", "null"],
                                            "description": "The amount of discounts, reductions, or adjustments applied to this service line by the health plan.",
                                        },
                                        "health_plan_responsibility": {
                                            "type": ["number", "null"],
                                            "description": "The amount paid or payable by the health plan for this service line.",
                                        },
                                    },
                                    "additionalProperties": False,
                                },
                                "description": "The individual service lines or procedures included in this claim. Each item represents a specific service, procedure, or charge, and includes details about dates, amounts, and benefit application. Format and labeling may vary widely across EOBs.",
                            },
                            "benefit_period_end": {
                                "type": ["string", "null"],
                                "description": "The end date of the benefit period relevant to this EOB or claim. This is the end of the coverage period for deductibles and out-of-pocket calculations.",
                                "extend:type": "date",
                            },
                            "deductible_maximum": {
                                "type": "object",
                                "required": ["amount", "iso_4217_currency_code"],
                                "properties": {
                                    "amount": {"type": ["number", "null"]},
                                    "iso_4217_currency_code": {"type": ["string", "null"]},
                                },
                                "description": "The total deductible maximum for the benefit period for the patient or family, as specified by the plan.",
                                "extend:type": "currency",
                                "additionalProperties": False,
                            },
                            "benefit_period_start": {
                                "type": ["string", "null"],
                                "description": "The start date of the benefit period relevant to this EOB or claim. This is the beginning of the coverage period for deductibles and out-of-pocket calculations.",
                                "extend:type": "date",
                            },
                            "out_of_pocket_maximum": {
                                "type": "object",
                                "required": ["amount", "iso_4217_currency_code"],
                                "properties": {
                                    "amount": {"type": ["number", "null"]},
                                    "iso_4217_currency_code": {"type": ["string", "null"]},
                                },
                                "description": "The total out-of-pocket maximum for the benefit period for the patient or family, as specified by the plan.",
                                "extend:type": "currency",
                                "additionalProperties": False,
                            },
                            "deductible_met_to_date": {
                                "type": "object",
                                "required": ["amount", "iso_4217_currency_code"],
                                "properties": {
                                    "amount": {"type": ["number", "null"]},
                                    "iso_4217_currency_code": {"type": ["string", "null"]},
                                },
                                "description": "The cumulative amount of deductible met by the patient for the current benefit period as of this EOB. This reflects progress toward the annual deductible.",
                                "extend:type": "currency",
                                "additionalProperties": False,
                            },
                            "patient_responsibility": {
                                "type": "object",
                                "required": ["amount", "iso_4217_currency_code"],
                                "properties": {
                                    "amount": {"type": ["number", "null"]},
                                    "iso_4217_currency_code": {"type": ["string", "null"]},
                                },
                                "description": "The total amount the patient may owe to the provider for these services, after insurance payments and adjustments. This may include deductibles, copays, coinsurance, and non-covered amounts.",
                                "extend:type": "currency",
                                "additionalProperties": False,
                            },
                            "discounts_and_reductions": {
                                "type": "object",
                                "required": ["amount", "iso_4217_currency_code"],
                                "properties": {
                                    "amount": {"type": ["number", "null"]},
                                    "iso_4217_currency_code": {"type": ["string", "null"]},
                                },
                                "description": "The total amount of discounts, reductions, or adjustments applied to the billed charges by the health plan. This may include network discounts, negotiated rates, or other reductions.",
                                "extend:type": "currency",
                                "additionalProperties": False,
                            },
                            "out_of_pocket_met_to_date": {
                                "type": "object",
                                "required": ["amount", "iso_4217_currency_code"],
                                "properties": {
                                    "amount": {"type": ["number", "null"]},
                                    "iso_4217_currency_code": {"type": ["string", "null"]},
                                },
                                "description": "The cumulative amount of out-of-pocket expenses met by the patient for the current benefit period as of this EOB. This includes deductibles, copays, and coinsurance.",
                                "extend:type": "currency",
                                "additionalProperties": False,
                            },
                            "health_plan_responsibility": {
                                "type": "object",
                                "required": ["amount", "iso_4217_currency_code"],
                                "properties": {
                                    "amount": {"type": ["number", "null"]},
                                    "iso_4217_currency_code": {"type": ["string", "null"]},
                                },
                                "description": "The total amount paid or payable by the health plan for this claim. This is the insurer's share of the allowed amount.",
                                "extend:type": "currency",
                                "additionalProperties": False,
                            },
                        },
                        "additionalProperties": False,
                    },
                    "baseProcessor": "extraction_performance",
                }
            },
        },
    ],
}


async def main() -> None:
    client = Extend(token=API_KEY)
    state = load_state()

    print(f"Deploying \"{WORKFLOW['name']}\"…")

    if state.get("workflow_id"):
        workflow_id = state["workflow_id"]
        print(f"✓ workflow already provisioned ({workflow_id}) — updating steps")
        await client.workflows.update(
            workflow_id, steps=WORKFLOW["steps"]
        )
    else:
        try:
            workflows_response = await client.workflows.list(
                name=WORKFLOW["name"]
            )
            items: list[dict[str, Any]] = getattr(
                workflows_response, "data", None
            ) or getattr(workflows_response, "items", [])
            existing = next(
                (w for w in items if w.get("name") == WORKFLOW["name"]),
                None,
            )
            if existing and existing.get("id"):
                workflow_id = existing["id"]
                state["workflow_id"] = workflow_id
                save_state(state)
                print(
                    f"✓ workflow \"{WORKFLOW['name']}\" found in your account ({workflow_id}) — updating steps"
                )
                await client.workflows.update(workflow_id, steps=WORKFLOW["steps"])
        except Exception:
            pass

        if not state.get("workflow_id"):
            created_workflow = await client.workflows.create(**WORKFLOW)
            workflow_id = getattr(
                created_workflow, "id", None
            ) or getattr(getattr(created_workflow, "workflow", None), "id", None)
            if not workflow_id:
                raise ValueError(
                    "Could not read created workflow id from response"
                )
            state["workflow_id"] = workflow_id
            save_state(state)
            print(f"+ created workflow ({workflow_id})")

    workflow_id = state["workflow_id"]
    try:
        await client.workflows.create_version(workflow_id)
    except Exception:
        pass

    print("\nDone. Run documents through it with:")
    print(
        f"  POST https://api.extend.ai/workflow_runs  {{ workflow: {{ id: \"{workflow_id}\" }}, file: {{ url: \"https://…\" }} }}"
    )
    print(
        "Or open the workflow in the Extend dashboard to review and deploy it."
    )


if __name__ == "__main__":
    import asyncio

    try:
        asyncio.run(main())
    except Exception as e:
        print(str(e) if hasattr(e, "__str__") else e, file=sys.stderr)
        sys.exit(1)
// This uses the Extend REST API directly because Extend has no official Java SDK yet.
// Call Extend's REST API at https://api.extend.ai with Bearer token authentication.

import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class Provision {
    private static final String API = "https://api.extend.ai";
    private static final String VERSION = "2026-02-09";
    private static final String API_KEY = System.getenv("EXTEND_API_KEY");
    
    private static final Path STATE_DIR = Paths.get(System.getProperty("user.dir"), ".extend");
    private static final Path STATE_FILE = STATE_DIR.resolve("explanation-of-benefits-eob-extractor.json");
    
    private static final HttpClient httpClient = HttpClient.newHttpClient();
    
    static class State {
        String workflowId;
        
        static State load() throws IOException {
            State state = new State();
            if (Files.exists(STATE_FILE)) {
                String json = Files.readString(STATE_FILE);
                state.workflowId = extractWorkflowId(json);
            }
            return state;
        }
        
        void save() throws IOException {
            Files.createDirectories(STATE_DIR);
            String json = "{\"workflowId\": " + 
                (workflowId != null ? "\"" + workflowId + "\"" : "null") + "}";
            Files.writeString(STATE_FILE, json);
        }
        
        private static String extractWorkflowId(String json) {
            int idx = json.indexOf("\"workflowId\":");
            if (idx < 0) return null;
            int start = json.indexOf("\"", idx + 14);
            if (start < 0) return null;
            int end = json.indexOf("\"", start + 1);
            if (end < 0) return null;
            return json.substring(start + 1, end);
        }
    }
    
    static class ApiResponse {
        String id;
        String workflow_id;
        List<Map<String, Object>> data;
        List<Map<String, Object>> items;
        
        static ApiResponse fromJson(String json) {
            ApiResponse resp = new ApiResponse();
            resp.id = extractField(json, "\"id\"");
            resp.workflow_id = extractField(json, "\"workflow_id\"");
            resp.data = parseArray(json, "\"data\"");
            resp.items = parseArray(json, "\"items\"");
            return resp;
        }
        
        private static String extractField(String json, String key) {
            int idx = json.indexOf(key);
            if (idx < 0) return null;
            int colonIdx = json.indexOf(":", idx);
            if (colonIdx < 0) return null;
            int start = json.indexOf("\"", colonIdx);
            if (start < 0) return null;
            int end = json.indexOf("\"", start + 1);
            if (end < 0) return null;
            return json.substring(start + 1, end);
        }
        
        private static List<Map<String, Object>> parseArray(String json, String key) {
            return null;
        }
    }
    
    static String api(String method, String pathName, String body) throws Exception {
        if (API_KEY == null || API_KEY.isEmpty()) {
            throw new IllegalStateException("Set EXTEND_API_KEY environment variable first.");
        }
        
        HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
            .uri(new URI(API + pathName))
            .method(method, body != null 
                ? HttpRequest.BodyPublishers.ofString(body) 
                : HttpRequest.BodyPublishers.noBody())
            .header("Authorization", "Bearer " + API_KEY)
            .header("x-extend-api-version", VERSION);
        
        if (body != null) {
            requestBuilder.header("Content-Type", "application/json");
        }
        
        HttpResponse<String> response = httpClient.send(
            requestBuilder.build(),
            HttpResponse.BodyHandlers.ofString()
        );
        
        if (response.statusCode() < 200 || response.statusCode() >= 300) {
            String msg = response.body();
            if (msg.length() > 300) msg = msg.substring(0, 300);
            throw new IOException(method + " " + pathName + " failed (" + 
                response.statusCode() + "): " + msg);
        }
        
        return response.body();
    }
    
    static String buildWorkflowJson() {
        StringBuilder sb = new StringBuilder();
        sb.append("{\n");
        sb.append("  \"name\": \"Explanation of Benefits Processing Pipeline\",\n");
        sb.append("  \"steps\": [\n");
        
        // startTrigger1
        sb.append("    {\n");
        sb.append("      \"name\": \"startTrigger1\",\n");
        sb.append("      \"type\": \"TRIGGER\",\n");
        sb.append("      \"next\": [{\"step\": \"parse1\"}]\n");
        sb.append("    },\n");
        
        // parse1
        sb.append("    {\n");
        sb.append("      \"name\": \"parse1\",\n");
        sb.append("      \"type\": \"PARSE\",\n");
        sb.append("      \"config\": {\n");
        sb.append("        \"parseConfig\": {\n");
        sb.append("          \"blockOptions\": {\n");
        sb.append("            \"text\": {\n");
        sb.append("              \"agentic\": {\"enabled\": true},\n");
        sb.append("              \"signatureDetectionEnabled\": true\n");
        sb.append("            },\n");
        sb.append("            \"tables\": {\n");
        sb.append("              \"agentic\": {\"enabled\": true},\n");
        sb.append("              \"tableHeaderContinuationEnabled\": true\n");
        sb.append("            },\n");
        sb.append("            \"figures\": {\"enabled\": true}\n");
        sb.append("          },\n");
        sb.append("          \"chunkingStrategy\": {\n");
        sb.append("            \"type\": \"page\",\n");
        sb.append("            \"options\": {}\n");
        sb.append("          }\n");
        sb.append("        }\n");
        sb.append("      },\n");
        sb.append("      \"next\": [{\"step\": \"extraction2\"}]\n");
        sb.append("    },\n");
        
        // extraction2 with full schema
        sb.append("    {\n");
        sb.append("      \"name\": \"extraction2\",\n");
        sb.append("      \"type\": \"EXTRACT\",\n");
        sb.append("      \"config\": {\n");
        sb.append("        \"extractorConfig\": {\n");
        sb.append("          \"baseProcessor\": \"extraction_performance\",\n");
        sb.append("          \"schema\": {\n");
        sb.append("            \"type\": \"object\",\n");
        sb.append("            \"additionalProperties\": false,\n");
        sb.append("            \"required\": [\"member_id\",\"plan_name\",\"eob_number\",\"group_number\",\"patient_name\",\"amount_billed\",\"provider_name\",\"amount_allowed\",\"date_processed\",\"subscriber_name\",\"claim_line_items\",\"benefit_period_end\",\"deductible_maximum\",\"benefit_period_start\",\"out_of_pocket_maximum\",\"deductible_met_to_date\",\"patient_responsibility\",\"discounts_and_reductions\",\"out_of_pocket_met_to_date\",\"health_plan_responsibility\"],\n");
        sb.append("            \"properties\": {\n");
        
        // String fields
        String[] stringFields = {"member_id", "plan_name", "eob_number", "group_number", "patient_name", "provider_name", "subscriber_name"};
        for (String field : stringFields) {
            sb.append("              \"").append(field).append("\": {\"type\": [\"string\", \"null\"]},\n");
        }
        
        // Currency fields
        String[] currencyFields = {"amount_billed", "amount_allowed", "deductible_maximum", "out_of_pocket_maximum", "deductible_met_to_date", "patient_responsibility", "discounts_and_reductions", "out_of_pocket_met_to_date", "health_plan_responsibility"};
        for (int i = 0; i < currencyFields.length; i++) {
            String field = currencyFields[i];
            sb.append("              \"").append(field).append("\": {\"type\": \"object\", \"additionalProperties\": false, \"required\": [\"amount\", \"iso_4217_currency_code\"], \"properties\": {\"amount\": {\"type\": [\"number\", \"null\"]}, \"iso_4217_currency_code\": {\"type\": [\"string\", \"null\"]}}}");
            if (i < currencyFields.length - 1) sb.append(",");
            sb.append("\n");
        }
        
        // Date fields
        sb.append(",\n              \"date_processed\": {\"type\": [\"string\", \"null\"], \"extend:type\": \"date\"},\n");
        sb.append("              \"benefit_period_end\": {\"type\": [\"string\", \"null\"], \"extend:type\": \"date\"},\n");
        sb.append("              \"benefit_period_start\": {\"type\": [\"string\", \"null\"], \"extend:type\": \"date\"},\n");
        
        // claim_line_items array
        sb.append("              \"claim_line_items\": {\n");
        sb.append("                \"type\": \"array\",\n");
        sb.append("                \"items\": {\n");
        sb.append("                  \"type\": \"object\",\n");
        sb.append("                  \"additionalProperties\": false,\n");
        sb.append("                  \"required\": [\"notes\",\"copay_amount\",\"amount_billed\",\"amount_allowed\",\"service_end_date\",\"deductible_amount\",\"amount_not_covered\",\"coinsurance_amount\",\"service_start_date\",\"service_description\",\"patient_responsibility\",\"discounts_and_reductions\",\"health_plan_responsibility\"],\n");
        sb.append("                  \"properties\": {\n");
        sb.append("                    \"notes\": {\"type\": [\"string\", \"null\"]},\n");
        sb.append("                    \"copay_amount\": {\"type\": [\"number\", \"null\"]},\n");
        sb.append("                    \"amount_billed\": {\"type\": [\"number\", \"null\"]},\n");
        sb.append("                    \"amount_allowed\": {\"type\": [\"number\", \"null\"]},\n");
        sb.append("                    \"deductible_amount\": {\"type\": [\"number\", \"null\"]},\n");
        sb.append("                    \"amount_not_covered\": {\"type\": [\"number\", \"null\"]},\n");
        sb.append("                    \"coinsurance_amount\": {\"type\": [\"number\", \"null\"]},\n");
        sb.append("                    \"service_description\": {\"type\": [\"string\", \"null\"]},\n");
        sb.append("                    \"patient_responsibility\": {\"type\": [\"number\", \"null\"]},\n");
        sb.append("                    \"discounts_and_reductions\": {\"type\": [\"number\", \"null\"]},\n");
        sb.append("                    \"health_plan_responsibility\": {\"type\": [\"number\", \"null\"]},\n");
        sb.append("                    \"service_start_date\": {\"type\": [\"string\", \"null\"], \"extend:type\": \"date\"},\n");
        sb.append("                    \"service_end_date\": {\"type\": [\"string\", \"null\"], \"extend:type\": \"date\"}\n");
        sb.append("                  }\n");
        sb.append("                }\n");
        sb.append("              }\n");
        
        sb.append("            }\n");
        sb.append("          }\n");
        sb.append("        }\n");
        sb.append("      }\n");
        sb.append("    }\n");
        
        sb.append("  ]\n");
        sb.append("}\n");
        
        return sb.toString();
    }
    
    static String buildStepsJson() {
        StringBuilder sb = new StringBuilder();
        sb.append("{\"steps\": [");
        sb.append("{\"name\":\"startTrigger1\",\"type\":\"TRIGGER\",\"next\":[{\"step\":\"parse1\"}]},");
        sb.append("{\"name\":\"parse1\",\"type\":\"PARSE\",\"config\":{\"parseConfig\":{\"blockOptions\":{\"text\":{\"agentic\":{\"enabled\":true},\"signatureDetectionEnabled\":true},\"tables\":{\"agentic\":{\"enabled\":true},\"tableHeaderContinuationEnabled\":true},\"figures\":{\"enabled\":true}},\"chunkingStrategy\":{\"type\":\"page\",\"options\":{}}}},\"next\":[{\"step\":\"extraction2\"}]},");
        sb.append("{\"name\":\"extraction2\",\"type\":\"EXTRACT\",\"config\":{\"extractorConfig\":{\"baseProcessor\":\"extraction_performance\",\"schema\":{\"type\":\"object\",\"additionalProperties\":false,\"required\":[\"member_id\",\"plan_name\",\"eob_number\",\"group_number\",\"patient_name\",\"amount_billed\",\"provider_name\",\"amount_allowed\",\"date_processed\",\"subscriber_name\",\"claim_line_items\",\"benefit_period_end\",\"deductible_maximum\",\"benefit_period_start\",\"out_of_pocket_maximum\",\"deductible_met_to_date\",\"patient_responsibility\",\"discounts_and_reductions\",\"out_of_pocket_met_to_date\",\"health_plan_responsibility\"],\"properties\":{\"member_id\":{\"type\":[\"string\",\"null\"]},\"plan_name\":{\"type\":[\"string\",\"null\"]},\"eob_number\":{\"type\":[\"string\",\"null\"]},\"group_number\":{\"type\":[\"string\",\"null\"]},\"patient_name\":{\"type\":[\"string\",\"null\"]},\"provider_name\":{\"type\":[\"string\",\"null\"]},\"subscriber_name\":{\"type\":[\"string\",\"null\"]},\"date_processed\":{\"type\":[\"string\",\"null\"],\"extend:type\":\"date\"},\"benefit_period_start\":{\"type\":[\"string\",\"null\"],\"extend:type\":\"date\"},\"benefit_period_end\":{\"type\":[\"string\",\"null\"],\"extend:type\":\"date\"},\"amount_billed\":{\"type\":\"object\",\"additionalProperties\":false,\"required\":[\"amount\",\"iso_4217_currency_code\"],\"properties\":{\"amount\":{\"type\":[\"number\",\"null\"]},\"iso_4217_currency_code\":{\"type\":[\"string\",\"null\"]}}},\"amount_allowed\":{\"type\":\"object\",\"additionalProperties\":false,\"required\":[\"amount\",\"iso_4217_currency_code\"],\"properties\":{\"amount\":{\"type\":[\"number\",\"null\"]},\"iso_4217_currency_code\":{\"type\":[\"string\",\"null\"]}}},\"deductible_maximum\":{\"type\":\"object\",\"additionalProperties\":false,\"required\":[\"amount\",\"iso_4217_currency_code\"],\"properties\":{\"amount\":{\"type\":[\"number\",\"null\"]},\"iso_4217_currency_code\":{\"type\":[\"string\",\"null\"]}}},\"out_of_pocket_maximum\":{\"type\":\"object\",\"additionalProperties\":false,\"required\":[\"amount\",\"iso_4217_currency_code\"],\"properties\":{\"amount\":{\"type\":[\"number\",\"null\"]},\"iso_4217_currency_code\":{\"type\":[\"string\",\"null\"]}}},\"deductible_met_to_date\":{\"type\":\"object\",\"additionalProperties\":false,\"required\":[\"amount\",\"iso_4217_currency_code\"],\"properties\":{\"amount\":{\"type\":[\"number\",\"null\"]},\"iso_4217_currency_code\":{\"type\":[\"string\",\"null\"]}}},\"patient_responsibility\":{\"type\":\"object\",\"additionalProperties\":false,\"required\":[\"amount\",\"iso_4217_currency_code\"],\"properties\":{\"amount\":{\"type\":[\"number\",\"null\"]},\"iso_4217_currency_code\":{\"type\":[\"string\",\"null\"]}}},\"discounts_and_reductions\":{\"type\":\"object\",\"additionalProperties\":false,\"required\":[\"amount\",\"iso_4217_currency_code\"],\"properties\":{\"amount\":{\"type\":[\"number\",\"null\"]},\"iso_4217_currency_code\":{\"type\":[\"string\",\"null\"]}}},\"out_of_pocket_met_to_date\":{\"type\":\"object\",\"additionalProperties\":false,\"required\":[\"amount\",\"iso_4217_currency_code\"],\"properties\":{\"amount\":{\"type\":[\"number\",\"null\"]},\"iso_4217_currency_code\":{\"type\":[\"string\",\"null\"]}}},\"health_plan_responsibility\":{\"type\":\"object\",\"additionalProperties\":false,\"required\":[\"amount\",\"iso_4217_currency_code\"],\"properties\":{\"amount\":{\"type\":[\"number\",\"null\"]},\"iso_4217_currency_code\":{\"type\":[\"string\",\"null\"]}}},\"claim_line_items\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"additionalProperties\":false,\"required\":[\"notes\",\"copay_amount\",\"amount_billed\",\"amount_allowed\",\"service_end_date\",\"deductible_amount\",\"amount_not_covered\",\"coinsurance_amount\",\"service_start_date\",\"service_description\",\"patient_responsibility\",\"discounts_and_reductions\",\"health_plan_responsibility\"],\"properties\":{\"notes\":{\"type\":[\"string\",\"null\"]},\"copay_amount\":{\"type\":[\"number\",\"null\"]},\"amount_billed\":{\"type\":[\"number\",\"null\"]},\"amount_allowed\":{\"type\":[\"number\",\"null\"]},\"deductible_amount\":{\"type\":[\"number\",\"null\"]},\"amount_not_covered\":{\"type\":[\"number\",\"null\"]},\"coinsurance_amount\":{\"type\":[\"number\",\"null\"]},\"service_description\":{\"type\":[\"string\",\"null\"]},\"patient_responsibility\":{\"type\":[\"number\",\"null\"]},\"discounts_and_reductions\":{\"type\":[\"number\",\"null\"]},\"health_plan_responsibility\":{\"type\":[\"number\",\"null\"]},\"service_start_date\":{\"type\":[\"string\",\"null\"],\"extend:type\":\"date\"},\"service_end_date\":{\"type\":[\"string\",\"null\"],\"extend:type\":\"date\"}}}}}}}}\n");
        sb.append("]}\n");
        return sb.toString();
    }
    
    public static void main(String[] args) {
        try {
            System.out.println("Deploying \"Explanation of Benefits Processing Pipeline\"…");
            
            State state = State.load();
            String workflowName = "Explanation of Benefits Processing Pipeline";
            
            if (state.workflowId != null && !state.workflowId.isEmpty()) {
                System.out.println("✓ workflow already provisioned (" + state.workflowId + ") — updating steps");
                api("POST", "/workflows/" + state.workflowId, buildStepsJson());
            } else {
                // Try to find existing workflow by name
                try {
                    String encoded = URLEncoder.encode(workflowName, StandardCharsets.UTF_8);
                    String listResponse = api("GET", "/workflows?name=" + encoded, null);
                    String existingId = extractExistingWorkflowId(listResponse, workflowName);
                    
                    if (existingId != null && !existingId.isEmpty()) {
                        state.workflowId = existingId;
                        state.save();
                        System.out.println("✓ workflow \"" + workflowName + "\" found in your account (" + existingId + ") — updating steps");
                        api("POST", "/workflows/" + existingId, buildStepsJson());
                    }
                } catch (Exception e) {
                    // Lookup is best-effort; fall through to create
                }
                
                if (state.workflowId == null || state.workflowId.isEmpty()) {
                    String createResponse = api("POST", "/workflows", buildWorkflowJson());
                    String wfId = ApiResponse.fromJson(createResponse).id;
                    if (wfId == null || wfId.isEmpty()) {
                        throw new IOException("Could not read created workflow id from response");
                    }
                    state.workflowId = wfId;
                    state.save();
                    System.out.println("+ created workflow (" + wfId + ")");
                }
            }
            
            // Deploy current draft as new version (best-effort)
            try {
                api("POST", "/workflows/" + state.workflowId + "/versions", "{}");
            } catch (Exception e) {
                // Best-effort
            }
            
            System.out.println("\nDone. Run documents through it with:");
            System.out.println("  POST " + API + "/workflow_runs  { workflow: { id: \"" + state.workflowId + "\" }, file: { url: \"https://…\" } }");
            System.out.println("Or open the workflow in the Extend dashboard to review and deploy it.");
            
        } catch (Exception e) {
            System.err.println(e.getMessage() != null ? e.getMessage() : e.toString());
            System.exit(1);
        }
    }
    
    static String extractExistingWorkflowId(String json, String name) {
        int idx = json.indexOf("\"name\"");
        while (idx >= 0) {
            int startQuote = json.indexOf("\"", idx + 7);
            if (startQuote < 0) break;
            int endQuote = json.indexOf("\"", startQuote + 1);
            if (endQuote < 0) break;
            String foundName = json.substring(startQuote + 1, endQuote);
            if (foundName.equals(name)) {
                int idIdx = json.lastIndexOf("\"id\"", idx);
                if (idIdx >= 0) {
                    int idStartQuote = json.indexOf("\"", idIdx + 5);
                    if (idStartQuote >= 0) {
                        int idEndQuote = json.indexOf("\"", idStartQuote + 1);
                        if (idEndQuote >= 0) {
                            return json.substring(idStartQuote + 1, idEndQuote);
                        }
                    }
                }
            }
            idx = json.indexOf("\"name\"", endQuote);
        }
        return null;
    }
}
// This code uses the Extend REST API directly because Extend does not publish
// an official Go SDK. It makes the same API calls that the TypeScript SDK wraps.
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
)

const (
	API     = "https://api.extend.ai"
	VERSION = "2026-02-09"
)

type State struct {
	WorkflowID *string `json:"workflowId,omitempty"`
}

type WorkflowListResponse struct {
	Data  []WorkflowItem `json:"data,omitempty"`
	Items []WorkflowItem `json:"items,omitempty"`
}

type WorkflowItem struct {
	ID   *string `json:"id,omitempty"`
	Name *string `json:"name,omitempty"`
}

type CreateWorkflowResponse struct {
	ID       *string `json:"id,omitempty"`
	Workflow *struct {
		ID *string `json:"id,omitempty"`
	} `json:"workflow,omitempty"`
}

var (
	apiKey  string
	stateDir string
	stateFile string
	state State
	client = &http.Client{}
)

func init() {
	apiKey = os.Getenv("EXTEND_API_KEY")
	if apiKey == "" {
		fmt.Fprintf(os.Stderr, "Set EXTEND_API_KEY first.\n")
		os.Exit(1)
	}

	cwd, err := os.Getwd()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to get working directory: %v\n", err)
		os.Exit(1)
	}

	stateDir = filepath.Join(cwd, ".extend")
	stateFile = filepath.Join(stateDir, "explanation-of-benefits-eob-extractor.json")

	if data, err := os.ReadFile(stateFile); err == nil {
		json.Unmarshal(data, &state)
	}
}

func saveState() error {
	if err := os.MkdirAll(stateDir, 0755); err != nil {
		return err
	}
	data, err := json.MarshalIndent(state, "", "  ")
	if err != nil {
		return err
	}
	return os.WriteFile(stateFile, data, 0644)
}

func apiCall(method, pathName string, body interface{}) (map[string]interface{}, error) {
	var reqBody io.Reader
	var contentType string

	if body != nil {
		bodyBytes, err := json.Marshal(body)
		if err != nil {
			return nil, fmt.Errorf("failed to marshal request body: %w", err)
		}
		reqBody = bytes.NewReader(bodyBytes)
		contentType = "application/json"
	}

	req, err := http.NewRequest(method, API+pathName, reqBody)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	req.Header.Set("Authorization", "Bearer "+apiKey)
	req.Header.Set("x-extend-api-version", VERSION)
	if contentType != "" {
		req.Header.Set("Content-Type", contentType)
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("request failed: %w", err)
	}
	defer resp.Body.Close()

	respBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %w", err)
	}

	var data map[string]interface{}
	json.Unmarshal(respBytes, &data)

	if resp.StatusCode >= 400 {
		respStr := string(respBytes)
		if len(respStr) > 300 {
			respStr = respStr[:300]
		}
		return nil, fmt.Errorf("%s %s failed (%d): %s", method, pathName, resp.StatusCode, respStr)
	}

	return data, nil
}

func main() {
	workflow := map[string]interface{}{
		"name": "Explanation of Benefits Processing Pipeline",
		"steps": []map[string]interface{}{
			{
				"name": "startTrigger1",
				"type": "TRIGGER",
				"next": []map[string]interface{}{
					{"step": "parse1"},
				},
			},
			{
				"name": "parse1",
				"type": "PARSE",
				"config": map[string]interface{}{
					"parseConfig": map[string]interface{}{
						"blockOptions": map[string]interface{}{
							"text": map[string]interface{}{
								"agentic": map[string]interface{}{
									"enabled": true,
								},
								"signatureDetectionEnabled": true,
							},
							"tables": map[string]interface{}{
								"agentic": map[string]interface{}{
									"enabled": true,
								},
								"tableHeaderContinuationEnabled": true,
							},
							"figures": map[string]interface{}{
								"enabled": true,
							},
						},
						"chunkingStrategy": map[string]interface{}{
							"type":    "page",
							"options": map[string]interface{}{},
						},
					},
				},
				"next": []map[string]interface{}{
					{"step": "extraction2"},
				},
			},
			{
				"name": "extraction2",
				"type": "EXTRACT",
				"config": map[string]interface{}{
					"extractorConfig": map[string]interface{}{
						"schema": extractionSchema(),
						"baseProcessor": "extraction_performance",
					},
				},
			},
		},
	}

	fmt.Printf("Deploying \"%s\"…\n", workflow["name"])

	if state.WorkflowID != nil {
		fmt.Printf("✓ workflow already provisioned (%s) — updating steps\n", *state.WorkflowID)
		if _, err := apiCall("POST", fmt.Sprintf("/workflows/%s", *state.WorkflowID), map[string]interface{}{"steps": workflow["steps"]}); err != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			os.Exit(1)
		}
	} else {
		// Try to find an existing workflow with the same name
		q := url.QueryEscape("Explanation of Benefits Processing Pipeline")
		list, err := apiCall("GET", fmt.Sprintf("/workflows?name=%s", q), nil)
		if err == nil {
			items := []WorkflowItem{}
			if data, ok := list["data"].([]interface{}); ok {
				for _, v := range data {
					if b, err := json.Marshal(v); err == nil {
						var item WorkflowItem
						if json.Unmarshal(b, &item) == nil {
							items = append(items, item)
						}
					}
				}
			} else if data, ok := list["items"].([]interface{}); ok {
				for _, v := range data {
					if b, err := json.Marshal(v); err == nil {
						var item WorkflowItem
						if json.Unmarshal(b, &item) == nil {
							items = append(items, item)
						}
					}
				}
			}

			for _, item := range items {
				if item.Name != nil && *item.Name == "Explanation of Benefits Processing Pipeline" && item.ID != nil {
					state.WorkflowID = item.ID
					saveState()
					fmt.Printf("✓ workflow \"Explanation of Benefits Processing Pipeline\" found in your account (%s) — updating steps\n", *item.ID)
					if _, err := apiCall("POST", fmt.Sprintf("/workflows/%s", *item.ID), map[string]interface{}{"steps": workflow["steps"]}); err != nil {
						fmt.Fprintf(os.Stderr, "Error: %v\n", err)
						os.Exit(1)
					}
					printDone(state.WorkflowID)
					return
				}
			}
		}

		if state.WorkflowID == nil {
			created, err := apiCall("POST", "/workflows", workflow)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
				os.Exit(1)
			}

			var wfID *string
			if id, ok := created["id"].(string); ok {
				wfID = &id
			} else if wf, ok := created["workflow"].(map[string]interface{}); ok {
				if id, ok := wf["id"].(string); ok {
					wfID = &id
				}
			}

			if wfID == nil {
				fmt.Fprintf(os.Stderr, "Error: Could not read created workflow id from response\n")
				os.Exit(1)
			}

			state.WorkflowID = wfID
			saveState()
			fmt.Printf("+ created workflow (%s)\n", *wfID)
		}
	}

	// Best-effort: deploy the current draft as a new version
	apiCall("POST", fmt.Sprintf("/workflows/%s/versions", *state.WorkflowID), map[string]interface{}{})

	printDone(state.WorkflowID)
}

func printDone(wfID *string) {
	fmt.Println("\nDone. Run documents through it with:")
	fmt.Printf("  POST %s/workflow_runs  { workflow: { id: \"%s\" }, file: { url: \"https://…\" } }\n", API, *wfID)
	fmt.Println("Or open the workflow in the Extend dashboard to review and deploy it.")
}

func extractionSchema() map[string]interface{} {
	return map[string]interface{}{
		"type": "object",
		"required": []string{
			"member_id", "plan_name", "eob_number", "group_number", "patient_name",
			"amount_billed", "provider_name", "amount_allowed", "date_processed",
			"subscriber_name", "claim_line_items", "benefit_period_end",
			"deductible_maximum", "benefit_period_start", "out_of_pocket_maximum",
			"deductible_met_to_date", "patient_responsibility", "discounts_and_reductions",
			"out_of_pocket_met_to_date", "health_plan_responsibility",
		},
		"properties": map[string]interface{}{
			"member_id": map[string]interface{}{
				"type":        []string{"string", "null"},
				"description": "The unique identification number assigned to the insured member or subscriber. This is used by the health plan to identify the covered individual. May be labeled as 'Member ID', 'Subscriber ID', or similar.",
			},
			"plan_name": map[string]interface{}{
				"type":        []string{"string", "null"},
				"description": "The name of the health insurance plan or product under which the claim was processed. May appear as 'Plan Name', 'Product', or similar.",
			},
			"eob_number": map[string]interface{}{
				"type":        []string{"string", "null"},
				"description": "The unique identifier for this Explanation of Benefits (EOB) document or claim. This is the primary reference number used to track this EOB. May include numbers, letters, or special characters. Common labels include 'Claim #', 'EOB #', or similar.",
			},
			"group_number": map[string]interface{}{
				"type":        []string{"string", "null"},
				"description": "The group or employer number associated with the health plan. This identifies the employer or group plan under which the member is covered. Commonly labeled as 'Group #', 'Group Number', etc.",
			},
			"patient_name": map[string]interface{}{
				"type":        []string{"string", "null"},
				"description": "The full name of the individual who received the medical services. May be labeled as 'Patient', 'Covered Person', or similar.",
			},
			"amount_billed": currencyType("The total amount billed by the provider for all services included in this claim. This is the original charge before any discounts, reductions, or insurance adjustments."),
			"provider_name": map[string]interface{}{
				"type":        []string{"string", "null"},
				"description": "The name of the healthcare provider, facility, or organization that rendered the services for this claim. May be labeled as 'Provider', 'Facility', or similar.",
			},
			"amount_allowed": currencyType("The total amount allowed by the health plan for the services provided, after discounts and reductions. This is the maximum amount considered for payment."),
			"date_processed": map[string]interface{}{
				"type":            []string{"string", "null"},
				"description":     "The date on which this claim or EOB was processed by the health plan. May be labeled as 'Date Processed', 'Processed On', or similar.",
				"extend:type":     "date",
			},
			"subscriber_name": map[string]interface{}{
				"type":        []string{"string", "null"},
				"description": "The name of the primary insured individual or policyholder. This is the person who holds the insurance policy, sometimes labeled as 'Subscriber', 'Participant', or 'Member'.",
			},
			"claim_line_items": map[string]interface{}{
				"type": "array",
				"items": map[string]interface{}{
					"type": "object",
					"required": []string{
						"notes", "copay_amount", "amount_billed", "amount_allowed",
						"service_end_date", "deductible_amount", "amount_not_covered",
						"coinsurance_amount", "service_start_date", "service_description",
						"patient_responsibility", "discounts_and_reductions", "health_plan_responsibility",
					},
					"properties": map[string]interface{}{
						"notes": map[string]interface{}{
							"type":        []string{"string", "null"},
							"description": "Any notes, remarks, or explanations related to this service line, such as denial reasons, bundling, or special instructions. May be referenced by footnotes or symbols.",
						},
						"copay_amount": map[string]interface{}{
							"type":        []interface{}{float64(0), nil},
							"description": "The copayment amount the patient is responsible for on this service line.",
						},
						"amount_billed": map[string]interface{}{
							"type":        []interface{}{float64(0), nil},
							"description": "The amount billed by the provider for this specific service line, before any discounts or reductions.",
						},
						"amount_allowed": map[string]interface{}{
							"type":        []interface{}{float64(0), nil},
							"description": "The amount allowed by the health plan for this service line after discounts and reductions.",
						},
						"service_end_date": map[string]interface{}{
							"type":            []string{"string", "null"},
							"description":     "The date on which the service or procedure ended, if applicable. May be omitted if service occurred on a single day.",
							"extend:type":     "date",
						},
						"deductible_amount": map[string]interface{}{
							"type":        []interface{}{float64(0), nil},
							"description": "The portion of this service line applied to the patient's deductible.",
						},
						"amount_not_covered": map[string]interface{}{
							"type":        []interface{}{float64(0), nil},
							"description": "The amount for this service line that is not covered by the health plan and is the patient's responsibility.",
						},
						"coinsurance_amount": map[string]interface{}{
							"type":        []interface{}{float64(0), nil},
							"description": "The coinsurance amount the patient is responsible for on this service line.",
						},
						"service_start_date": map[string]interface{}{
							"type":            []string{"string", "null"},
							"description":     "The date on which the service or procedure began or was rendered. May be labeled as 'Service Date', 'Date of Service', or similar.",
							"extend:type":     "date",
						},
						"service_description": map[string]interface{}{
							"type":        []string{"string", "null"},
							"description": "A description of the medical service, procedure, or supply provided. May include procedure names, codes, or other identifying information.",
						},
						"patient_responsibility": map[string]interface{}{
							"type":        []interface{}{float64(0), nil},
							"description": "The total amount the patient is responsible for on this service line, including deductible, copay, coinsurance, and non-covered amounts.",
						},
						"discounts_and_reductions": map[string]interface{}{
							"type":        []interface{}{float64(0), nil},
							"description": "The amount of discounts, reductions, or adjustments applied to this service line by the health plan.",
						},
						"health_plan_responsibility": map[string]interface{}{
							"type":        []interface{}{float64(0), nil},
							"description": "The amount paid or payable by the health plan for this service line.",
						},
					},
					"additionalProperties": false,
				},
				"description": "The individual service lines or procedures included in this claim. Each item represents a specific service, procedure, or charge, and includes details about dates, amounts, and benefit application. Format and labeling may vary widely across EOBs.",
			},
			"benefit_period_end": map[string]interface{}{
				"type":            []string{"string", "null"},
				"description":     "The end date of the benefit period relevant to this EOB or claim. This is the end of the coverage period for deductibles and out-of-pocket calculations.",
				"extend:type":     "date",
			},
			"deductible_maximum": currencyType("The total deductible maximum for the benefit period for the patient or family, as specified by the plan."),
			"benefit_period_start": map[string]interface{}{
				"type":            []string{"string", "null"},
				"description":     "The start date of the benefit period relevant to this EOB or claim. This is the beginning of the coverage period for deductibles and out-of-pocket calculations.",
				"extend:type":     "date",
			},
			"out_of_pocket_maximum": currencyType("The total out-of-pocket maximum for the benefit period for the patient or family, as specified by the plan."),
			"deductible_met_to_date": currencyType("The cumulative amount of deductible met by the patient for the current benefit period as of this EOB. This reflects progress toward the annual deductible."),
			"patient_responsibility": currencyType("The total amount the patient may owe to the provider for these services, after insurance payments and adjustments. This may include deductibles, copays, coinsurance, and non-covered amounts."),
			"discounts_and_reductions": currencyType("The total amount of discounts, reductions, or adjustments applied to the billed charges by the health plan. This may include network discounts, negotiated rates, or other reductions."),
			"out_of_pocket_met_to_date": currencyType("The cumulative amount of out-of-pocket expenses met by the patient for the current benefit period as of this EOB. This includes deductibles, copays, and coinsurance."),
			"health_plan_responsibility": currencyType("The total amount paid or payable by the health plan for this claim. This is the insurer's share of the allowed amount."),
		},
		"additionalProperties": false,
	}
}

func currencyType(description string) map[string]interface{} {
	return map[string]interface{}{
		"type": "object",
		"required": []string{"amount", "iso_4217_currency_code"},
		"properties": map[string]interface{}{
			"amount": map[string]interface{}{
				"type": []interface{}{float64(0), nil},
			},
			"iso_4217_currency_code": map[string]interface{}{
				"type": []string{"string", "null"},
			},
		},
		"description":         description,
		"extend:type":         "currency",
		"additionalProperties": false,
	}
}

Frequently Asked Questions (FAQ)

The most common issues are confusing similar financial fields and losing table context. Typical examples include mixing up billed vs. allowed amounts, patient vs. plan responsibility, line-item vs. claim-level totals, or deductible maximums vs. met-to-date values. Multi-page tables can also cause missed rows or lost headers. These are usually easily fixed with clearer field descriptions, EOB-specific extraction rules, item-level citations, and parser/table tuning when needed.
Good parsed output helps the extractor preserve service-line structure, column headers, and relationships between amounts. Extend's Extractor automatically parses the document, so only keep the parser if you also need the output .md.
Item-level citations are enabled because EOBs commonly contain service-line tables, and each row may include many important financial fields. Using item-level citations helps each extracted service line point back to its own source evidence, making it easier to validate billed amounts, allowed amounts, deductible amounts, copays, coinsurance, plan responsibility, and patient responsibility on a row-by-row basis.
Fine-tuning can be done by changing extractor and parser settings based on the desired tradeoff: - Use the light extractor processor to reduce latency/cost for simpler documents. - Disable bounding box citations if source highlighting is not needed. - Adjust array strategy for large claim-line tables. - Add extraction rules for EOB-specific ambiguity, such as distinguishing claim totals from line-item amounts. - Tune preprocessing/chunking if related claim sections are split across pages. - Adjust post-processing merge strategy if repeated or conflicting values appear across chunks. - Update parser configuration if tables, OCR, scanned pages, or multi-page layouts are not being captured well.
Tags
Insurance ClaimsPatient StatementClaim ProcessingBenefit Details
About this template

An Explanation of Benefits (EOB) shows how an insurance claim was processed, including charges, coverage applied, and what the patient may owe. This extractor captures claim, member, subscriber, patient, provider, financial total, benefit-period, deductible/out-of-pocket, and service-line details. It is optimized for higher extraction accuracy and reviewability. Fine-tuning can be done through extractor settings, citations, array strategy, chunking, merge strategy, extraction rules, and parser configuration.

Document formats
  • PDF
  • Images & Scans
  • Word / DOCX
  • Excel / CSV
Requirements
  • Complex layouts
  • Long tables
  • Scanned documents
  • Multi-language