DEV Community

Mattias chaw
Mattias chaw

Posted on

Build a run receipt schema for multi-provider AI workloads

Build a run receipt schema for multi-provider AI workloads

An AI workload needs more than a request log when it becomes part of a real product.

A request log can tell you that a call happened. A trace can show the shape of an agent run. A billing table can show token counts and debits. Those pieces are useful, but they often live in different systems. When a production workflow fails, spends more than expected, or needs to be reviewed by support, finance, or security, the team needs one portable record that explains what ran and what can be verified.

That record is a run receipt.

I use the term run receipt for a compact, exportable evidence packet attached to one AI workload. It does not need raw prompts, private documents, API keys, payment identifiers, or customer names. It needs the operational facts that let another engineer reconstruct the route, pricing source, token shape, failures, retries, and missing evidence.

AIWave is built around one OpenAI-compatible route to Chinese model families such as DeepSeek, GLM, Kimi, Qwen, ERNIE, MiniMAX, Doubao, StepFun, and MiMo. A unified route is useful because client code can stay stable while model choices change. That same convenience creates an evidence requirement: every serious workload should leave behind enough structured facts to explain the route later.

This article gives a practical schema and implementation pattern for a run receipt.

Start with the receipt boundary

A run receipt should be scoped to a workload, not to a single HTTP request.

One workload may include planning, retrieval, model calls, tool calls, retries, validation, and a final response. If each request is reviewed alone, the team may miss the real failure. A model call may be correct while the tool output is stale. A retry may look harmless until ten steps multiply it. A fallback may be rare in count but important in spend.

Use a receipt boundary that matches the question a human will ask.

Boundary Good fit Weak fit
request Debugging one API response Explaining a multi-step agent run
step Comparing model choices inside an agent Explaining total cost
run Reviewing one workload end to end High-volume aggregate monitoring
window Weekly finance review Debugging one failure

For most production agent and batch workflows, run is the right default. It is small enough to inspect and large enough to connect routing, cost, and outcome evidence.

Check the current pricing source

Before writing this article, I checked AIWave's public pricing endpoint on 2026-09-04 at 13:13 UTC. The endpoint returned success=true, 63 route records, pricing_version=a42d372ccf0b5dd13ecf71203521f9d2, auto_groups=["default"], and group ratios of default=3 and vip=1.

The same live response included these route fields:

Route model_ratio completion_ratio cache_ratio Enabled groups
deepseek-v4-flash 0.319 3 0.0318 default, vip, svip
deepseek-v4-pro 0.957 3 0.0333 default, vip, svip
glm-5.1 1.05 3.142857 0.32381 default, vip, svip
kimi-k3 2.25 5 0.2 default, vip, svip
qwen3-max 0.7810988945590882 4 absent default, vip, svip

Using AIWave's public VIP x1 rate-card conversion, selected examples correspond to these per-1M token planning rates:

Route Input Cached input Output
deepseek-v4-flash $0.638 $0.0203 $1.914
deepseek-v4-pro $1.914 $0.0637 $5.742
glm-5.1 $2.10 $0.6800 $6.60
kimi-k3 $4.50 $0.90 $22.50

These are dated planning inputs, not a promise that every account, request, future route, or provider-side table will produce the same invoice. A receipt should store the source URL, checked time, pricing version, effective group, resolved model, and token buckets beside the run.

Define the minimum receipt

The minimum receipt should answer eight questions:

  1. What workload ran?
  2. Who owns the review without exposing the customer?
  3. Which route and model actually served each step?
  4. Which pricing version and account group applied?
  5. How many input, cached input, and output tokens were recorded?
  6. Did retries, fallbacks, or tool calls change the result?
  7. What outcome was observed?
  8. Which fields are missing or unverifiable?

Here is a compact JSON shape:

{
  "receipt_version": "run-receipt-v0.1",
  "receipt_id": "rr_2026_09_04_example_001",
  "run_id": "support_summary_2026_09_04_042",
  "created_at": "2026-09-04T13:13:27Z",
  "environment": "production",
  "account_ref": "acct_hash_7f2b",
  "feature": "support_summary",
  "pricing": {
    "source_url": "https://aiwave.live/api/pricing",
    "checked_at": "2026-09-04T13:13:27Z",
    "pricing_version": "a42d372ccf0b5dd13ecf71203521f9d2",
    "effective_group": "vip"
  },
  "summary": {
    "status": "completed",
    "outcome_label": "passed_schema_validation",
    "total_steps": 3,
    "total_retries": 1,
    "fallbacks": 0
  },
  "totals": {
    "input_tokens": 2200000,
    "cached_input_tokens": 1600000,
    "output_tokens": 340000
  },
  "privacy": {
    "contains_raw_prompt": false,
    "contains_api_key": false,
    "redaction_policy": "field-minimized-v1"
  },
  "unknowns": ["provider_internal_queue_time"]
}
Enter fullscreen mode Exit fullscreen mode

