> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentscore.com/llms.txt
> Use this file to discover all available pages before exploring further.

# AgentScore SDK (TypeScript)

> Official TypeScript/Node.js client for AgentScore.

## Installation

```bash theme={"dark"}
npm install @agent-score/sdk
```

## Quick start

```typescript theme={"dark"}
import { AgentScore } from "@agent-score/sdk";

const client = new AgentScore({ apiKey: process.env.AGENTSCORE_API_KEY });

// On-the-fly assessment with decision
const assessment = await client.assess("0xdb5aa553feeb2c3e3d03e8360b36fb0f7e480671", {
  policy: { require_kyc: true },
});

if (assessment.decision === "deny") {
  console.log("Denied:", assessment.decision_reasons);
}
```

## API reference

### Constructor

```typescript theme={"dark"}
new AgentScore(options: {
  apiKey: string;
  baseUrl?: string;   // default: "https://api.agentscore.com"
  timeout?: number;   // default: 10000 (ms)
  userAgent?: string; // custom prefix prepended to default UA, e.g. "myapp/1.0"
})
```

Set `userAgent` to prepend your app's identifier to outbound requests so AgentScore support can trace traffic back to you:

```typescript theme={"dark"}
const client = new AgentScore({ apiKey, userAgent: "myapp/1.0" });
// Outbound UA: "myapp/1.0 (@agent-score/sdk@x.y.z)"
```

### `client.assess(address, options?)`

On-the-fly trust assessment with policy evaluation. Available on every tier, gated by monthly quota.

```typescript theme={"dark"}
// With wallet address
const result = await client.assess("0x1234...", {
  policy: { require_kyc: true },
  chain: "base",
});

// With operator credential (non-wallet agents)
const result = await client.assess(null, {
  operatorToken: "opc_abc123...",
  policy: { require_kyc: true, min_age: 21 },
});

if (result.decision === "deny") {
  console.log("Denied:", result.decision_reasons);
}
```

Returns `AssessResponse` with `decision`, `decision_reasons`, and `identity_method` (`"wallet"` or `"operator_token"`).

### Server-side signer-match + OFAC SDN screening

Pass `signer: { address, network }` to opt into server-side wallet-signer-match and OFAC SDN wallet-address screening in a single round trip:

```typescript theme={"dark"}
const result = await client.assess("0xclaimed...", {
  signer: { address: "0xsigner...", network: "evm" },
  policy: { require_sanctions_clear: true },
});

// Wallet-binding verdict
if (result.signer_match?.kind === "wallet_signer_mismatch") {
  console.log("Mismatch:", result.signer_match.expected_signer, "vs", result.signer_match.actual_signer);
}

// OFAC SDN wallet-address verdict; discriminated union
// { status: 'clear' } | { sanctioned: true, ofac_label, sdn_uid, listed_at } | { status: 'unavailable' }
if (result.signer_sanctions && "sanctioned" in result.signer_sanctions) {
  console.log("OFAC hit:", result.signer_sanctions.ofac_label, result.signer_sanctions.sdn_uid);
}
```

Set `signer.address: null` for rails with no wallet signer (Stripe SPT, card); the response's `signer_match.kind` will be `wallet_auth_requires_wallet_signing`. Wallet-OFAC SDN enforcement on the `signer` block is unconditional whenever a signer is supplied; no `policy.require_sanctions_clear` opt-in required. A `sanctioned: true` OR `status: "unavailable"` verdict flips `decision` to `deny` with `decision_reasons` including `sanctions_flagged` or `sanctions_check_unavailable` respectively (fail-closed; OFAC strict-liability). `policy.require_sanctions_clear` is the separate NAME-based screen on the resolved operator's KYC identity.

## Compliance assessment

Use compliance policy fields to enforce KYC, sanctions, age, and jurisdiction requirements:

```typescript theme={"dark"}
const result = await client.assess("0x1234...", {
  policy: {
    require_kyc: true,
    require_sanctions_clear: true,
    min_age: 21,
    blocked_jurisdictions: ["KP", "IR"],
  },
});

if (result.decision === "deny") {
  console.log("Denied:", result.decision_reasons);
  console.log("Policy checks:", result.policy_result.checks);

  // If denial is resolvable through verification, verify_url is present
  if (result.verify_url) {
    console.log("Verification required:", result.verify_url);
  }
}
```

## Sessions & credentials

