Documentation › Integrator guides
Using the Steward API
The Steward API is a read-only HTTP surface for external integrations — CI scripts, dashboards, custom tooling that needs to poll Steward state without driving a browser session. This doc covers what the API can do, what it can't, and how to think about the integration shape before you write code.
For the per-RPC reference (request bodies, error envelopes,
curl examples), see /docs/api. This concept
doc is the "should I be using this?" companion.
What the API is
Steward exposes a Connect-RPC / HTTP surface at
https://steward-dev.ai/api/ for read-only operations.
A signed-in user mints a personal access token from the
browser surface, then their integration calls the API with
that token in an Authorization: Bearer ... header.
The shape is small on purpose. Five read RPCs ship today:
| RPC | Purpose |
|---|---|
MeService.Get |
Returns the token owner's identity + resolved permissions. The "who am I + what can I see" probe. |
ClaService.Get |
The CLA signature state for a repo. |
EvaluationsService.Get |
One evaluation job by id, including the report JSON + markdown. |
GovernanceService.GetSections |
A repo's governance.pxf as parsed sections — including the preset-resolved effective config for preset-pinned files. |
GovernanceService.PreviewPresetChange |
Read-only what-if: the effective config the repo would run under a different preset pin. |
Full request/response shapes + curl examples are at
/docs/api.
When the API is the right tool
The API is the right surface when:
- You're driving from CI or a script — somewhere that doesn't have a browser to maintain a Steward session.
- You're polling a state that changes — most commonly an
evaluation that's queued/running and you want to know when
it lands
done. - You're building a dashboard that reads aggregate Steward state alongside data from other systems.
- You need the data in a structured shape — the API returns JSON; the dashboard returns rendered HTML.
The API is not the right surface when:
- You want push notifications. Steward doesn't ship outbound webhooks today. You poll instead — see "polling pattern" below.
- You want to write data. The API is read-only by design; see "Why no write" below.
- You want a one-off lookup — a human reading
/app/repos/<id>is faster than a CI script's tooling overhead.
Why no write
Token auth is read-only by design. CreateEvaluation, GovernanceUpdateSection, and the other write RPCs require an interactive browser session because each one intersects with the user's monthly credit budget. A token-driven write loop could burn a user's quota faster than their dashboard can warn them.
Drive writes from /app; reach for an API token when a CI
runner needs to poll status from outside the browser.
The evaluations:run scope exists in the validation
allowlist but is reserved — not wired to any RPC yet,
and won't be until the billing/quota intersection is
designed.
Why no outbound webhooks
Two reasons:
- Outbound webhook delivery is a sustained-ops commitment — retry queues, signature schemes, replay protection. Steward's current size doesn't justify the support burden.
- Polling is fine for the typical integration shape. The most common ask is "tell me when my evaluation is done"; the evaluation's median time-to-done is under 20 seconds. Polling every 5 seconds resolves the typical integration quickly enough.
If outbound webhooks become a load-bearing need, they'll ship later. Today, plan for polling.
The polling pattern
For "wait until this evaluation is done":
1. POST /api/steward.api.v1.EvaluationsService/Create
from a browser session (NOT from the API token; writes
are session-only).
2. Record the returned job_id.
3. Hand the job_id to your CI script + API token.
4. Script polls EvaluationsService.Get(job_id) every 5s.
5. When response.status == "done", read response.report_md.
6. When status == "failed" or "skipped", read the error_message
metadata + decide whether to retry.
Suggested polling cadence:
| Phase | Cadence |
|---|---|
| Initial poll | Within 2 seconds of trigger. Most evaluations land in under 20s. |
| Steady-state | Every 5 seconds. |
| After 60 seconds | Slow to every 15 seconds. |
| After 5 minutes | Slow to every 60 seconds — if it hasn't landed by then, it's unusual. |
| After 15 minutes | Treat as failed. Log + alert. |
Steward's worker's per-job timeout is 5 minutes, so any
evaluation older than ~7 minutes is genuinely stuck and your
poll loop should give up. The status will move to failed
eventually (via the boot-time recoverStuck sweep) but you
don't need to wait for it.
Token auth basics
The auth flow is standard:
- Mint a token from
/app/account→ API tokens. - Steward returns the plaintext once:
stwd_pat_abcdef1234567890... - Send it on every API call:
Authorization: Bearer stwd_pat_abcdef1234567890...
A few properties worth knowing:
- Plaintext shown once. The server stores only the SHA-256 hash + a display prefix. If you lose the plaintext, you mint a new token.
- User-scoped. A token acts as the user who minted it. Revoking the user's account revokes all their tokens via FK cascade.
- Tokens cannot mint tokens. Calling
MeService.CreateApiTokenwith a Bearer token in the Authorization header returnsPERMISSION_DENIED. This closes the privilege-escalation surface: a leaked low-scope token can't bootstrap a higher-scope one.
Full mint/rotate/revoke walkthrough at Mint and manage API tokens.
Scopes are picked at mint time
Each token carries one or more scopes, set when you mint and not editable afterwards. Four read scopes are wired:
| Scope | Grants |
|---|---|
account:read |
MeService.Get |
cla:read |
ClaService.Get |
evaluations:read |
EvaluationsService.Get |
repos:read |
GovernanceService.GetSections, GovernanceService.PreviewPresetChange |
Pick narrowly. A CI script that polls evaluations needs
evaluations:read only; granting repos:read "just in
case" widens the blast radius if the token leaks.
For tokens that need multiple scopes, list them at mint time:
scopes = ["evaluations:read", "cla:read"]
To rotate scopes, revoke the token and mint a new one.
There's no scope-edit RPC by design — every scope change
emits a fresh api_token.minted audit row so the trail is
clean.
Rate limits
60 requests/minute per token. Over-cap requests return
429 Too Many Requests with a Retry-After header (seconds
until the next per-minute bucket starts).
Notes:
- The counter is per-token, not per-user. A user with multiple tokens gets multiple budgets.
- The
Retry-Aftervalue is honest — if it says 23 seconds, you can call again in 23 seconds. Backing off longer thanRetry-Afteris fine; backing off shorter earns another 429. - There's no separate "burst" budget; the 60/minute is a rolling per-minute bucket.
Full rate-limit + error-handling pattern at Handle errors + rate limits.
Audit trail for your integration
Every meaningful action lands an audit event you can read later:
| Event | When it fires |
|---|---|
api_token.minted |
Token created. Metadata: name + scopes. |
api_token.revoked |
Token revoked from the dashboard. |
api_token.used |
First call per hour per token. Dedup'd to keep the log readable. |
The dedup on api_token.used matters when you're polling: a
script calling once per minute generates one audit row per
hour, not 60 per hour. Read this when looking at audit
volume from /sponsor/audit — your integration's footprint
is small by design.
If you need fine-grained call counts (e.g. for billing attribution), keep your own telemetry. Steward doesn't record every API call as an audit row.
What's next
/docs/api— per-RPC reference: request bodies, error envelopes, curl examples.- Mint and manage API tokens — the mint / rotate / revoke walkthrough.
- Integrate Steward with CI — worked example of the polling pattern in a CI script (forthcoming in slice 2).
- Handle errors + rate limits — the error envelope catalogue + backoff strategy (forthcoming in slice 3).
Steward