Build in Public · LF-14

API Failures, Refunds, and Reconciliation: How to Audit Net Charges for a Request

Separate tasks, call attempts, delivery states, and wallet events to handle pre-charge rejections, empty results, and partial refunds without double-counting, using integer-based offline examples.

For "failures are not charged" to serve as an enforceable product commitment, it must clarify two things: what constitutes a failure, and how to prove no net charge occurred. Relying solely on HTTP status codes misses empty results and partial deliveries, while checking only balance changes mixes in concurrent requests, deposits, or adjustments. Reliable reconciliation requires tracing an individual request alongside its associated events.

This note discusses how API clients organize reconciliation records and explains ambiguous fields using the current EveryInfra codebase. The offline ledger shown here is an application design example, not a customer billing statement or an accounting revenue recognition rule. The specific action billing conditions, currency units, and final statements must be verified against the corresponding product contract.

  • Business task: the goal the user actually wants to achieve, such as inspecting a set of approved product reviews. A single task may involve multiple API calls.
  • Call attempt: an actual request, storing its own timestamp, parameter version, model or action, and available request ID.
  • Delivery result: success, empty result, partial delivery, failure, or still unknown; this is a business state, not a wallet state.
  • Wallet event: charges, refunds, or other explicit adjustments related to the attempt, used to explain net changes.

Link these four objects together instead of forcing them into a single success field. If a request completes part of its goal, the business can retain the delivered content while continuing to audit billing for the unfinished portion; a timeout may also leave delivery and settlement undetermined, so it must not be casually recorded as zero cost.

The business task ID, request ID, asynchronous job_id, and billing reference are also distinct concepts. Store the relationships actually provided by the service; do not synthesize non-existent billing IDs from similar strings. HTTP request IDs generated when querying tasks must never overwrite the association identifier of the initial task submission.

Pre-Charge Rejection vs. Post-Charge Refund Follow Different Paths

Referring to the deployed source baseline in 2026-09-04, checks for EveryInfra text request identity, inputs, model, and capability permissions all occur before billing; missing messages may return input errors prior to authentication, so a uniform precedence order across all errors is not declared here. Along paths where messages are valid and identity checks pass, an unknown model produces 422 unknown_model before charges occur. Such failures represent execution rejections and do not generate an initial charge followed by a refund; the legacy unknown model 503 narrative should no longer be used.

For requests that enter the execution phase and subsequently fail, partial charges may need to be refunded. The existence of a refund branch in the implementation does not mean the client has observed its completion. If a response is lost, query available records using request identifiers first; in the absence of evidence, retain "settlement pending audit" rather than modifying financial records based solely on error text.

The 2026-09-02 archives contain desensitized samples of post-failure refunds, as well as empty results and batch partial delivery samples. They illustrate that these scenarios deserve separate acceptance, rather than acting as a universal guarantee for every capability or failure type today. This round covers source code and public documentation only, without introducing real charges, billing refunds, or account operations.

Read Field Definitions Before Deciding How to Add Them

Taking synchronous data responses as an example, billing.charged indicates the billing status in the response, billing.credits represents the billing unit count reported by the response, and billing.amount is used for displays requiring monetary semantics. Quota remaining values represent account snapshots and cannot be treated as single-request costs; catalog list prices cannot replace actual request records.

Partial refunds are the most common source of calculation errors. The current data implementation subtracts the calculated refund amount from the original fee before writing the remaining fee to billing.credits, while separately returning refunded_credits. Consequently, you must not subtract refunded_credits from billing.credits again, or the same refund will be subtracted twice. This conclusion is limited to the synchronous data branch verified here and does not automatically apply to all products.

In a pure synthetic example, an original charge of 30 units with a 10-unit refund results in a net charge of 20. If the final response already reports credits as 20 and refunded_credits as 10, then 20 is already the net amount expressed by that response. To audit the raw events, use 30 minus 10 instead; do not subtract 10 from 20 to arrive at 10.

Each polling attempt for the same request may carry duplicate status; do not sum the credits across every response. Store response snapshots, billing events, and settlement adjustments separately in your database, deduplicate them by their respective stable identities first, and then aggregate. Seeing a new HTTP log entry does not mean a new consumption occurred.

Interpret Empty Results and Partial Deliveries by Action

When search results are empty, verify whether valid deliveries for that tool might still appear in other fields. The current general search implementation checks both results and answer simultaneously; staring only at empty results can cause responses with answers to be misjudged as entirely empty. Clients should follow the tool contract and avoid guessing content value at the financial layer.

Batch data requests require auditing target counts against actual deliveries. The current partial refund branch provides total_targets, billed_targets, and refunded_credits, while business results may also include partial and missed. These fields help explain gaps, but specific values must come from actual responses; comment counts cannot be treated as successful target counts.

Before reprocessing unfinished targets, retain delivered objects and original requests, clarifying which targets remain unknown. Resending an entire batch may duplicate existing content or generate new call attempts. Client-side result deduplication and server-side duplicate charge prevention are two distinct operations.

Asynchronous Acceptance Does Not Equal Final Settlement

HTTP 202 and job_id indicate entry into the task processing pipeline, not that the user has obtained results. Retain the original task and query its final status; a successful task query merely indicates the query itself completed, and you must check whether the task is still running or awaiting finalization. Disconnections cannot automatically be interpreted as successful cancellations.

