Handle lifecycle decisions and failures
An integration must distinguish invalid obligation data from an unsuccessful
synchronization. ERROR is an alarm that the integration recognized invalid or
inconsistent data in Oblidog. A provider outage, login failure or network timeout
alone is a failed integration run; it is not a reason to mark an obligation
ERROR.
Ledger's authoritative obligation lifecycle defines allowed transitions and field effects. Consult it instead of assuming all actions work from every state.
| SDK operation | Business intent |
|---|---|
mark_ready(period) |
Confirm complete, validated payment data and make the obligation ready for payment. |
mark_paid(period) |
Record a payment confirmed by the provider or another trusted source; importing an invoice alone is insufficient. |
cancel(period) |
Cancel an obligation that is being collected and should no longer be paid. |
reopen(period) |
Deliberately return a ready, paid, canceled or erroneous obligation to data collection so it can be corrected; clears paid_at. |
mark_error(period) |
Raise an alarm about invalid obligation data while preserving values, notes, components and payment timestamp. |
Updating values does not make an obligation ready automatically. Correct an
ERROR obligation by deliberately reopening it, correcting its data, and then
marking it ready. Do not automate reopening merely because an update was
rejected: that can undo a meaningful paid or canceled decision.
Report recognized inconsistent data
Suppose your provider reconciliation has established that the total stored in Oblidog is inconsistent. This reusable function appends a sanitized diagnostic, raises the obligation alarm, and reports a failed run. Call it only after that business validation has actually failed; it does not diagnose a mismatch itself.
import os
from oblidog_client import OblidogClient, ObligationPeriod
def report_inconsistent_obligation(period: ObligationPeriod) -> None:
with OblidogClient(
base_url=os.environ["OBLIDOG_BASE_URL"],
api_key=os.environ["OBLIDOG_API_KEY"],
) as client:
with client.integrations.run() as run:
client.obligations.append_note(
period, "Reconciliation found an inconsistent invoice total"
)
client.obligations.mark_error(period)
run.finish_failure(
code="invalid_obligation_data",
message="Invoice total needs review",
)
Reporting run failure does not itself change the obligation lifecycle. Likewise,
marking an obligation ERROR does not finish the run. If the original
synchronization already has an active run, perform these calls inside that run
instead of starting a nested one.
Catch SDK failures at the synchronization boundary
This complete entry point updates a known editable obligation. Configure the environment and replace the sample invoice values as described in the import guide. Exceptions leave the run block before being caught, allowing its context manager to report failure. Nonzero exit status lets your scheduler detect failure.
import datetime
import logging
import os
from oblidog_client import (
OblidogApiError,
OblidogClient,
OblidogConflictError,
OblidogConnectionError,
OblidogError,
OblidogValidationError,
ObligationPeriod,
)
logger = logging.getLogger(__name__)
def main() -> int:
try:
with OblidogClient(
base_url=os.environ["OBLIDOG_BASE_URL"],
api_key=os.environ["OBLIDOG_API_KEY"],
) as client:
with client.integrations.run() as run:
client.obligations.update(
ObligationPeriod(2026, 8),
current_amount="425.30",
due_date=datetime.date(2026, 8, 20),
)
run.finish_success(changes_detected=None)
except OblidogValidationError as error:
logger.error("Request rejected (HTTP %s); review payload", error.status_code)
return 1
except OblidogConflictError as error:
logger.error("Integration state conflict: %s", error.code.value)
return 1
except OblidogConnectionError:
logger.error("API unavailable; reconcile writes before another attempt")
return 1
except OblidogApiError as error:
logger.error("Unexpected API response (HTTP %s)", error.status_code)
return 1
except OblidogError:
logger.error("Synchronization failed with an SDK error")
return 1
return 0
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
raise SystemExit(main())
Catch OblidogConflictError before OblidogApiError because it is a subclass.
Do not refresh the integration revision and blindly replay the invocation after
a conflict; check for another worker or a changed integration configuration.
A 422 rejects the request payload; it does not prove that stored obligation data
is inconsistent. Treat local ValueError/TypeError (such as an invalid
ObligationPeriod) as input or programming errors and correct them at the source.
OblidogConnectionError covers transport failures and exhausted retryable server
responses. Only reads are retried automatically. A timeout during a write can
mean the server applied it without returning a response. Read the current state
before deciding how to resume, especially after appending notes or creating
records without an external ID. The run wrapper attempts failure reporting on
an exception, but a disconnected API can prevent that report too.
API and validation exceptions retain status_code and raw response content.
Keep raw bodies, credentials, provider responses and exception tracebacks out of
user-facing notes and run failure messages. The handler logs only status codes
and typed conflict codes. See the exceptions reference,
client/run reference, and
obligations reference for the supported contracts.