This page is the map; the per-namespace pages are the territory. The method index below is generated from the client source and checked against the constructed client, so it cannot name a method the package does not export — and every namespace links to a page documenting each method’s signature, route, and semantics. TypeScript only; there is no Python package.
@vetta/core into dist/, so the installed package resolves against
nothing but zod. Inside this repository it resolves as a workspace dependency, which is how
apps/cli and the dashboard consume it. Everything documented below is the real, current surface
of packages/sdk.
@usenaive-sdk/vetta is the one place that knows how to talk to the control plane over HTTP. apps/cli and the dashboard both go through it, which is the whole reason it exists: the bearer key, the Vetta-Version pin, the Idempotency-Key on every mutating verb, and the error envelope are decided once, in src/http.ts, instead of twice and slightly differently.
What it is not
It is deliberately thinner than a product SDK:- No retry policy. A caller that wants retries owns that decision, because the CLI and the dashboard want different ones.
- No pagination magic.
listreturns one page and the cursor. No auto-paging iterator. - No client-side taxonomy.
ApiError.typeandApiError.statusare read off the response. The server owns the code → (type, status) table; a second copy here would be a second taxonomy that could drift. - No wire shapes of its own. Responses parse with the zod schemas from
@vetta/core. Where a body is not yet modelled there — shell exec, billing, the per-opfsreplies — the client says so explicitly with a local schema orRawSchema, rather than inventing a type.
Constructing a client
Everything the client cannot decide for itself is injected. That is not ceremony: it is what lets the test suite assert exact traffic against a recordingfetch, and what lets the CLI own the real one.
string
required
Control-plane origin. Trailing slashes are trimmed.
string
required
Sent as
Authorization: Bearer …. One client per credential — vetta login has none until the sign-in reply comes back.typeof fetch
required
Injected so tests can record and the CLI can wrap it (it names the host in connection errors).
() => string
required
Called once per mutating verb. Injected so a test gets a deterministic key.
Coverage
The method table is the route table. The API serves 132 routes across 95 paths; the client reaches 89 of those 95 paths from exactly one method each, across twenty namespaces:
Cross-cutting behavior has its own pages too: Errors, Pagination, and Streaming.
Two methods are parameterized over a family rather than split per route, because splitting them would produce five near-identical methods with no added type safety:
computers.fs(id, op, body)reaches all five/v1/computers/{id}/fs/*routes. The reply differs per op ({content},{entries},{created}…), so it comes back asRawSchema.stream(sessionId, options)is the one consumer ofGET /v1/sessions/{id}/stream.
That accounting is generated, not asserted by hand —
scripts/interface-parity rebuilds it from the route table, the client source and this page.
Do not write those five paths on this page with an HTTP verb in front of them.
apps/api/test/drift.e2e.ts scans every published docs/**.mdx for METHOD /v1/… to learn what the docs promise a customer. Pairing a verb with one of these paths would register a deliberately-unpublished route as documented and quietly retire the allowance that keeps it honest.Invariants worth knowing before your first call
Money is integer micro-USD, always
Every monetary field on every signature isz.number().int(). The client never exposes a float and never converts to dollars — formatting is a presentation decision, and doing it here once cost a 10⁶ error in an earlier audit. credits.topup takes amountMicroUsd; sessions.setBudget takes capMicroUsd.
The vault has no read method, by contract
vaults exposes create, list, get, delete and credentials.{create,list,delete} — and no reveal. The control plane serves no reveal route and VaultCredentialSchema has no value field, so list could not return one even if a handler tried. Do not add one. Rotation is create-new + delete-old rather than an update, so last_injected_at stays attributable to exactly one secret.
The two skill pushes are not interchangeable
skills.push({ slug, content }) hits POST /v1/skills and is create-or-version by slug: unknown slug creates the skill. skills.pushVersion(ref, { content }) hits POST /v1/skills/{id}/versions and requires the skill to exist — an unknown ref is a 404, not a create. A release pipeline pinned to a skill id wants the second one, so a rename cannot silently mint a second skill.
Both accept an optional sha256. It is an integrity claim the server checks against its own digest, never the digest of record. A mismatch is rejected.
Worked examples
Every block below was run against staging on 2026-08-22. Replies are shown as returned, except the two marked as abridged.Reading the balance
79416537 micro-USD is $79.416537. The client hands you the integer; you format it.
Pushing a skill, then a second version of it
id and slug on the reply. They are what makes the chain above work, and they are the reason pushes parse with SkillPushSchema (core’s SkillVersionSchema extended) rather than the bare version schema, which would strip them.
Why pushVersion is not just push
push with an unknown slug would have created a skill. That is the entire difference.
The integrity claim is checked, not trusted
ApiError.param names the offending field, which is what the CLI prints.
Listing versions
The page envelope
{ data, has_more, next_cursor } on every list. There is no auto-paging helper; pass after: next_cursor.
Deleting
{ id, object, deleted } acknowledgement.
Handling errors
Every non-2xx throwsApiError. There is one shape:
requestId is the single most useful field in a report — it is the server’s own correlation id for that call.
Streaming
stream is a resumable async generator over session events. afterSeq is exclusive, so resuming from the last seq you saw does not replay it:
send escape hatch, so the control-plane key never reaches a browser.
The same operation in the other two interfaces
Every method below has a counterpart in the CLI and a page in the API reference, named the same way. The three surfaces are generated against one route table, so a gap in any of them is a build failure rather than a discovery.Method index
Every method on the client, and the route it calls — 179 methods across 26 namespaces. Generated from the client source byscripts/sdk-index.mjs; the drift gate fails if it falls behind.
agents
apps
auditLogs
auth
board
computers
connections
credits
deployments
domains
emails
files
harnesses
identities
keys
media
models
orgs
phones
proxy
sessions
skills
social
stream
vaults
webhooks
Plan
An organization needs an active plan before it can be used. These two methods are open even when it does not have one, so a client can always read the state and start a checkout.402 with code subscription_required.