attest
Stream a spend-control attestation — the budget configuration in force paired with real and denied spend, alert delivery evidence, and the audit trail
attest exports an evidentiary bundle for a project or the whole account: the budget configuration that was in force, the spend it governed and the spend it denied, whether alerts were delivered, what the subscription was, and who changed what. It is built for compliance reviews and post-incident write-ups, not for the hot loop. Every gap in what the calling credential could see is stated explicitly with a coverage note, so the export describes its own limits.
solwyn --project <project_id> attest [--range 7d|30d|90d|365d] [--from <iso> --to <iso>] [--format json|csv] > attestation.json
solwyn attest --account [--range ...] [--format json|csv] > attestation.json| Option | Type | Default | Purpose |
|---|---|---|---|
--account | boolean | false | Account-wide evidence across every project. Requires JWT. |
--range | 7d | 30d | 90d | 365d | 30d | Preset window. |
--from, --to | ISO 8601 with timezone | none | An explicit window, both together; --to is exclusive. Replaces --range. |
--format | json | csv | json | Output format. |
| Contract | |
|---|---|
| Auth | project: any; --account: JWT |
| Exit codes | 0 success · 1 generic/API · 2 usage · 3 auth · 5 not found |
| JSON schema | stream (attestation JSON, or section,field,value CSV), not an envelope |
Like costs export, this is a stream: the response body is copied to stdout byte for byte, with no schema envelope, and --json does not change it. Redirect it to a file. Window headers — X-Solwyn-Effective-From, X-Solwyn-Retention-Clamped, X-Solwyn-Truncated, and their siblings — are echoed on stderr. A 400 or 422 from the API exits 2, because it means the request was malformed.
What the bundle contains
| Section | Contents |
|---|---|
scope, range, window | Whether this is a project or account attestation, the requested and effective window, and whether retention clamped it. |
spend_summary | Real spend and call count, denied spend and denial count. |
budgets[] | Per project: limit, period, mode, sub-period, whether archived, and the rules in force. |
alerts | Delivered count and the events, when delivery receipts are available. |
account_spend_alert | The account-wide threshold's configuration, when visible. |
subscription | Current tier, a snapshot, and tier changes in the window, when visible. |
audit | The mutation trail: who changed what, when. |
Under a project key, the account-level sections carry the note JWT evidence unavailable under a project key., and audit actors are redacted. Run with a login, or with --account, for the complete picture.
When visible, the current subscription snapshot carries tier, billing_interval, status, pending_update_exists, current_period_start, current_period_end, billing_cycle_anchor, nullable cancel_at and grace_period_until, pause_collection_exists, raw_status_at, created_at, and updated_at. It does not carry the removed cancel_at_period_end boolean. Keep nullable lifecycle dates as null; do not infer cancellation from a missing field. The command streams Core's attestation bytes unchanged. Pending plans and scheduled changes are shown separately by plan.
{
"schema_version": "1",
"generated_at": "2026-08-31T00:00:00Z",
"scope": {"type": "project", "account_id": "acct_alpha", "project_id": "proj_0123456789abcdef01234567"},
"range": "30d",
"window": {"requested_from": "2026-08-01T00:00:00Z", "requested_to": "2026-08-31T00:00:00Z",
"effective_from": "2026-08-02T00:00:00Z", "effective_to": "2026-08-31T00:00:00Z", "retention_clamped": true},
"spend_summary": {"real_spend_usd": 1.25, "real_call_count": 4, "denied_spend_usd": 2.5, "denial_count": 1},
"budgets": [{"project_id": "proj_0123456789abcdef01234567", "project_name": "Deployment", "archived": false,
"budget_limit_usd": 100.0, "budget_period": "monthly", "budget_mode": "hard_deny", "rules": []}],
"alerts": {"delivery_evidence_available": false, "coverage_note": "No delivery receipts.", "events": []},
"account_spend_alert": {"evidence_available": false, "coverage_note": "JWT evidence unavailable under a project key."},
"subscription": {"current_tier": null, "coverage_note": "JWT evidence unavailable under a project key."},
"audit": {"begins_at": "2026-08-02T00:00:00Z", "coverage_note": "Actors are redacted for project-key exports.",
"events": [{"id": "audit_1", "actor_user_id": null, "action": "budget.updated",
"target_type": "project", "target_id": "proj_0123456789abcdef01234567", "created_at": "2026-08-03T00:00:00Z"}]}
}Abridged and pretty-printed; the stream is one document. The CSV form flattens the same tree to one section,field,value row per leaf:
section,field,value
report,schema_version,1
report,generated_at,2026-08-31T00:00:00Z
scope,type,project
scope,project_id,proj_0123456789abcdef01234567
spend_summary,real_spend_usd,1.25
spend_summary,denied_spend_usd,2.5Related
audit— the same mutation trail, paged and filteredalerts— alert history with per-channel delivery receiptsbudget rules list— the rules thebudgetssection reports- Scripting — which commands stream rather than emit an envelope