```typescript theme={"dark"}
// Create a verification session (for merchant use)
const session = await client.createSession({
  context: "wine_purchase",
  product_name: "Cabernet 2022",
});
// session.verify_url: give to user
// session.poll_url: URL the agent polls
// session.poll_secret: X-Poll-Secret header for the agent's polls

// Registration-only session: the buyer signs in with an AgentScore account, no identity documents.
await client.createSession({ kind: "sign_in" });

// Poll for completion
const poll = await client.pollSession(session.session_id, session.poll_secret);
if (poll.status === "verified") {
  console.log("Credential:", poll.operator_token);
}

// Create a credential directly (if user already KYC'd)
const cred = await client.createCredential({ label: "claude-code", ttl_days: 1 });
console.log("Use this:", cred.credential);

// List and revoke
const list = await client.listCredentials();
await client.revokeCredential(list.credentials[0].id);
```

## Report captured wallets

After an agent paid under a credential, report the signer wallet so AgentScore can build a cross-merchant profile:

```typescript theme={"dark"}
await client.associateWallet({
  operatorToken: "opc_...",
  walletAddress: signerFromPayment, // EIP-3009 `from`, Tempo MPP DID, or Solana pubkey
  network: "evm", // or "solana"
  idempotencyKey: paymentIntentId, // optional, retries of same payment no-op
});
```

Fire-and-forget. Response is `{ associated: true, first_seen: boolean, deduped?: true }`.

## Quota observability

`assess()` responses include an optional `quota` field captured from `X-Quota-Limit` / `X-Quota-Used` / `X-Quota-Reset` headers so callers can monitor approach-to-cap proactively before hitting 429:

```typescript theme={"dark"}
const result = await client.assess("0x1234...", { policy: { require_kyc: true } });
if (result.quota?.limit && result.quota.used != null) {
  const pct = result.quota.used / result.quota.limit;
  if (pct >= 0.95) {
    log.warn(`AgentScore quota at ${Math.round(pct * 100)}%; resets at ${result.quota.reset}`);
  }
}
```

`reset` is the UTC date (`YYYY-MM-DD`) the calendar-month quota resets, e.g. `"2026-06-01"`; every field is `null` when the API sent no quota headers (Enterprise and unlimited plans).

## Error handling

The SDK throws typed errors. Subclasses of `AgentScoreError` let callers branch on `instanceof` without parsing `err.code`:

```typescript theme={"dark"}
import {
  AgentScoreError,
  InvalidCredentialError,
  PaymentRequiredError,
  QuotaExceededError,
  RateLimitedError,
  TimeoutError,
  TokenExpiredError,
} from "@agent-score/sdk";

try {
  const result = await client.assess(null, {
    operatorToken: "opc_...",
    signer: { address: "0xs", network: "evm" },
  });
} catch (err) {
  if (err instanceof TokenExpiredError) {
    // 401 token_expired; credential revoked or TTL-expired. The 401 body carries an
    // auto-minted verification session so the agent recovers without an API key.
    console.log("verify_url:", err.verifyUrl, "session_id:", err.sessionId, "poll_secret:", err.pollSecret);
  } else if (err instanceof InvalidCredentialError) {
    // 401 invalid_credential; operator_token doesn't exist (typo, never minted).
  } else if (err instanceof QuotaExceededError) {
    // 429 quota_exceeded; wait for the quota reset window; don't retry.
  } else if (err instanceof RateLimitedError) {
    // 429 rate_limited; the SDK already waited out Retry-After and retried once, so back off before trying again.
  } else if (err instanceof PaymentRequiredError) {
    // 402; vendor tier misconfig.
  } else if (err instanceof AgentScoreError) {
    // Anything else: 4xx/5xx + network failures wrap to AgentScoreError with code "network_error"/"timeout"/etc.
    console.log(err.code, err.message, err.status);
  }
}
```

On `wallet_signer_mismatch` responses (verdict, not exception), spread `signer_match.agent_instructions` (a JSON-encoded `{action, steps, user_message}` envelope) directly into a 403 body; the SDK never re-derives recovery copy locally.

`AgentScoreError.details: Record<string, unknown>` carries response-body fields beyond `{code, message}`; `verify_url`, `linked_wallets`, `claimed_operator`, `actual_signer`, `expected_signer`, `reasons`, `agent_memory`; so callers can branch on granular denial codes without re-parsing:

```typescript theme={"dark"}
try {
  await client.assess("0xabc...", { policy: { require_kyc: true } });
} catch (err) {
  if (!(err instanceof AgentScoreError)) throw err;
  if (err.code === "wallet_signer_mismatch") {
    const linked = err.details.linked_wallets as string[] | undefined;
    // re-sign from any address in `linked`, or switch to X-Operator-Token
  }
  if (err.code === "token_expired") {
    const verifyUrl = err.details.verify_url as string | undefined;
    // surface verifyUrl to the user so they can re-verify
  }
}
```

## TypeScript types

The SDK exports all response types:

```typescript theme={"dark"}
import type {
  AssessResponse,
} from "@agent-score/sdk";
```

## Links

* [npm](https://www.npmjs.com/package/@agent-score/sdk)
* [GitHub](https://github.com/agentscore/node-sdk)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.