Connect Your Agent

SDK Quickstart

Go from zero to an agent that asks AgentOps for permission before paying — in your own TypeScript. Two credentials, one package, no new auth to learn.

Prerequisites

  • An AgentOps organisation with a funded Casper float and an org guard policy — see the Quickstart under Getting Started.
  • Your AgentOps API base URL, shown in the console.
  • An operator key (sk_...) — issued from the console under Settings.
  • Node.js 18+ (the SDK uses the global fetch and is ESM-only).

1. Install

bash
npm install @agops-labs/sdk@0.2.0

2. Provision an agent (operator key)

Use your sk_ key to create an agent. You get back the agent’s id and its own ag_ key — hand that key to the agent process.

ts
import { createAgentOpsClient } from '@agops-labs/sdk';

const operator = createAgentOpsClient({
  baseUrl: 'https://<your-agentops-host>',
  apiKey: process.env.AGENTOPS_OPERATOR_KEY!, // sk_live_...
});

const { agent, api_key } = await operator.createAgent({ name: 'Research Agent' });
// agent.id  → 'agt_...'
// api_key   → 'ag_live_...'  (the agent's own credential)

NOTE

Set the agent’s policy (spend cap, per-transaction max, service allowlist, velocity limit) once, from the console or the policy route. Everything below is enforced against it.

3. Pay for a service (agent key)

The shortest path is createGuardedFetch — a drop-in fetch that runs the whole x402 handshake for you: call the service, catch the 402, check policy, attach the signed header, retry. You write one call instead of five steps.

ts
import { createGuardedFetch, PaymentDeniedError } from '@agops-labs/sdk';

const guardedFetch = createGuardedFetch({
  baseUrl: 'https://<your-agentops-host>',
  apiKey: process.env.AGENTOPS_AGENT_KEY!, // ag_live_...
  agentId: agent.id,                       // must be this key's agent
  onDecision: (d) => console.log('decision', d.decisionId), // optional audit hook
});

const res = await guardedFetch('https://svc.example/risk-oracle/score', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ pair: 'CSPR-USDC', side: 'buy', size_motes: '1000000000' }),
});
const data = await res.json(); // the paid 200

Responses that are not 402 pass through untouched, so this is safe as the only fetch in an agent — free endpoints and errors behave exactly as the platform fetch does. When policy refuses, you get a typed error instead of a confusing 402:

ts
try {
  await guardedFetch(SERVICE_URL);
} catch (err) {
  if (err instanceof PaymentDeniedError) {
    console.log(err.reason);     // 'spend_cap_exceeded', 'service_not_allowed', ...
    console.log(err.decisionId); // audit id for this refusal
  }
}

NOTE

PaymentDeniedError means policy worked correctly. PaymentFlowError is the different case where the call never reached policy — a bad key, a tenant mismatch, or a malformed challenge. Idempotency keys are generated per payment automatically.

4. Or drive the steps yourself

Prefer explicit control — or authorizing a cspr-trade / casper-deploy action rather than paying for an HTTP resource? Construct a client with the ag_ key and call authorize directly. By default it is synchronous — the ALLOW / DENY body comes straight back.

ts
const asAgent = createAgentOpsClient({
  baseUrl: 'https://<your-agentops-host>',
  apiKey: process.env.AGENTOPS_AGENT_KEY!, // ag_live_...
});

const result = await asAgent.authorize({
  kind: 'x402-payment',
  agentId: agent.id,
  idempotencyKey: 'req-42',        // makes retries safe
  paymentRequired: rawBodyFrom402, // the API's 402 response body, verbatim
});

if (result.outcome === 'DENY') {
  console.log('blocked:', result.reason); // human-readable, e.g. "Spend cap exceeded…"
} else {
  // result.payment_header = { name, value } — attach it and retry the paid request
}

Async opt-in

Prefer not to block? Ask for a pollable decision id instead, then read it later with the operator client.

ts
const { decisionId } = await asAgent.authorize(sameInput, { async: true });
const status = await operator.getDecision(decisionId); // { outcome, status, ... }

5. What happens on the server

Every authorize call runs the full policy firewall before anything is signed:

  • bearer auth → idempotency → kill-switch → action kind → network
  • service allowlist → asset type → per-transaction max
  • spend cap (atomic) → velocity limit → sign

On ALLOW, a background worker reads settlement from Casper and anchors a SHA-256 fingerprint of the decision into the on-chain GuardRegistry — a tamper-proof receipt.

HEADS UP

One client instance uses one API key. The server scopes sk_ (operator) and ag_ (agent) routes separately — construct two clients if your code needs both.