This object is not a substitute for the provider invoice. It is the workload's portable explanation. It tells another reviewer which source to check, which route was used, which fields were intentionally absent, and which fields could not be verified.

Store steps as append-only events

Receipts become unreliable when they are overwritten. Store step events append-only, then render the receipt from those events.

create table ai_run_events (
  event_id text primary key,
  receipt_id text not null,
  run_id text not null,
  step_id text not null,
  sequence integer not null,
  event_type text not null,
  created_at text not null,
  requested_model text,
  resolved_model text,
  provider text,
  pricing_version text,
  effective_group text,
  input_tokens integer,
  cached_input_tokens integer,
  output_tokens integer,
  latency_ms integer,
  retry_count integer not null default 0,
  fallback_from text,
  fallback_reason text,
  status text not null,
  error_code text,
  evidence_hash text,
  redaction_policy text not null
);
Enter fullscreen mode Exit fullscreen mode

If a correction is needed, add a new event that explains the correction. Do not edit the original event in place. That gives support and engineering a review trail without pretending the first record never existed.

This design also keeps the receipt flexible. A simple chat call may emit one model event. A coding agent may emit planning, retrieval, model, tool, validation, retry, and finalization events. The rendered receipt can summarize both without changing the core model.

Keep model evidence separate from business outcome

Do not let the model response become the only success signal.

A model call may return HTTP 200 and still fail the workflow. A structured output may parse but violate a business rule. A code generation run may finish but fail tests. A customer support summary may be fluent but omit the ticket status.

Put route evidence and outcome evidence in different fields:

Field Example Meaning
request_status success The model request completed
validation_status schema_passed The response matched a technical contract
outcome_label needs_human_review The workload result still requires review
test_result unit_tests_failed A downstream check rejected the output
decision_status not_approved A human or policy gate has not approved execution

This separation matters for agent systems. A route can work while the run should stop. A receipt should make that distinction visible.

Calculate cost from buckets, not guesses

The receipt should store the buckets needed for cost explanation. At minimum, keep fresh input, cached input, output, retry count, fallback count, pricing version, and effective account group.

from dataclasses import dataclass
from decimal import Decimal


@dataclass(frozen=True)
class Rate:
    input_per_m: Decimal
    cached_input_per_m: Decimal
    output_per_m: Decimal


rates = {
    "deepseek-v4-flash": Rate(Decimal("0.638"), Decimal("0.0203"), Decimal("1.914")),
    "deepseek-v4-pro": Rate(Decimal("1.914"), Decimal("0.0637"), Decimal("5.742")),
    "glm-5.1": Rate(Decimal("2.10"), Decimal("0.6800"), Decimal("6.60")),
    "kimi-k3": Rate(Decimal("4.50"), Decimal("0.90"), Decimal("22.50")),
}


def estimate_step_usd(model: str, input_tokens: int, cached_input_tokens: int, output_tokens: int) -> Decimal:
    rate = rates[model]
    fresh_input = max(input_tokens - cached_input_tokens, 0)
    million = Decimal("1000000")
    return (
        Decimal(fresh_input) / million * rate.input_per_m
        + Decimal(cached_input_tokens) / million * rate.cached_input_per_m
        + Decimal(output_tokens) / million * rate.output_per_m
    )


print(estimate_step_usd("deepseek-v4-flash", 2_000_000, 1_500_000, 300_000))
Enter fullscreen mode Exit fullscreen mode

In production, load rates from a dated snapshot rather than hard-coding them. The example is here to show the shape of the calculation, not to replace the gateway's billing source.

Add privacy rules to the schema

A receipt should be useful because it avoids sensitive material by default.

Do not include raw prompts, completions, API keys, customer emails, phone numbers, payment identifiers, or private documents in the core receipt. If a support case needs a redacted excerpt, attach it as a separate evidence object with its own retention class and approval status.

Use explicit privacy fields:

