AI video API result reconciliation: Resolve Conflicting Evidence
Your application stores a completed video, then receives an older running observation. A separate worker records a failure, while your accounting import shows a charge. AI video API result reconciliation turns these conflicting records into an explainable local decision without launching another generation. The question is not which message arrived last: it is what the available evidence supports about delivery and money.
Reconciliation is the process of comparing independently collected evidence and recording a decision with its supporting references. For a GenRelay integration, keep this application-owned process separate from integration planning on the GenRelay video API page. The examples below describe local records and policy, not GenRelay request fields, response schemas, event guarantees, or billing contracts.
What should result reconciliation decide?
Result reconciliation should decide whether delivery is supported, whether accounting evidence matches, and whether contradictory records require human review.
Represent three independent dimensions rather than forcing everything into one success flag:
- Generation evidence: observations about whether the remote job finished or failed.
- Delivery evidence: proof that your application retained and validated the intended output.
- Ledger evidence: accounting records associated with the same operation and charge scope.
A ledger is an accounting record of financial entries. A charge does not demonstrate that the user received a usable video. A readable video does not establish its final cost. Keeping these dimensions separate prevents an accounting delay from silently changing a delivered result into a failed generation.
This differs from video job recovery, which addresses interrupted tracking and uncertain execution. Here, the operation already has collected evidence. The reconciler compares that evidence, records disagreements, and leaves submission untouched. It neither chooses polling intervals nor retries generation.
Why can valid observations arrive in a confusing order?
Independent collectors can observe different moments, and transport arrival order does not establish the order of remote state transitions.
A worker might save an earlier response after another worker saves a later one. A delivery checker might finish before an accounting import. A duplicated notification can arrive after both. Timestamps from different machines can also disagree. Do not treat a later local ingestion time as proof of a newer provider state.
An observation is an immutable record of something a collector saw. Record its source, scoped job identity, ingestion time, normalized meaning, and a protected reference to the original evidence. Namespace identities by tenant, provider account, and remote job identifier so unrelated jobs cannot be merged accidentally. Protect raw payloads and redact credentials or signed download URLs in logs.
A terminal observation reports a completed or failed outcome. Preserve it even when another observation disagrees. An old running observation must not erase completion, and a failure must not overwrite success simply because it arrived later. Without a documented revision or precedence contract, contradictory terminal observations require manual review rather than a guessed winner.
How can Python reconcile an unordered evidence set?
Use a deterministic reducer that evaluates the entire evidence set and produces separate delivery and accounting decisions.
A reducer is a function that turns a collection of records into a summary. Save this standard-library example as reconcile.py and run python reconcile.py. Every key and status is invented for this local demonstration. The input adapter, not this function, must authenticate sources and bind each record to the correct job.
The example treats financial input as comparable, normalized totals in one currency and billing scope. Real itemized charges, credits, taxes, and revisions need an accounting adapter before this comparison. It compares actual evidence, not an estimated reservation.
import json
from itertools import permutations
def reconcile(records):
unique = {}
for item in records:
previous = unique.get(item["id"])
if previous is not None and previous != item:
raise ValueError("observation identity reused with changed content")
unique[item["id"]] = dict(item)
rows = list(unique.values())
terminal = {
row["value"] for row in rows
if row["kind"] == "generation"
and row["value"] in {"succeeded", "failed"}
}
artifacts = {
row["value"] for row in rows if row["kind"] == "verified_artifact"
}
billed = {
row["value"] for row in rows if row["kind"] == "provider_total"
}
booked = {
row["value"] for row in rows if row["kind"] == "local_total"
}
conflict = (
len(terminal) > 1 or len(artifacts) > 1
or ("failed" in terminal and bool(artifacts))
)
if conflict:
delivery = "manual_review"
elif terminal == {"succeeded"} and artifacts:
delivery = "ready"
elif terminal == {"failed"}:
delivery = "reported_failed"
else:
delivery = "awaiting_evidence"
if len(billed) > 1 or len(booked) > 1:
accounting = "manual_review"
elif not billed or not booked:
accounting = "awaiting_evidence"
elif billed == booked:
accounting = "matched"
else:
accounting = "mismatch"
return {
"delivery": delivery,
"accounting": accounting,
"evidence_ids": sorted(unique),
}
SAMPLE = [
{"id": "g1", "kind": "generation", "value": "succeeded"},
{"id": "g0", "kind": "generation", "value": "running"},
{"id": "a1", "kind": "verified_artifact", "value": "local-object-A"},
{"id": "p1", "kind": "provider_total", "value": 12000},
{"id": "l1", "kind": "local_total", "value": 12000},
]
if __name__ == "__main__":
expected = reconcile(SAMPLE)
assert expected["delivery"] == "ready"
assert expected["accounting"] == "matched"
for ordering in permutations(SAMPLE):
assert reconcile(ordering) == expected
assert reconcile(SAMPLE + [SAMPLE[0]]) == expected
failure = {"id": "g2", "kind": "generation", "value": "failed"}
assert reconcile(SAMPLE + [failure])["delivery"] == "manual_review"
assert reconcile(SAMPLE[:3])["accounting"] == "awaiting_evidence"
print("reducer assertions passed: 120 arrival orders")
Here, verified_artifact means a trusted local checker has confirmed the expected object exists in controlled storage and passed your integrity and media-validation checks. It is not a download URL from a response. The single-artifact assumption is explicit: applications expecting multiple outputs need an expected manifest and per-output validation instead of treating multiple objects as a conflict.
How do you make the decision durable across restarts?
Commit observations and the derived decision in one database transaction, and preserve earlier decisions as an audit history.
Durable means that committed records survive ordinary process restarts, subject to the storage system's guarantees. Idempotency means repeating the same operation does not introduce another effect. The next example uses SQLite to make identical evidence ingestion idempotent and rejects changed content under an existing observation identity.
Save it as durable.py alongside reconcile.py, then run python durable.py. The temporary database keeps this demonstration self-contained. Use a persistent application-owned path, backups, access controls, and appropriate database operational settings in a deployed system.
import json
import sqlite3
from pathlib import Path
from tempfile import TemporaryDirectory
from reconcile import SAMPLE, reconcile
def connect(path):
db = sqlite3.connect(path, isolation_level=None)
db.executescript("""
CREATE TABLE IF NOT EXISTS observations (
job TEXT NOT NULL,
eid TEXT NOT NULL,
payload TEXT NOT NULL,
PRIMARY KEY (job, eid)
);
CREATE TABLE IF NOT EXISTS decisions (
job TEXT NOT NULL,
version INTEGER NOT NULL,
payload TEXT NOT NULL,
PRIMARY KEY (job, version)
);
""")
return db
def ingest(db, job, batch):
db.execute("BEGIN IMMEDIATE")
try:
for item in batch:
payload = json.dumps(item, sort_keys=True)
old = db.execute(
"SELECT payload FROM observations WHERE job=? AND eid=?",
(job, item["id"]),
).fetchone()
if old is not None and old[0] != payload:
raise ValueError("immutable observation conflict")
if old is None:
db.execute(
"INSERT INTO observations VALUES (?, ?, ?)",
(job, item["id"], payload),
)
records = [json.loads(row[0]) for row in db.execute(
"SELECT payload FROM observations WHERE job=?", (job,)
)]
decision = json.dumps(reconcile(records), sort_keys=True)
last = db.execute(
"SELECT version, payload FROM decisions "
"WHERE job=? ORDER BY version DESC LIMIT 1", (job,)
).fetchone()
if last is None or last[1] != decision:
version = 1 if last is None else last[0] + 1
db.execute("INSERT INTO decisions VALUES (?, ?, ?)",
(job, version, decision))
db.execute("COMMIT")
return json.loads(decision)
except Exception:
db.execute("ROLLBACK")
raise
if __name__ == "__main__":
with TemporaryDirectory() as directory:
path = Path(directory) / "evidence.sqlite"
db = connect(path)
job = "tenant-A/account-A/job-A"
assert ingest(db, job, SAMPLE)["delivery"] == "ready"
db.close()
db = connect(path)
ingest(db, job, list(reversed(SAMPLE)))
assert db.execute("SELECT count(*) FROM decisions").fetchone()[0] == 1
try:
ingest(db, job, [dict(SAMPLE[0], value="failed")])
except ValueError:
pass
else:
raise AssertionError("mutation was accepted")
assert db.execute("SELECT count(*) FROM observations").fetchone()[0] == 5
failure = {"id": "g2", "kind": "generation", "value": "failed"}
assert ingest(db, job, [failure])["delivery"] == "manual_review"
assert db.execute("SELECT count(*) FROM decisions").fetchone()[0] == 2
db.close()
print("durability, deduplication, rollback, and conflict assertions passed")
The database stores decisions, not external delivery or financial effects. A production extension can add an outbox, a table of pending actions committed with each decision. Workers then process those actions with stable action identifiers and destination-side deduplication where supported. A database commit alone cannot make a network action happen exactly once.
What should happen when evidence remains contradictory?
Freeze automatic irreversible actions and create a review case containing the conflicting evidence and prior decisions.
Do not delete an earlier success to make the dashboard look consistent. If a video was already delivered before a later conflict, retain the delivery receipt and escalate; the new review state does not undo that historical action. Likewise, do not automatically refund a charge because one collector reported failure.
A review case should identify the tenant, remote job, retained artifact, accounting references, conflicting observation identities, and policy version. An operator can append a resolution record with their identity, rationale, and supporting evidence. That resolution changes the local business decision while preserving both terminal observations. The example intentionally stops at review; it contains no mechanism for inventing provider precedence.
Keep financial adjustments separate from generation decisions. Budget reservations describe provisional holds, not proof of final charges. Missing billing evidence should remain pending; a discrepancy should open an accounting task. Compare like currencies and scopes, and record credits as explicit entries rather than silently overwriting earlier totals.
Which operational checks make reconciliation actionable?
Track unresolved evidence age, contradiction counts, and delivery-versus-accounting discrepancies with a named owner for each queue.
Set local review deadlines according to customer commitments, not an assumed provider timing guarantee. When a deadline expires, escalate the case without converting missing evidence into failure. Version the decision policy so that an audit can distinguish new evidence from changed business rules.
Test duplicate records, reversed arrivals, terminal disagreement, missing artifacts, mismatched totals, restart recovery, and transaction rollback. Also test tenant isolation and evidence identity collisions in your input adapter. Neither example validates an actual API payload or implements a production authentication boundary.
What do developers ask about AI video API result reconciliation?
Developers commonly ask whether completion, delivery, accounting, and retries can share one status; they should remain distinct.
Does a successful generation observation prove delivery?
No. Delivery requires your own evidence that the expected output was retained and validated. A remote completion record can coexist with a missing or inaccessible local artifact.
Should the latest timestamp resolve a terminal conflict?
No, not without a documented ordering and correction contract. Preserve conflicting terminal observations and request review instead of assuming that clock order establishes authority.
Does a ledger mismatch mean the video should be generated again?
No. Investigate the financial discrepancy against the existing operation. Another generation adds a separate execution and potential charge without explaining the original mismatch.
Can a reconciliation decision change after it was committed?
Yes. Append a new version supported by new evidence or an authorized resolution. Keep earlier decisions and completed action receipts so the history remains explainable.