Invoice Agent

An invoice says Net 30 from March 15. The deadline is anchored to the invoice date, not to when your agent processes it. Set reference_date to the document date and Biruni gives you the correct due date.

Invoice line
Payment due: Net 30 from March 15, 2026.
parse_invoice()Your LLM / OCR resolve_time()Biruni API schedule_payment()Your AP system notify()Your alerts

The agent

invoice_agent.py
import httpx, os

BIRUNI_URL = "https://biruni.dev/v1/resolve"


# ── Stubs: replace with your integrations ────────────────────

async def schedule_payment(invoice_id: str, due_date: str):
    """Queue a payment or set a reminder. (ERP, QuickBooks, Xero ...)"""
    ...

async def notify(text: str):
    """Alert the AP team. (Slack, email, PagerDuty ...)"""
    ...

def parse_invoice(document: str) -> dict:
    """
    Use your LLM or OCR pipeline to extract structured fields.
    Returns: {
        "invoice_id": str, "terms_phrase": str,
        "invoice_date": str, "tz": str,
    }
    """
    ...


# ── Biruni integration ──────────────────────────────────────

async def resolve_time(phrase: str, tz: str, ref: str) -> dict:
    async with httpx.AsyncClient() as client:
        r = await client.post(
            BIRUNI_URL,
            headers={"Authorization": f"Bearer {os.environ['BIRUNI_API_KEY']}"},
            json={
                "expression": phrase,
                "reference_date": ref,
                "timezone": tz,
            },
        )
        r.raise_for_status()
        return r.json()


# ── Agent entry point ────────────────────────────────────────

async def handle(document: str):
    fields = parse_invoice(document)

    result = await resolve_time(
        fields["terms_phrase"],
        fields["tz"],
        ref=fields["invoice_date"],  # anchor to the invoice date, not today
    )

    match result["status"]:
        case "resolved":
            await schedule_payment(fields["invoice_id"], result["resolved"])
            await notify(
                f"Invoice {fields['invoice_id']} due {result['resolved'][:10]}."
            )

        case "ambiguous":
            await notify(
                f"Invoice {fields['invoice_id']} has ambiguous terms — "
                f"needs manual review."
            )

        case _:
            await notify(
                f"Couldn't parse terms for invoice {fields['invoice_id']}."
            )