Run the economic debugger API¶
The current product slice is a headless, self-hostable API. It does not require
the console package and does not depend on the release-blocked zeroth-sdk
prototype.
Install and migrate¶
Install the backend extra from a source checkout without the UI:
From a source checkout, configure a durable database and signing secret, then apply the economic-plane migration chain:
export ECP_DATABASE_URL=sqlite+pysqlite:////var/lib/zeroth/econ_plane.db
export ECP_JWT_SECRET=<a-persistent-random-secret>
export ECP_SERVICE_PRINCIPAL_TENANT_ID=acme
uv run alembic -c alembic-econ.ini upgrade head
uv run uvicorn zeroth.econ.plane.main:app --host 127.0.0.1 --port 8001
Use PostgreSQL for a managed or multi-process deployment. SQLite startup can
converge supported historical schemas, but operators should still apply
migrations explicitly. /health reports schema_revision.state=current when
the database is ready.
Authenticate¶
All ingestion and debugger routes require an econ-plane JWT. Zeroth's bundled runtime mints short-lived service tokens automatically. For a local API probe, mint one from the configured service identity:
export TOKEN="$(uv run python -c 'from zeroth.econ.analytics.service_auth import mint_econ_service_token; print(mint_econ_service_token() or "")')"
The JWT tenant claim—not a request field—selects the data boundary. Admin and
Analyst may ingest; Viewer, Approver, Analyst, and Admin may query.
Define a successful outcome¶
Before the debugger can calculate cost per successful outcome, an Admin must bind each workflow version to one terminal outcome type and predicate:
curl -sS http://127.0.0.1:8001/v1/debugger/outcome-definitions \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"workflow_id": "invoice-processing",
"workflow_version": "v3",
"outcome_type": "approval",
"operator": "equals",
"target": true
}'
Definitions are tenant-scoped and immutable for a workflow version. Replaying
the exact definition is idempotent; changing the outcome type, operator, or
target returns 409. Publish a new workflow version to change success
semantics. Supported operators are equals, not_equals,
greater_than_or_equal, and less_than_or_equal; ordered comparisons require
a numeric target. This covers definitions such as fraud_flag == false and
reopen_rate <= 0.05 without guessing what a raw value means.
List the definitions visible to the current tenant:
curl -sS -G http://127.0.0.1:8001/v1/debugger/outcome-definitions \
-H "Authorization: Bearer $TOKEN" \
--data-urlencode workflow_id=invoice-processing
Ingest economic evidence¶
An execution identifies the workflow, immutable version, run, step, attempt, analyzed subject, and bounded typed dimensions. Keep measured and estimated costs explicit. The shipped client uses the same contract. It can require both writes to be accepted before returning:
import os
from datetime import UTC, datetime
from decimal import Decimal
from zeroth.econ.instrumentation import (
ExecutionEvent,
InstrumentationClient,
OutcomeEvent,
)
with InstrumentationClient.authenticated(
base_url="http://127.0.0.1:8001/v1",
bearer_token=os.environ["ZEROTH_ECON_TOKEN"],
) as econ:
econ.track_execution_confirmed(
ExecutionEvent(
execution_id="evt-001",
join_key="run-001",
timestamp=datetime.now(UTC),
capability_id="invoice-processing",
implementation_id="invoice-processing:v3",
model_version="gpt-5-mini",
workflow_id="invoice-processing",
workflow_version="v3",
run_id="run-001",
step_id="extract",
attempt=1,
subject_id="account-42",
dimensions={"plan": "enterprise", "region": "us-east"},
token_cost_usd=Decimal("0.0125"),
cost_measurement="measured",
usage_measurement="measured",
metadata={
"provider": "openai",
"model": "gpt-5-mini",
"project_id": "proj_a",
},
)
)
econ.track_outcome_confirmed(
OutcomeEvent(
execution_id="evt-001",
join_key="run-001",
capability_id="invoice-processing",
outcome_type="approval",
outcome_value=True,
)
)
Confirmed delivery raises when the plane rejects a write, so a setup check
cannot silently report success while evidence remains only in memory. The
ordinary track_execution and track_outcome methods remain buffered for
long-running applications.
Do not put the token in source code. For global instrumentation helpers, set
ECP_BASE_URL and ECP_BEARER_TOKEN, then call
configure(InstrumentationConfig.from_env()); the runtime reads the token from
the environment. Choose one authentication path: use
InstrumentationClient.authenticated for a short-lived static token, or the
ordinary constructor's headers_provider in a long-running service when
credentials must rotate.
Equivalent HTTP contract¶
The execution request emitted by the client is equivalent to:
curl -sS http://127.0.0.1:8001/v1/instrumentation/executions \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"execution_id": "evt-001",
"join_key": "run-001",
"timestamp": "2026-08-30T12:00:00Z",
"capability_id": "invoice-processing",
"implementation_id": "invoice-processing:v3",
"model_version": "gpt-5-mini",
"workflow_id": "invoice-processing",
"workflow_version": "v3",
"run_id": "run-001",
"step_id": "extract",
"attempt": 1,
"subject_id": "account-42",
"dimensions": {"plan": "enterprise", "region": "us-east"},
"token_cost_usd": "0.0125",
"cost_measurement": "measured",
"usage_measurement": "measured"
}'
The terminal outcome request uses the same run identity:
curl -sS http://127.0.0.1:8001/v1/instrumentation/outcomes \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"execution_id": "evt-001",
"join_key": "run-001",
"capability_id": "invoice-processing",
"implementation_id": "invoice-processing:v3",
"outcome_type": "approval",
"outcome_value": true,
"occurred_at": "2026-08-30T12:00:01Z"
}'
Replaying the same execution identity and immutable payload reports
duplicate; changing its identity-bearing fields reports a validation error
instead of double-counting spend.
Query the debugger¶
curl -sS -G http://127.0.0.1:8001/v1/debugger/timeline \
-H "Authorization: Bearer $TOKEN" \
--data-urlencode workflow_id=invoice-processing
curl -sS -G http://127.0.0.1:8001/v1/debugger/cohorts \
-H "Authorization: Bearer $TOKEN" \
--data-urlencode workflow_id=invoice-processing \
--data-urlencode group_by=dimension \
--data-urlencode dimension=plan
curl -sS -G http://127.0.0.1:8001/v1/debugger/breakage \
-H "Authorization: Bearer $TOKEN" \
--data-urlencode workflow_id=invoice-processing
curl -sS -G http://127.0.0.1:8001/v1/debugger/report \
-H "Authorization: Bearer $TOKEN" \
--data-urlencode workflow_id=invoice-processing \
--data-urlencode cohort_dimension=plan
start is inclusive and end is exclusive. Timeline and cohort results keep
measured and estimated dollars separate and report incomplete evidence.
Breakage reports money present in failed runs at each observed step; its
attribution value is failed_run_exposure_not_step_causality. Do not describe
that number as proof that the step caused the failure.
Each request scans at most 50,000 recent execution events. That bound is for single-team debugging. Organization history, scheduled reports, chargeback, and provider-bill reconciliation belong in pre-aggregated managed storage.
Generate a shareable local diagnostic¶
Keep the JWT in an environment variable and render the API response to Markdown without installing or launching the UI:
export ZEROTH_ECON_TOKEN="$TOKEN"
uv run zeroth-econ diagnose \
--workflow-id invoice-processing \
--cohort-dimension plan \
--output economic-diagnostic.md
Use --format json for automation. The report returns 404 rather than a
zero-value story when the selected workflow and window have no evidence. It
chooses one next action, keeps measured and estimated cost separate, and embeds
the same claim limits as the API. It never labels failed-run exposure as causal
waste or historical evidence as proven savings. If a version has no outcome
definition, the report names it under undefined_outcome_versions, marks its
runs unresolved, and recommends defining success before changing the workflow.
Debug and roll back¶
- A
401means the JWT is missing, invalid, or expired. - A
403means the authenticated role is insufficient or the payload claims a different tenant. - A
422on ingestion usually means inconsistent identity, an invalid dimension, or a missing capability relationship. - A result with
incomplete_events > 0means cost or identity evidence is absent; it is not zero spend. - Check
/health, application logs, and thealembic_versionrow before investigating query totals.
Revisions 20260830_11, 20260830_12, and 20260830_13 are additive. Before
rollback, stop serving the affected routes and back up the database. Downgrade
to 20260830_12 to remove only provider-bill reconciliation, as described in
the reconciliation guide. To remove outcome
definitions as well, run:
This drops the provider-bill tables and outcome_definitions; historical
execution and outcome rows remain, but all workflow versions become unresolved
until definitions are restored.
To remove the complete debugger evidence spine as well, then run:
That second downgrade removes the debugger indexes and evidence-spine columns;
historical rows remain, but their new identity fields are lost. Roll forward
with upgrade head before re-enabling the routes.
Commercial activation trigger¶
Do not meter these debugger queries. Offer the free layer to production teams and record requests for cross-team rollups, provider-invoice reconciliation, chargeback, retention, SSO/SCIM, or signed change evidence. The first normalized provider-bill API now exists; use the reconciliation guide to test it with a real export. Do not expand into credentialed connectors, billing, or the organization shell until a qualified buyer validates this closure report. The safer fallback is managed hosting plus SSO/RBAC/retention if reconciliation demand does not appear. Use the commercial pilot runbook for the privacy-safe, asynchronous qualification funnel and stop criteria.