> ## 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.

# Set up governance via the SDK

This page covers the catalog setup you make as the vendor through the Stigg TypeScript SDK. Most of these are one-time or infrequent operations. The [Check and Ingest](https://docs.stigg.io/documentation/governance/check-and-ingest.md) calls happen per request at runtime.

**Prerequisites:** a metered feature in a plan the customer is subscribed to, or a credit currency the customer has credits in. A feature check for a customer with no active subscription is denied with `NoActiveSubscription`.

## Core setup

**1. Install and initialize the client**

Install the Stigg TypeScript client and initialize it with your server-side API key.

```bash theme={null}
npm install @stigg/typescript
```

```typescript theme={null}
import Stigg from '@stigg/typescript';

const client = new Stigg({
  apiKey: process.env.STIGG_API_KEY,
});
```

All examples in the governance docs use this `client` instance.

**2. Define entity types**

Entity types are the categories of resource you want to govern, such as `department`, `team`, `user`, and `agent`. Define them once per environment. The `attributionKeys` are the dimension keys your ingest events carry to identify instances of each type.

```typescript theme={null}
const response = await client.v1.entityTypes.upsert({
  types: [
    { id: 'department', displayName: 'Department', attributionKeys: ['departmentId'] },
    { id: 'team',       displayName: 'Team',       attributionKeys: ['teamId'] },
    { id: 'user',       displayName: 'User',       attributionKeys: ['userId'] },
    { id: 'agent',      displayName: 'AI agent',   attributionKeys: ['agentId'] },
  ],
});
```

The upsert is **idempotent**, so re-submitting the same payload is safe. You can send up to 100 types per request.

> [accordion] Example response
>
> ```json theme={null}
> {
>   "data": [
>     {
>       "id": "department",
>       "displayName": "Department",
>       "description": "A department in the customer's organization",
>       "attributionKeys": ["departmentId"],
>       "archivedAt": null,
>       "createdAt": "2026-01-15T10:00:00.000Z",
>       "updatedAt": "2026-01-15T10:00:00.000Z"
>     }
>   ]
> }
> ```

**3. Provision entities per customer**

When a customer creates a department, team, or user in your product, provision the matching entity in governance. Entities belong to a Stigg customer, so pass the customer ID as the first argument.

```typescript theme={null}
const response = await client.v1.customers.entities.upsert('cus-acme', {
  entities: [
    { id: 'engineering',       entityTypeId: 'department', displayName: 'Engineering', metadata: { costCenter: 'cc-100' } },
    { id: 'platform',          entityTypeId: 'team',       displayName: 'Platform' },
    { id: 'mobile',            entityTypeId: 'team',       displayName: 'Mobile' },
    { id: 'support-assistant', entityTypeId: 'agent' },
  ],
});
```

* You can send up to 100 entities per request. The upsert is idempotent, so it's safe to re-run during re-syncs.
* Keys in `metadata` merge over existing values. Set a key to `""` to delete it. Omit `metadata` to leave the stored metadata unchanged.

> [accordion] Example response
>
> ```json theme={null}
> {
>   "data": [
>     {
>       "id": "engineering",
>       "displayName": "Engineering",
>       "entityTypeId": "department",
>       "metadata": { "costCenter": "cc-100" },
>       "archivedAt": null,
>       "createdAt": "2026-01-15T10:00:00.000Z",
>       "updatedAt": "2026-01-15T10:00:00.000Z"
>     }
>   ]
> }
> ```

**4. Create entity entitlements**

An entity entitlement sets the usage limit for an entity on a feature or credit. Entity entitlements appear as columns in the customer's [Governance tab](https://docs.stigg.io/documentation/governance/setting-up/stigg-app.md#view-and-manage-entities-for-a-customer) in the Stigg app. Your customers usually create them through an in-app interface you build, but you can also set them in code.

```typescript theme={null}
const response = await client.v1.customers.entities.entitlements.upsert('cus-acme', {
  entityEntitlements: [
    { entityId: 'engineering', featureId: 'feature-ai-tokens', usageLimit: 1_000_000, cadence: 'P1M' },
    { entityId: 'platform',    featureId: 'feature-ai-tokens', parentId: 'engineering', usageLimit: 200_000, cadence: 'P1M' },
  ],
});
```

* Set either `featureId` for a feature budget or `currencyId` for a credit budget, never both.
* An upsert updates the existing entity entitlement with the same `entityId`, `featureId` or `currencyId`, and `scopeEntityIds`. Any other combination creates a new one.
* On re-upsert, omitting `usageLimit` or `cadence` keeps the existing value. On first create, both are required.
* Set `usageLimit` to `null` to track usage without a limit. Usage still counts and appears in check responses, but never blocks. This is useful for observing usage before you enforce it.

> [accordion] Example response
>
> ```json theme={null}
> {
>   "data": [
>     {
>       "id": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
>       "entityId": "engineering",
>       "featureId": "feature-ai-tokens",
>       "scopeEntityIds": [],
>       "usageLimit": 1000000,
>       "cadence": "P1M",
>       "parentId": null,
>       "createdAt": "2026-01-15T10:00:00.000Z",
>       "updatedAt": "2026-01-15T10:00:00.000Z"
>     }
>   ]
> }
> ```

## Place entities in a hierarchy

The `parentId` field places an entity under a parent in the governance hierarchy. Check evaluates every budget on the path from the entity up to the root, and grants a request only when all of them allow it.

`parentId` is sent on an entity entitlement, but the parent belongs to the entity. An entity has one parent across all its features and credit currencies.

> [!NOTE]
>
> `parentId` has three states. **Omitting** it leaves the current parent unchanged, and new entities start at the root. **`null`** detaches the entity to the root. An **entity ID** sets or changes the parent. Only leaf entities can move, so an entity with children stays put.

## Add dimension-scoped sub-budgets

Set `scopeEntityIds` to add a tighter budget for a specific context. The scoped budget applies only when every listed entity is in the request's resolved set.

```typescript theme={null}
await client.v1.customers.entities.entitlements.upsert('cus-acme', {
  entityEntitlements: [

    // Node-wide budget, always applies
    { entityId: 'platform', featureId: 'feature-ai-tokens', scopeEntityIds: [],         usageLimit: 200_000, cadence: 'P1M' },

    // Applies only when gpt-4o is in the request context
    { entityId: 'platform', featureId: 'feature-ai-tokens', scopeEntityIds: ['gpt-4o'], usageLimit: 10_000,  cadence: 'P1M' },
  ],
});
```

## List and manage

### List entity types

```typescript theme={null}
for await (const entityType of client.v1.entityTypes.list()) {
  console.log(entityType.id, entityType.displayName);
}
```

### List entities

```typescript theme={null}
// All active entities for the customer
for await (const entity of client.v1.customers.entities.list('cus-acme')) {
  console.log(entity.id, entity.entityTypeId);
}

// Filter by entity type
for await (const team of client.v1.customers.entities.list('cus-acme', { entityTypeId: 'team' })) {
  console.log(team.id);
}
```

### Get a single entity

```typescript theme={null}
const response = await client.v1.customers.entities.retrieve('engineering', { id: 'cus-acme' });
```

### Archive entities

Archiving is a soft delete. The entity drops out of the list but you can still fetch it by ID. To restore it, upsert it again.

```typescript theme={null}
await client.v1.customers.entities.archive('cus-acme', { ids: ['old-project'] });
```

The response lists the archived IDs: `{ data: { ids: ['old-project'] } }`.

### List entity entitlements

```typescript theme={null}
// All entity entitlements for the customer
for await (const entitlement of client.v1.customers.entities.entitlements.list('cus-acme')) {
  console.log(entitlement.entityId, entitlement.featureId, entitlement.usageLimit);
}

// Filter by entity
for await (const entitlement of client.v1.customers.entities.entitlements.list('cus-acme', { entityId: 'platform' })) {
  console.log(entitlement);
}

// Filter by credit currency (use featureId for a feature)
for await (const entitlement of client.v1.customers.entities.entitlements.list('cus-acme', { currencyId: 'currency-ai-tokens' })) {
  console.log(entitlement);
}
```

## What's next

> [card] [Check and ingest](https://docs.stigg.io/documentation/governance/check-and-ingest.md)
>
> Gate access before consumption and record usage after

> [card] [Query the governance tree](https://docs.stigg.io/documentation/governance/query.md)
>
> Fetch usage and budget data for dashboards and admin UIs


## Related topics

- [Set up governance via the Stigg app](https://docs.stigg.io/documentation/governance/setting-up/stigg-app.md)
- [Set up governance via the REST API](https://docs.stigg.io/documentation/governance/setting-up/rest-api.md)
- [Usage governance](https://docs.stigg.io/documentation/ai-agents/usage-governance.md)
- [Set up the integration](https://docs.stigg.io/documentation/native-integrations/crm/salesforce/salesforce-setup.md)
- [Welcome to Stigg](https://docs.stigg.io/documentation/getting-started/welcome-to-stigg.md)


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