SOLWYN
Commands

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.

Terminal
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
OptionTypeDefaultPurpose
--accountbooleanfalseAccount-wide evidence across every project. Requires JWT.
--range7d | 30d | 90d | 365d30dPreset window.
--from, --toISO 8601 with timezonenoneAn explicit window, both together; --to is exclusive. Replaces --range.
--formatjson | csvjsonOutput format.
Contract
Authproject: any; --account: JWT
Exit codes0 success · 1 generic/API · 2 usage · 3 auth · 5 not found
JSON schemastream (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

SectionContents
scope, range, windowWhether this is a project or account attestation, the requested and effective window, and whether retention clamped it.
spend_summaryReal spend and call count, denied spend and denial count.
budgets[]Per project: limit, period, mode, sub-period, whether archived, and the rules in force.
alertsDelivered count and the events, when delivery receipts are available.
account_spend_alertThe account-wide threshold's configuration, when visible.
subscriptionCurrent tier, a snapshot, and tier changes in the window, when visible.
auditThe 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.5
  • audit — the same mutation trail, paged and filtered
  • alerts — alert history with per-channel delivery receipts
  • budget rules list — the rules the budgets section reports
  • Scripting — which commands stream rather than emit an envelope

On this page