{
  "privacy": {
    "contains_raw_prompt": false,
    "contains_completion_text": false,
    "contains_customer_identifier": false,
    "contains_payment_identifier": false,
    "redaction_policy": "field-minimized-v1",
    "retention_class": "support-30d"
  }
}
Enter fullscreen mode Exit fullscreen mode

This keeps the receipt shareable across engineering, finance, and support. It also forces the team to admit when a field is missing instead of filling the gap with customer content.

Export in boring formats

A receipt that only works inside one dashboard is not portable evidence.

Start with JSON for machines, CSV for finance review, and HTML for humans. PDF can come later if buyers ask for it, but the first version should keep the original structured data intact.

Format Primary user Requirement
JSON Engineering Exact fields, stable schema version
CSV Finance and operations One row per step or cost bucket
HTML Support and customer review Readable summary with source links

Every export should include the schema version, creation time, pricing source, pricing version, and unknown fields. If a value is derived, label it. If a value is not available, write unknown instead of inventing a number.

Render a human summary

The human summary should be short and inspectable.

Use this shape:

Run support_summary_2026_09_04_042 completed with 3 steps and 1 retry.
The run used deepseek-v4-flash for extraction and deepseek-v4-pro for final review.
Pricing source was https://aiwave.live/api/pricing, checked at 2026-09-04T13:13:27Z,
with pricing_version a42d372ccf0b5dd13ecf71203521f9d2 and effective_group vip.
No raw prompt, completion text, API key, or payment identifier is included.
Unknown: provider_internal_queue_time.
Enter fullscreen mode Exit fullscreen mode

That is enough for a first review. A finance teammate can see the pricing source. A support teammate can see the missing field. An engineer can open the JSON for details.

Validate before publishing or sharing

Treat run receipts like release artifacts. Validate them before they leave the system.

FORBIDDEN_KEYS = {
    "api_key",
    "authorization",
    "password",
    "secret",
    "payment_id",
    "customer_email",
}


def validate_receipt(receipt: dict) -> list[str]:
    errors = []
    text = str(receipt).lower()

    for key in FORBIDDEN_KEYS:
        if key in text:
            errors.append(f"forbidden field or value contains {key}")

    pricing = receipt.get("pricing", {})
    if not pricing.get("source_url"):
        errors.append("missing pricing.source_url")
    if not pricing.get("checked_at"):
        errors.append("missing pricing.checked_at")
    if not pricing.get("pricing_version"):
        errors.append("missing pricing.pricing_version")

    privacy = receipt.get("privacy", {})
    if privacy.get("contains_api_key") is not False:
        errors.append("privacy.contains_api_key must be false")
    if privacy.get("contains_raw_prompt") is not False:
        errors.append("privacy.contains_raw_prompt must be false")

    return errors
Enter fullscreen mode Exit fullscreen mode

The validator should be stricter than the dashboard. If a receipt cannot prove its source, version, and privacy posture, it should stay internal until fixed.

Use receipts as a product boundary

A run receipt should not become an all-purpose observability platform on day one. Keep the first version narrow:

Include now Defer until needed
Run id, steps, model route, tokens, status Full trace viewer
Pricing source and version Real-time contract negotiation
Retry and fallback counts Automated policy changes
Privacy and unknown fields Long retention by default
JSON, CSV, HTML export Complex role management

This boundary is important for small teams. The receipt proves what a workload can explain today. It does not need to solve every monitoring, audit, and governance problem at once.

Final checklist

Before you rely on a run receipt, make sure it answers these questions:

  1. Which workload does this receipt describe?
  2. Which exact route served each model step?
  3. Which pricing source and version were checked?
  4. Which effective account group applied?
  5. Which usage buckets were recorded?
  6. Which retries, fallbacks, and errors happened?
  7. Which business or validation outcome was observed?
  8. Which sensitive fields are intentionally excluded?
  9. Which fields are still unknown?
  10. Which export format lets another team review the evidence?

The value of a multi-provider gateway is not only that it can route traffic. It should also help teams explain what happened after a workload ran. A run receipt turns that explanation into a small, dated, portable record that developers, support, and finance can inspect without exposing the data that should stay private.

Source links to keep close

Keep these sources beside receipt reviews:

Source Use
AIWave pricing Public gateway pricing context
https://aiwave.live/api/pricing Machine-readable pricing snapshot
AIWave models docs Model catalog and route context
AIWave trust page Public trust and operational posture
AIWave Chat Completions docs OpenAI-compatible request shape

Recheck these sources before copying numbers into a long-lived runbook. Pricing versions, route fields, model ids, and account policy can change after a successful integration.

Top comments (0)