JavaScript / TypeScript SDK

Zero-dependency SDK for Node.js, Deno, Bun, and browsers. Uses native fetch. TypeScript-first with full type exports.

Installation

bash
npm install @getstackrun/sdk

Quick Start

typescript
import { Stack } from '@getstackrun/sdk';

const stack = new Stack({ apiKey: 'sk_live_...' });

// Register an agent
const agent = await stack.agents.register({ name: 'my-agent' });

// Issue a passport
const passport = await stack.passports.issue({
  agent_id: agent.id,
  scopes: ['read', 'write'],
  intent_description: 'Process invoices',
  intent_services: ['slack', 'stripe'],
});

// Retrieve credentials
const cred = await stack.credentials.get('slack');

Services

stack.agents

typescript
stack.agents.register({ name, description?, credential_access? })
stack.agents.list()
stack.agents.get(id)
stack.agents.update(id, { name?, status?, credential_access? })
stack.agents.delete(id)
stack.agents.unblock(id)

stack.passports

typescript
stack.passports.issue({ agent_id, scopes?, ttl_seconds?, ... })
stack.passports.verify(token, serviceId?)
stack.passports.revoke(jti, reason?)
stack.passports.delegate({ parent_token, child_agent_id, ... })
stack.passports.refresh(token, ttlSeconds?)
stack.passports.listActive({ agentId?, sessionId? })
stack.passports.revokeAgent(agentId, reason?)
stack.passports.revokeAll(reason?)
stack.passports.checkpoint(jti, input)
stack.passports.checkout(jti, input)

stack.skills

typescript
// Publishing
stack.skills.publish({ name, description, input_schema, output_schema, ... })
stack.skills.get(id)
stack.skills.browse({ query?, tags?, trust_level? })
stack.skills.listOwned()
stack.skills.update(id, updates)
stack.skills.suspend(id)
stack.skills.activate(id)

// Invocations
stack.skills.invoke(skillId, { agent_id, input, passport_claims? })
stack.skills.getInvocation(invocationId)
stack.skills.poll(invocationId, { intervalMs?, timeoutMs? })
stack.skills.listInvocations()

// Requests
stack.skills.postRequest({ description, max_price_cents?, tags? })
stack.skills.listRequests({ status?, limit? })
stack.skills.suggestComposition(requestId)

stack.identity

typescript
stack.identity.listProviders()
stack.identity.initiateVerification({ provider_key, return_url? })
stack.identity.completeVerification(providerKey, sessionRef)
stack.identity.listClaims()
stack.identity.revokeClaim(id)
stack.identity.grantDelegation(scopes)

Other Services

typescript
stack.services.listAvailable()
stack.services.listConnected()
stack.services.connect({ service_id, provider, credential })
stack.services.connectCustom({ name, credential })
stack.services.disconnect(connectionId)

stack.credentials.get(provider)
stack.credentials.getByConnection(connectionId)

stack.dropoffs.create({ from_agent, to_agent, schema })
stack.dropoffs.deposit(id, agentId, payload)
stack.dropoffs.collect(id, agentId)

stack.team.invite({ email, name?, role? })
stack.team.list()
stack.team.update(id, { role?, allowed_connections? })
stack.team.revoke(id)

stack.notifications.create({ channel_type, destination, events? })
stack.notifications.list()
stack.notifications.verify(id, code)
stack.notifications.test(id)

stack.securityEvents.list({ agentId?, page?, limit? })
stack.securityEvents.resolve(id)

stack.proxy.request({ provider, method, url, headers?, body? })

stack.scan.scan({ content, context?, source? }, { passportToken? })
stack.scan.usage()

stack.audit.list({ limit?, since?, agentId?, passportJti? })
stack.audit.chainHead()
stack.audit.verifyChain({ from?, to?, limit? })

stack.audit

typescript
// Tail recent entries — newest-first. Pass `since` (epoch ms) for
// incremental polling; pass `agentId` or `passportJti` to narrow.
const { entries, max_timestamp } = await stack.audit.list({
  limit: 20,
  agentId: 'agt_support',
});

// Anchor the chain head externally to prove no row was rewritten later.
const head = await stack.audit.chainHead();
console.log(head.latest_entry_hash, head.total_entries);

// Walk the chain and verify every entry's hash + link integrity.
const verdict = await stack.audit.verifyChain({ limit: 10_000 });
if (!verdict.valid) {
  console.error('chain break at', verdict.first_break);
}

Each entry carries layer, action,outcome, duration_ms, plus the hash-chain fields. Cascade revokes write onepassport.revoke entry plus onepassport.revoke_cascade per child — filter bypassportJti to see the chain for a single passport.

Offline passport verification

The SDK exports verifyPassportOffline as a standalone function (not a method on Stack). It verifies the JWT signature using only the public JWKS - it never calls the STACK API. Ideal for downstream services that see thousands of passport-gated requests per second and only need cryptographic validity plus expiry checks.

typescript
import { verifyPassportOffline } from '@getstackrun/sdk';

// Mode 1: JWKS URL - fetched + cached once per URL per process.
const claims = await verifyPassportOffline(token, {
  jwksUrl: 'https://api.getstack.run/v1/.well-known/jwks.json',
});

// Mode 2: pre-fetched JWK - fully air-gapped; no network at verify time ever.
const claims2 = await verifyPassportOffline(token, {
  publicJwk: {
    kty: 'OKP', crv: 'Ed25519', x: '...',
    kid: 'stack-passport-v1', alg: 'EdDSA', use: 'sig',
  },
});

console.log(claims.jti, claims.stk.agent_id, claims.stk.services);

Offline mode checks signature + issuer + audience + expiry. It does NOT consult revocation state - if you need revocation-aware verdicts, combine with a short cache against POST /v1/passports/verify or subscribe to thestack:revoked pub/sub channel.

Error Handling

typescript
import { Stack, StackError, NotFoundError } from '@getstackrun/sdk';

try {
  const agent = await stack.agents.get('agt_nonexistent');
} catch (err) {
  if (err instanceof NotFoundError) {
    console.log('Agent not found');
  } else if (err instanceof StackError) {
    console.log(err.code, err.status, err.message);
  }
}

Not wrapped (call via fetch)

A handful of endpoints are not yet wrapped by Stack. Use the HTTP API directly with the same API key. See/docs/api/billing for the shapes.

typescript
const headers = { Authorization: `Bearer ${process.env.STACK_API_KEY}` };

// Audit export (full date-range; the SDK's `stack.audit.list` covers tailing)
const exp = await fetch('https://api.getstack.run/v1/audit/export?format=ndjson', { headers });

// Billing balance
const balance = await fetch('https://api.getstack.run/v1/billing/balance', { headers }).then(r => r.json());
stack | docs