API
Everything the dashboard does goes through a JSON API you can call yourself. Programmatic access authenticates with
your tenant access key (wgk_…) — find and rotate it under Settings → API access.
The access key is your API credential. Day-to-day dashboard sign-in uses email links instead; you only need the key for scripts and integrations.
Base URL: https://waveguide.adwave.com
Authentication
Exchange your access key for a tenant-scoped session token (30-day expiry):
curl -X POST https://waveguide.adwave.com/auth/login \
-H "content-type: application/json" \
-d '{"access_key": "wgk_..."}'
# → { "token": "eyJ...", "tenant": { "tenantId": "ten_...", "name": "Acme" } }
Use it as a bearer on every call:
export WG_TOKEN="eyJ..."
curl -H "authorization: Bearer $WG_TOKEN" \
https://waveguide.adwave.com/tenants/ten_.../spend
Tokens are scoped to your tenant only. “Sign out everywhere” (Settings) and key rotation both invalidate outstanding sessions.
Endpoints
All paths are relative to /tenants/:id unless noted. :id is your tenant id (ten_…).
| Method | Path | Purpose |
|---|---|---|
| GET | /tenants/:id | Tenant status + connections |
| GET | /tenants/:id/spend | Month-to-date model spend by category |
| GET | /tenants/:id/sessions | Recent agent sessions |
| GET | /tenants/:id/audit | Audit log (most recent 200) |
| GET | /tenants/:id/approvals | Pending approvals |
| GET/POST | /tenants/:id/soul | Read / replace SOUL.md |
| POST | /tenants/:id/policies | Create a standing policy |
| POST | /tenants/:id/wake | Wake the agent with a message (rate-limited) |
| POST | /tenants/:id/kill | Kill switch |
| POST | /tenants/:id/resume | Resume after pause/kill |
| POST | /tenants/:id/rotate-key | Rotate the access key (returned once) |
| POST | /tenants/:id/export | Full data export → returns a bundle key |
| POST | /tenants/:id/delete | Permanent delete (irreversible) |
| GET | /tenants/:id/channels | List channel bindings |
| GET | /tenants/:id/billing-portal | Stripe customer portal URL |
Examples
Wake the agent with a task:
curl -X POST -H "authorization: Bearer $WG_TOKEN" \
-H "content-type: application/json" \
-d '{"text": "Summarize this week and flag anything odd."}' \
https://waveguide.adwave.com/tenants/ten_.../wake
Create a standing policy (auto-approve budget changes with a delta under $50):
curl -X POST -H "authorization: Bearer $WG_TOKEN" \
-H "content-type: application/json" \
-d '{
"action_type": "campaign.budget_change",
"predicate": {"budget_delta_cents": {"lt": 5000}},
"effect": "auto_approve"
}' \
https://waveguide.adwave.com/tenants/ten_.../policies
Predicates map dotted paths in the tool input to constraints (lt, lte, gt, gte, eq, in), combined with AND
semantics. An empty predicate {} matches every action of that type. Policies apply to T2 only — T3 always requires a
human — and tainted input forces approval regardless of policy.
Kill switch:
curl -X POST -H "authorization: Bearer $WG_TOKEN" \
https://waveguide.adwave.com/tenants/ten_.../kill
Approval links
Approval resolution endpoints (/a/:token/approve, /a/:token/deny) use the signed single-use token carried in the
approval card — they are not access-key-authenticated, because the token itself is the credential. Treat approval links
like passwords: don’t forward them.
Rate limits and errors
Auth endpoints are rate-limited per IP; /wake allows 5 manual runs per hour. Errors return JSON
({"error": "message"}) with conventional status codes: 401 bad/expired credentials, 403 wrong tenant scope,
429 rate-limited, 5xx our problem.