Skip to content

Import an invoice into an obligation

Update an existing monthly obligation with a provider invoice, attach its line items, then make it ready for payment. Ledger creates obligations according to category recurrence; this recipe does not create a missing obligation.

First configure the environment. The example assumes the key-bound category has an August 2026 obligation in DRAFT or COLLECTING_DATA. Replace the sample period, invoice dates, amount and identifiers with validated provider data. The invoice currency must match the obligation currency.

import datetime
import os

from oblidog_client import OblidogClient, ObligationLifecycle, ObligationPeriod

with OblidogClient(
    base_url=os.environ["OBLIDOG_BASE_URL"],
    api_key=os.environ["OBLIDOG_API_KEY"],
) as client:
    with client.integrations.run() as run:
        period = ObligationPeriod(2026, 8)
        obligation = client.obligations.get(period)
        print(obligation.key)

        # Alternative discovery: filter obligations in this category.
        candidates = client.obligations.list(
            year=2026,
            month=8,
            lifecycle=ObligationLifecycle.COLLECTING_DATA,
        )
        print([item.key for item in candidates.data])

        if obligation.lifecycle not in (
            ObligationLifecycle.DRAFT,
            ObligationLifecycle.COLLECTING_DATA,
        ):
            raise ValueError("Review the obligation lifecycle before importing")

        client.obligations.update(
            period,
            current_amount="425.30",
            issue_date=datetime.date(2026, 8, 1),
            due_date=datetime.date(2026, 8, 20),
        )
        result = client.obligations.upsert_component(
            period,
            type="principal",
            label="August electricity",
            external_id="invoice-FV-123-2026-line-1",
            amount="425.30",
            metadata={"invoice_number": "FV/123/2026"},
        )
        print(result.result)
        components = client.obligations.list_components(period)
        print([item.label for item in components.data])
        client.obligations.append_note(period, "Imported invoice FV/123/2026")
        client.obligations.mark_ready(period)
        run.finish_success(changes_detected=True)

The obligation key (such as ENRG-2026-08) is useful for display and diagnostics. The supported lookup accepts ObligationPeriod, not the full key: use the year and month from the provider, with the category supplied by the API key. A 404 means no visible obligation exists for that period; check the category and recurrence instead of inventing a different key.

Supply money as decimal strings to avoid binary floating-point rounding. Omitting an update argument preserves it; passing None explicitly clears it. Components do not replace the obligation's amount: set the intended total with update. Keep a stable component external_id across repeated imports so the same line is updated rather than duplicated. The upsert's result is created, updated, or unchanged.

This sequence is not a transaction. Earlier writes can remain if a later call fails. Notes are appended on every call, and the entire sequence is not safe to replay blindly. After a partial failure, read the obligation and components, reconcile against the provider, and resume deliberately. In particular, avoid reopening a paid or canceled obligation just to force an import through.

mark_ready belongs last, after successful validation and all intended writes. For a known no-op report changes_detected=False; use None when you cannot reliably determine whether data changed. See lifecycle and failures for recovery guidance and the obligations reference for method signatures and return models.