Current data task queries primarily return task status, results, or failure information rather than providing a complete billing snapshot every time. When billing fields are missing, verify against authorized billing portals; do not fill in zeros, and do not treat creation and completion snapshots as separate new charges. This note does not claim that all asynchronous capabilities use the same settlement timing.

Audit Single Costs Using Integers, Not Floating-Point Derived Decimals

Reconciliation requires fixing wallets, measurement units, and request scopes first. Do not directly sum amounts across multiple wallets, different currencies, or display exchange rates. If interfaces use integer credits, prioritize auditing in the native unit; verify safe integers at boundaries, and use explicit integer representations when values exceed JavaScript's precise integer range.

The following demonstrates a custom event structure: each entry belongs to the same confirmed request scope, credits use positive integer strings, and kind specifies charges or refunds. It does not read real billing records, connect to databases, or alter balances. A complete set of events and billing authorization are prerequisites that callers must confirm beforehand.

Offline JavaScript: distinguish charge total, refund total, and net charge
function reconcileAttempt(entries) {
  const seen = new Map();
  let debited = 0n, refunded = 0n;
  for (const e of entries) {
    if (typeof e.id !== "string" || !e.id.trim()
        || !["debit", "refund"].includes(e.kind)
        || typeof e.credits !== "string" || !/^[1-9][0-9]*$/.test(e.credits)) {
      throw new Error("invalid synthetic ledger entry");
    }
    const fingerprint = JSON.stringify([e.kind, e.credits]);
    if (seen.has(e.id)) {
      if (seen.get(e.id) !== fingerprint) throw new Error("conflicting entry");
      continue; // duplicate snapshots of the same event are not counted again.
    }
    seen.set(e.id, fingerprint);
    if (e.kind === "debit") debited += BigInt(e.credits);
    else refunded += BigInt(e.credits);
  }
  if (refunded > debited) throw new Error("check scope or missing debit evidence");
  return {
    debited: debited.toString(), refunded: refunded.toString(),
    netCharged: (debited - refunded).toString()
  };
}

console.log(reconcileAttempt([
  {id: "synthetic-debit", kind: "debit", credits: "30"},
  {id: "synthetic-refund", kind: "refund", credits: "10"},
  {id: "synthetic-refund", kind: "refund", credits: "10"}
]));

The result is a charge of 30, a refund of 10, and a net charge of 20; repeatedly occurring duplicate refund events are not counted again. If the same event ID maps to conflicting content, the example halts rather than silently overwriting. Auditing also stops if refunds exceed acquired charges, which may stem from incomplete scopes or time windows, and should not be automatically assumed to mean the service over-refunded.

This is not a complete wallet algorithm. Deposits, grants, withdrawals, freezes, reversals, and other adjustments are not modeled in this example; real systems must handle them according to their respective event definitions. Zeros calculated from empty lists only indicate no input events were present, and do not prove an unknown request incurred no charges.

JavaScript Numbers are not arbitrary-precision integers. RFC 8259 instructions on JSON number interoperability ranges provide the context for keeping integer strings here before explicitly converting them to BigInt; this only reduces computational representation errors and cannot make up for missing billing events.

Retain Opening and Cross-Period Events When Auditing by Time Window

Requests may start on one day and finish on the next; refund or reversal events may also post later than the original charge. Filtering by request creation time versus wallet event occurrence time yields different sets. Reports should specify which time basis is used, then retain opening, closing, and cross-period associations to avoid false anomaly alarms caused by seeing refunds without the previous day's charges.

Balance identities require accounting for all debits and credits within that wallet window, rather than just the two event types discussed here. The difference between the current balance and the balance prior to a given call may also include concurrent tasks or other adjustments; it is suitable for cross-checking, not for independently proving the cost of a single request.

Wallet consumption, customer cash deposits, promotional credit usage, and accounting revenue follow different definitions. This note only discusses matching requests to wallet evidence and provides no revenue recognition or tax conclusions. External financial metrics should be published only after responsible product and finance personnel clarify their definitions.

Stripe's idempotent request documentation provides a point of comparison: secure retries require explicit service support for the corresponding idempotency mechanism. It does not prove that EveryInfra accepts the same idempotency key. The examples herein deduplicate acquired records by event ID rather than blocking duplicate charges server-side; these two types of deduplication must not be conflated.

Route Discrepancies to Review Queues Without Automatically Modifying Ledgers

  • Charge records exist with no linkable delivery: verify whether asynchronous processing is unfinished, results are unsaved, or associations were lost.
  • Failure results with unclear billing status: audit original requests and subsequent events without self-marking them as refunded or reversed.
  • Conflicting content for the same event: store original versions and retrieval timestamps, halting silent overwrites.
  • Refund events displayed repeatedly: determine whether the same event was read multiple times; do not count multiple times.
  • Inconsistent currency units or windows: align comparison scopes before evaluating differences.

When submitting support requests, provide only necessary request IDs, timestamps, capabilities, statuses, and desensitized billing references. Do not send complete API keys, customer body text, or entire account exports. Discrepancy triage and ledger modifications require separate permissions; an auto-generated anomaly flag does not constitute authorization for reversals or other accounting adjustments.

An auditable request should articulate what it delivered, what cost basis applied, whether refunds were factored into the net amount, and which events remain unconfirmed. Clarifying these relationships ensures that "failures do not incur final charges" is more than a slogan, preventing client-side double-subtraction of refunds from creating new billing discrepancies.