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

# Agent SDK

> Constructor, methods, context fields and return types of the Stigg Agent SDK.

<Note>
  The Stigg Agent SDK is in public beta and its API may change before general availability.
</Note>

## Installation

```bash theme={null}
npm install @stigg/agent-sdk-js
```

Requires Node.js 22 or later. The package does not depend on any LLM vendor SDK: install only the vendor clients you use.

## How it connects to Stigg

The Agent SDK builds on two Stigg clients and picks one for each operation. You never call either of them directly.

| Operation | Client | API |
| - | - | - |
| A check that names an agent or other dimensions | `@stigg/typescript` | REST `v1-beta` (Governance) |
| A check with no agent or dimensions | `@stigg/node-server-sdk`, through its entitlement cache | GraphQL |
| Reporting usage | `@stigg/node-server-sdk` | GraphQL |
| `register()` and `deregister()` | `@stigg/typescript` | REST `v1-beta` (Governance) |

On an account without Governance, the SDK uses the Node server SDK alone, at customer level. For caching, see [Caching and accuracy](/documentation/ai-agents/llm-wrappers/gating#caching-and-accuracy). For concepts, see [LLM and gateway wrappers](/documentation/ai-agents/llm-wrappers/overview) and [Agent governance](/documentation/ai-agents/agent-governance).

## StiggAgentSDK

```typescript theme={null}
import { StiggAgentSDK } from '@stigg/agent-sdk-js';

const stigg = new StiggAgentSDK(options?);
const stigg = new StiggAgentSDK(apiKey, options?);   // shorthand
```

Create one instance per Stigg environment and share it. The constructor is synchronous and makes no network calls. The connection to Stigg is set up on the first call that needs it, or up front with [`init()`](#init). If it fails, for example because Stigg is briefly unreachable, a later call tries again after a short wait, so the SDK recovers without a restart.

For example, `new StiggAgentSDK({ apiKey, baseUrl: 'https://stigg.internal', ingest: 'sync' })`. The shorthand takes the API key first and the other options second, for example `new StiggAgentSDK(apiKey, { ingest: 'sync' })`.

<ParamField path="options" type="StiggAgentSDKOptions">
  Optional settings. Every one has a working default.

  <Expandable title="options">
    <ParamField path="apiKey" type="string">
      A Stigg **server** API key. Omit it to read the `STIGG_SERVER_API_KEY` environment variable.
    </ParamField>

    <ParamField path="baseUrl" type="string">
      The Stigg API URL, for an environment other than the hosted API, such as a self-hosted Stigg. With a `baseUrl` and no key in either place, the SDK runs without a key, for example against a local stand-in.
    </ParamField>

    <ParamField path="cache" type="CacheOptions">
      How entitlement checks are cached, passed straight to the Node server SDK, for example `{ redis: { host, port, environmentPrefix } }`. See [Caching and accuracy](/documentation/ai-agents/llm-wrappers/gating#caching-and-accuracy). Cannot be combined with `client`.
    </ParamField>

    <ParamField path="client" type="Stigg">
      A Node server SDK client your application already has, used instead of building a new one. The Agent SDK never closes a client you pass in. Cannot be combined with `cache`.
    </ParamField>

    <ParamField path="checkErrorAction" type="'allow' | 'deny'" default="'allow'">
      What a call does when a check cannot complete, for example because of a network error. See [Fallback strategy](/documentation/ai-agents/llm-wrappers/gating#fallback-strategy).
    </ParamField>

    <ParamField path="ingest" type="'async' | 'sync'" default="'async'">
      How usage is reported by default. See [Ingest modes](/documentation/ai-agents/llm-wrappers/metering#ingest-modes).
    </ParamField>

    <ParamField path="timeoutMs" type="number" default="3000">
      Timeout for requests to Stigg, in milliseconds.
    </ParamField>

    <ParamField path="logger" type="'console' | 'none' | Logger" default="'console'">
      Where the SDK logs what it did. Pass `'none'` to silence it, or your own logger.
    </ParamField>

    <ParamField path="onError" type="(error: EngineError, context: ErrorContext) => void" default="logs a warning">
      Receives every failure the SDK does not throw: failed usage reports, calls whose usage could only partly be measured, and a failed connection to Stigg started in the background by `wrap()` (with `operation: 'start'`). `context` has `operation`, `customerId`, `idempotencyKey` and `runId`.
    </ParamField>

    <ParamField path="cacheTtlMs" type="number">
      Deprecated and ignored. The SDK no longer caches decisions itself, and logs a warning when this is set.
    </ParamField>
  </Expandable>
</ParamField>

## Methods

In the order you typically use them: wrap a client, register an agent, check and report anything a wrapped client does not cover, read the summary, flush before the process exits, and deregister agents you no longer need.

### wrap

```typescript theme={null}
const governed = stigg.wrap(client, options?);
```

Returns a new client that behaves like `client`. Every model call it makes is checked before the call (when the context sets `entitlement`) and its usage is reported after it. The client you passed in is not changed. Synchronous.

Each covered method also accepts a `stiggContext` field in its own parameters, which overrides the client's default for that call. A client wrapped with no `customerId` can serve every customer, and then each call must name its own.

<ParamField path="client" type="object" required>
  The LLM or gateway client to wrap, for example `new Anthropic()`. See [Supported providers](/documentation/ai-agents/llm-wrappers/supported-providers).
</ParamField>

<ParamField path="options" type="ClientOptions">
  <Expandable title="options">
    <ParamField path="stiggContext" type="StiggContext">
      The default [context](#stiggcontext) for every call through this client.
    </ParamField>

    <ParamField path="recipe" type="ClientRecipe">
      The recipe to use instead of automatic recognition. Required for LiteLLM Proxy and for [your own vendors](/documentation/ai-agents/llm-wrappers/providers/custom).
    </ParamField>

    <ParamField path="hooks" type="{ onDecision?, onIngest? }">
      `onDecision` is called with every [decision](#accessdecision) this client makes. `onIngest` is called with every [ingest result](#ingestresult), including failures.
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="governed" type="WrappedClient">
  The wrapped client. It also has a `stigg` property with these members:

  <Expandable title="governed.stigg">
    <ResponseField name="check(context?, requestedUsage?)" type="Promise<AccessDecision>">
      Checks access without making a call, using the client's context plus any overrides. See [check](#check).
    </ResponseField>

    <ResponseField name="report(metrics, context?)" type="Promise<IngestResult>">
      Reports usage this client did not observe. See [report](#report).
    </ResponseField>

    <ResponseField name="summary()" type="SessionSummary">
      What this client has done so far. See [summary](#summary).
    </ResponseField>

    <ResponseField name="flush()" type="Promise<SessionSummary>">
      Waits for this client's pending reports, then returns its summary.
    </ResponseField>

    <ResponseField name="close()" type="Promise<void>">
      Flushes, then releases the client.
    </ResponseField>

    <ResponseField name="headers(context?)" type="GovernanceHeaders">
      The `x-stigg-*` headers for a [gateway you operate](/documentation/ai-agents/llm-wrappers/gateways#a-gateway-you-operate).
    </ResponseField>

    <ResponseField name="lastDecision" type="AccessDecision | undefined">
      The most recent decision this client made.
    </ResponseField>
  </Expandable>
</ResponseField>

Throws if no recipe matches the client, or if the client is already wrapped. A call through the wrapped client throws if its `stiggContext` has a field the SDK does not recognize, such as a misspelled `agentId`, rather than ignore it.

### register

```typescript theme={null}
const agentId = await stigg.register(input);
```

Creates or updates an agent in Governance: its entity and, optionally, its budget and parent. For how to use it, see [Agent governance](/documentation/ai-agents/agent-governance#1-register-the-agent).

<ParamField body="customerId" type="string" required>
  The customer that owns the agent.
</ParamField>

<ParamField body="agentId" type="string" required>
  Your stable ID for the agent.
</ParamField>

<ParamField body="entityTypeId" type="string" required>
  The Governance entity type to create it as, for example `agent`. It must already exist. See [Setting up governance](/documentation/governance/setting-up).
</ParamField>

<ParamField body="displayName" type="string">
  A readable name for the agent, shown in the Stigg app instead of its ID.
</ParamField>

<ParamField body="metadata" type="Record<string, string>">
  Your own key-value data to store on the agent's entity, for example `{ owner: 'research-lead' }`. Values must be strings. See [Entities](/documentation/governance/core-concepts#entities).
</ParamField>

<ParamField body="budget" type="AgentBudget">
  The agent's budget and place in the tree for one credit currency or metered feature (a Governance [assignment](/documentation/governance/core-concepts#assignments)). Omit it to save only the agent's entity.

  <Expandable title="budget">
    <ParamField body="entitlement" type="{ currencyId } | { featureId }" required>
      The credit currency or metered feature the budget is set for.
    </ParamField>

    <ParamField body="limit" type="number | null">
      The budget. A number caps usage, and `null` tracks usage without blocking. Omit it to leave the current value.
    </ParamField>

    <ParamField body="apply" type="'always' | 'on-create'" default="'always'">
      When `limit` is saved. `'always'` saves it on every call. `'on-create'` saves it only if the agent has no budget for this entitlement yet, so a limit an admin changed in the Stigg app is not overwritten on every deploy. With `'on-create'`, `limit` is required and must be a number.
    </ParamField>

    <ParamField body="parentId" type="string | null">
      The parent entity for this entitlement. Omit it to leave the current parent, or pass `null` to make the agent a root.
    </ParamField>

    <ParamField body="cadence" type="string">
      How often the budget resets, as an [ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations): `PT1H`, `P1D`, `P7D` or `P1M`. Required when a budget is created. See [Cadence](/documentation/governance/core-concepts#cadence).
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="agentId" type="Promise<string>">
  The agent ID you passed in.
</ResponseField>

Throws `GovernanceNotEnabledError` when the input sets a `budget` and Governance is not enabled on the account. Without a `budget`, it logs a warning instead and returns.

### check

```typescript theme={null}
const decision = await stigg.check(context, requestedUsage?);
```

Checks whether a customer or agent has access, without making a model call. Use it for actions a wrapped client does not cover, such as a tool call, or to check before an agent starts a task. See [Gating actions that are not model calls](/documentation/ai-agents/llm-wrappers/gating#gating-actions-that-are-not-model-calls).

<ParamField path="context" type="StiggContext" required>
  Who the check is for and what it is checked against. Must include `customerId` and `entitlement`. See [StiggContext](#stiggcontext).
</ParamField>

<ParamField path="requestedUsage" type="number" default="1">
  How much the action is about to use. See [Requested usage](/documentation/ai-agents/llm-wrappers/gating#requested-usage).
</ParamField>

<ResponseField name="decision" type="Promise<AccessDecision>">
  The [decision](#accessdecision). When access is denied, `check()` returns a decision with `hasAccess: false` rather than throwing an `AccessDeniedError`, so you can branch on it directly.
</ResponseField>

### report

```typescript theme={null}
const result = await stigg.report(context, metrics);
```

Reports usage directly. A wrapped client reports the usage of model calls for you. Use this for usage it cannot see, such as the number of searches a tool made.

<ParamField path="context" type="StiggContext" required>
  Who the usage is attributed to. Must include `customerId`. See [StiggContext](#stiggcontext).
</ParamField>

<ParamField path="metrics" type="Record<string, number>" required>
  The usage to report, keyed by Stigg feature or credit currency ID, for example `{ 'feature-web-searches': 1 }`.
</ParamField>

<ResponseField name="result" type="Promise<IngestResult>">
  The [ingest result](#ingestresult). A failed report does not throw: the result has `ok: false`, and the error is passed to `onError`.
</ResponseField>

### summary

```typescript theme={null}
const summary = governed.stigg.summary();
```

A method of each wrapped client, not of the SDK instance. Returns what that client has done so far, without waiting for pending reports. To wait for them first, use [flush](#flush).

<ResponseField name="summary" type="SessionSummary">
  <Expandable title="properties">
    <ResponseField name="checks" type="number">
      How many checks the client made.
    </ResponseField>

    <ResponseField name="ingested" type="number">
      How many usage reports it sent.
    </ResponseField>

    <ResponseField name="ingestFailures" type="number">
      How many usage reports failed, including calls whose usage could only partly be measured, such as a [Gemini call with automatic function calling](/documentation/ai-agents/llm-wrappers/providers/gemini#automatic-function-calling-stream-the-call).
    </ResponseField>

    <ResponseField name="reported" type="Record<string, number>">
      Everything the client reported, summed per feature or currency ID. This is what it sent, not what was billed.
    </ResponseField>

    <ResponseField name="lastDecision" type="AccessDecision | undefined">
      The most recent decision.
    </ResponseField>
  </Expandable>
</ResponseField>

### flush

```typescript theme={null}
await governed.stigg.flush();            // one wrapped client
await stigg.flush({ close: true });      // every client, then release connections
```

Waits for pending usage reports. Reports are sent in the background, so call this before a serverless function, request-scoped worker or CLI exits.

<ParamField path="close" type="boolean" default="false">
  `stigg.flush()` only. After flushing, also release the SDK's connections, like [close](#close).
</ParamField>

<ResponseField name="summaries" type="Promise<SessionSummary[]>">
  `stigg.flush()` returns one [summary](#summary) per wrapped client. `governed.stigg.flush()` returns that client's summary.
</ResponseField>

### deregister

```typescript theme={null}
await stigg.deregister({ customerId, agentId });
```

Archives an agent's entity. Optional for long-lived agents. Deregister ephemeral agents, created for a single task or session, when they finish. See [Agent governance](/documentation/ai-agents/agent-governance#3-deregister-the-agent-optional).

<ParamField body="customerId" type="string" required>
  The customer that owns the agent.
</ParamField>

<ParamField body="agentId" type="string" required>
  The agent to archive.
</ParamField>

### Other methods

#### init

```typescript theme={null}
const resolution = await stigg.init();
```

Connects to Stigg up front instead of on the first call, so a wrong API key fails at startup. Optional. Returns which client serves each operation.

#### close

```typescript theme={null}
await stigg.close();
```

Releases the SDK's connections. A Node server SDK client you passed in as `client` is not closed.

#### denial

```typescript theme={null}
const decision = stigg.denial(err);
```

Returns the [decision](#accessdecision) if `err` is an `AccessDeniedError`, otherwise `undefined`. Use it in a `catch` block to tell denied access apart from other errors. The same as `isAccessDenied(err)`.

#### governanceHeaders

```typescript theme={null}
const headers = stigg.governanceHeaders(context);
```

The `x-stigg-customer-id`, `x-stigg-dimensions`, `x-stigg-attribution-key` and `x-stigg-run-id` headers for a context, with no wrapped client involved. See [A gateway you operate](/documentation/ai-agents/llm-wrappers/gateways#a-gateway-you-operate).

#### ingestFromHeaders

```typescript theme={null}
const result = await stigg.ingestFromHeaders(req.headers, input);
```

For a gateway or proxy you operate. Reads the customer and agent from the `x-stigg-*` request headers and reports the usage the proxy observed. It never checks access.

<ParamField path="headers" type="Record<string, string | string[] | undefined>" required>
  The incoming request's headers.
</ParamField>

<ParamField path="input" type="object" required>
  <Expandable title="input">
    <ParamField path="usage" type="unknown" required>
      The provider's usage object, as it appeared in the response.
    </ParamField>

    <ParamField path="idempotencyKey" type="string" required>
      The upstream request ID, so a retried report is not counted twice.
    </ParamField>

    <ParamField path="vendor" type="'anthropic' | 'openai' | 'openai-responses' | 'gemini' | 'typesafe'">
      The shape of `usage`. Without it, pass a `metrics` mapper.
    </ParamField>

    <ParamField path="entitlement" type="{ currencyId } | { featureId }" default="{ currencyId: 'credits' }">
      What the usage counts against.
    </ParamField>

    <ParamField path="metrics" type="MetricsMapper">
      A [usage mapper](/documentation/ai-agents/llm-wrappers/metering#usage-mappers).
    </ParamField>

    <ParamField path="ingest" type="'async' | 'sync'">
      The [ingest mode](/documentation/ai-agents/llm-wrappers/metering#ingest-modes). Defaults to the SDK's setting.
    </ParamField>

    <ParamField path="event" type="{ name: string }">
      The event name in `async` mode. Defaults to `<vendor>.proxied`.
    </ParamField>

    <ParamField path="onIngest" type="(result: IngestResult) => void">
      Called with the result, including a failed one.
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="result" type="Promise<IngestResult>">
  The [ingest result](#ingestresult).
</ResponseField>

## StiggContext

Says who a call is for and how it is checked and reported. Pass it to `wrap()` as the default for every call through a client, and inside any call's own parameters to override it for that call. Every field is optional in either place, but each call must end up with a `customerId`. A field the SDK does not recognize, such as a misspelled `agentId`, throws an error rather than being ignored.

<ParamField body="customerId" type="string">
  The Stigg customer the call belongs to.
</ParamField>

<ParamField body="entitlement" type="{ currencyId } | { featureId }">
  The credit currency or metered feature the call is checked against. Omit it to meter without checking.
</ParamField>

<ParamField body="agentId" type="string">
  The agent the call is attributed to. Requires `attributionKey`. See [Agent governance](/documentation/ai-agents/agent-governance).
</ParamField>

<ParamField body="attributionKey" type="string">
  The [attribution key](/documentation/governance/core-concepts#entity-types) of the agent's entity type, for example `agentId`. The agent ID is sent under this key on every check and report.
</ParamField>

<ParamField body="dimensions" type="Record<string, string>">
  More attribution, such as a user or a model, sent on every check and report. Merged key by key when overridden.
</ParamField>

<ParamField body="checkErrorAction" type="'allow' | 'deny'">
  Overrides the SDK's [fallback strategy](/documentation/ai-agents/llm-wrappers/gating#fallback-strategy).
</ParamField>

<ParamField body="usageMapper" type="(measurement: Measurement) => Record<string, number>">
  What to report. Defaults to the vendor's own metrics. See [Usage mappers](/documentation/ai-agents/llm-wrappers/metering#usage-mappers).
</ParamField>

<ParamField body="ingestMode" type="'async' | 'sync'">
  Overrides the SDK's [ingest mode](/documentation/ai-agents/llm-wrappers/metering#ingest-modes).
</ParamField>

<ParamField body="eventName" type="{ name: string }">
  The event name in `async` mode. Defaults to the vendor call, such as `anthropic.messages.create`.
</ParamField>

<ParamField body="idempotencyKey" type="string">
  Deduplicates the report. Defaults to the vendor's request ID when it has one.
</ParamField>

<ParamField body="correlationId" type="string">
  Sent as `x-stigg-run-id`, to correlate work across processes.
</ParamField>

## AccessDecision

What a check returns: from `check()`, from `stigg.denial(err)`, and to the `onDecision` hook.

<ResponseField name="hasAccess" type="boolean">
  Whether access is granted and the call may go ahead. The same as `hasAccess` in an [entitlement check](/guides/quick-start-guides/gating-access-to-features#1-check).
</ResponseField>

<ResponseField name="deniedBy" type="string | undefined">
  The entity whose limit denied access: an agent, a team, or the customer.
</ResponseField>

<ResponseField name="reason" type="string | undefined">
  Why access was denied, such as `BudgetExceeded` or `InsufficientCredits`.
</ResponseField>

<ResponseField name="remedy" type="'top-up' | 'raise-budget' | 'subscribe' | 'retry' | undefined">
  What would let the call through. See [Remedies are product decisions](/documentation/ai-agents/llm-wrappers/gating#remedies-are-product-decisions).
</ResponseField>

<ResponseField name="remaining" type="number | undefined">
  The smallest budget left anywhere in the chain.
</ResponseField>

<ResponseField name="usageLimit" type="number | null | undefined">
  The limit of the budget that decided.
</ResponseField>

<ResponseField name="currentUsage" type="number | undefined">
  The usage of the budget that decided.
</ResponseField>

<ResponseField name="source" type="'rest' | 'node-sdk' | 'cache' | 'policy' | 'unenforced'">
  What produced the decision. `policy` means the check could not complete and the [fallback strategy](/documentation/ai-agents/llm-wrappers/gating#fallback-strategy) decided. `unenforced` means no `entitlement` was set.
</ResponseField>

<ResponseField name="checkedAt" type="number">
  When the check was made, in epoch milliseconds.
</ResponseField>

<ResponseField name="isFallback" type="boolean | undefined">
  Stigg answered from fallback state.
</ResponseField>

<ResponseField name="locallyDerived" type="boolean | undefined">
  The SDK made the decision from live data, not Stigg. For a credit check, Stigg returns the balance and the SDK compares it with the requested usage. Can be `true` together with `isFallback`.
</ResponseField>

<ResponseField name="message" type="string">
  A sentence to show the user, matched to the remedy.
</ResponseField>

<ResponseField name="summary" type="string">
  A one-line description for logs.
</ResponseField>

## IngestResult

What a usage report returns.

<ResponseField name="ok" type="boolean">
  `true` only if every metric was accepted.
</ResponseField>

<ResponseField name="reported" type="Record<string, number>">
  What was reported, by feature or currency ID.
</ResponseField>

<ResponseField name="idempotencyKey" type="string">
  The key the report was deduplicated on.
</ResponseField>

<ResponseField name="mode" type="'async' | 'sync'">
  Which [ingest mode](/documentation/ai-agents/llm-wrappers/metering#ingest-modes) was used.
</ResponseField>

<ResponseField name="replayed" type="boolean | undefined">
  `sync` only: Stigg had already seen these keys, so nothing was counted again.
</ResponseField>

<ResponseField name="creditsConsumed" type="number | undefined">
  `sync` only: the credits the report used, when what it reported draws a credit balance. Not set when the reported features are not mapped to credits.
</ResponseField>

<ResponseField name="error" type="unknown">
  The failure, when `ok` is `false`.
</ResponseField>

## Helpers

| Export | Description |
| - | - |
| `anthropicRecipe`, `openAIRecipe`, `geminiRecipe`, `geminiChatRecipe`, `groqRecipe`, `mistralRecipe`, `ollamaRecipe`, `typesafeRecipe`, `openrouterRecipe(options)`, `litellmRecipe(options)`, `bedrockRecipe(options)` | The built-in recipes. See [Supported providers](/documentation/ai-agents/llm-wrappers/supported-providers). |
| `weightedMetrics(recipe, target, weights, { per })` | A mapper that converts metrics into one feature or currency using a rate table. Does not round. |
| `unitsPerTokens(target, tokensPerUnit)` | A mapper that reports one unit per N tokens of any kind. Does not round. Replaces `creditsPerTokens`. |
| `normalizedMetrics` | A mapper that reports four non-overlapping token counters under `stigg_*` names. |
| `estimateFromMaxTokens(tokensPerUnit)`, `estimateFromMaxOutputTokens(tokensPerUnit)`, `estimateFromState(tokensPerUnit, charsPerToken)` | Estimates of a call's usage for Anthropic, Gemini and TypeSafe, used as the [requested usage](/documentation/ai-agents/llm-wrappers/gating#requested-usage) of the check. Each throws when created if a rate is zero or negative. |
| `GOVERNANCE_HEADERS`, `parseGovernanceHeaders(headers)` | The header names, and a parser for the receiving side. |

## Errors

| Error | When |
| - | - |
| `AccessDeniedError` | Access was denied for a call through a wrapped client. Its `decision` property is the [decision](#accessdecision). |
| `GovernanceNotEnabledError` | `register()` set a `budget` on an account without Governance. |
| `EngineError` | A failure talking to Stigg. Failed usage reports are passed to `onError` instead of being thrown. |

`isAccessDenied(err)` returns the decision for an `AccessDeniedError` and `undefined` otherwise. `isGovernanceNotEnabled(err)` and `isEngineError(err)` check the other two. They work without `instanceof`, so they are safe across bundles.


## Related topics

- [Agent SDK](/api-and-sdks/references/sdk/backend/graphql/agent-sdk.md)
- [Bring your own vendor](/documentation/ai-agents/llm-wrappers/providers/custom.md)
- [JavaScript](/api-and-sdks/changelog/agent-sdk/javascript.md)
- [Backend](/api-and-sdks/references/sdk/backend/rest/index.md)
- [Gating calls](/documentation/ai-agents/llm-wrappers/gating.md)


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