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

# TypeScript SDK

> @atlasent/sdk — Node.js and edge-runtime client for AtlaSent.

## Install

```bash theme={null}
npm install @atlasent/sdk
# or
pnpm add @atlasent/sdk
# or
yarn add @atlasent/sdk
```

Supports Node.js 18+, Bun, Deno, and edge runtimes (Vercel Edge, Cloudflare Workers).

## Hello, world

The canonical execution-boundary surface. `protect()` mints and
verifies a [Permit](/concepts/permit) end-to-end and returns it on
success. There is no allow/deny return discriminator — if `protect()`
resolves, the action is authorized.

```typescript theme={null}
import atlasent, { AtlaSentDeniedError, AtlaSentError } from "@atlasent/sdk";

try {
  const permit = await atlasent.protect({
    agent: "agent-001",
    action: "ehr.write",
    context: { environment: "production", patient_id: "patient-42" },
  });

  // Authorized end-to-end. Execute and persist permit.permitId alongside
  // your own record so the audit chain stays two-way navigable.
  await db.patients.update("patient-42", payload, { atlasentPermitId: permit.permitId });
} catch (err) {
  if (err instanceof AtlaSentDeniedError) halt(err.reason);  // policy denied / verify-fail
  else if (err instanceof AtlaSentError)  halt("fail-closed"); // transport / auth / 5xx
  else throw err;
}
```

See [Runtime flow](/concepts/runtime-flow) for the full lifecycle and
the fail-closed matrix.

## `withPermit()` — lexically-scoped form

When the action body is a single scope, bind it to the permit's
lifetime directly. Same wire contract as `protect()`; same audit-chain
entry. The callback runs **only** on a verified permit.

```typescript theme={null}
import atlasent from "@atlasent/sdk";

const record = await atlasent.withPermit(
  {
    agent: "agent-001",
    action: "ehr.write",
    context: { environment: "production", patient_id: "patient-42" },
  },
  async (permit) => {
    return db.patients.update("patient-42", payload, { atlasentPermitId: permit.permitId });
  },
);
```

`protect()` returns the Permit to the caller; `withPermit()` binds it
to the callback. Pick the form that fits the call site — both produce
identical wire calls, identical audit-chain entries, and identical
fail-closed semantics. Errors thrown inside the callback propagate
untouched; the permit is already consumed by the verify step, so
there is no compensating revoke.

## `requirePermit()` — descriptor form

For dangerous operations described by a richer `ProtectedAction`
(`resource_id`, `environment`):

```typescript theme={null}
import atlasent from "@atlasent/sdk";

await atlasent.requirePermit(
  {
    action_type: "database.table.drop",
    actor_id: "agent:code-agent",
    resource_id: "prod-db.users",
    environment: "production",
    context: { reversibility: "irreversible", blast_radius: "customer_data" },
  },
  async () => {
    await db.raw("DROP TABLE users");
  },
);
```

## Error model

```typescript theme={null}
import {
  AtlaSentDeniedError,
  AtlaSentError,
} from "@atlasent/sdk";

try {
  const permit = await atlasent.protect({ agent: "...", action: "...", context: {} });
} catch (err) {
  if (err instanceof AtlaSentDeniedError) return halt(err.reason);     // policy / verify
  if (err instanceof AtlaSentError)       return halt("fail-closed");  // transport / auth / 5xx
  throw err;
}
```

`protect()` and `withPermit()` collapse policy denial, hold,
escalate, and verification failure into `AtlaSentDeniedError`.
Inspect `err.decision` (`"deny"` / `"hold"` / `"escalate"`) if you
need to branch.

## Inspecting the four-value decision

`protect()` and `withPermit()` raise on anything other than `allow`.
If you need to inspect `hold` / `escalate` at the call site — for
example, to route to an approval queue — drop down to the raw
`evaluate()` primitive on `AtlaSentClient`:

```typescript theme={null}
import { AtlaSentClient } from "@atlasent/sdk";

const client = new AtlaSentClient({ apiKey: process.env.ATLASENT_API_KEY! });
const result = await client.evaluate({
  action_type: "ehr.write",
  actor_id: "agent-001",
  context: { environment: "production" },
});

switch (result.decision) {
  case "allow":    /* call client.verifyPermitById(...), then execute */ break;
  case "deny":     /* surface result.reason */ break;
  case "hold":     /* wait for the approval webhook */ break;
  case "escalate": /* routed to a human reviewer */ break;
}
```

`evaluate()` does not verify the permit for you. Callers using the
raw primitive must call `verifyPermitById()` before executing — see
[Verify a permit explicitly](/quickstart/verify-permit).

## SCIM provisioning

Manage SCIM 2.0 user and group provisioning via `client.scim`.

```typescript theme={null}
import { AtlaSentClient } from "@atlasent/sdk";

const client = new AtlaSentClient({ apiKey: process.env.ATLASENT_API_KEY! });

// List provisioned users
const page = await client.scim.users.list("org_abc", { count: 50 });
for (const user of page.Resources) {
  console.log(user.userName, user.active);
}

// Provision a new user — schemas injected automatically
const newUser = await client.scim.users.create("org_abc", {
  userName: "alice@example.com",
  active: true,
  name: { givenName: "Alice", familyName: "Smith" },
  emails: [{ value: "alice@example.com", primary: true }],
});

// Deprovision
await client.scim.users.delete("org_abc", newUser.id);
```

Groups follow the same pattern via `client.scim.groups.*`. See [SCIM 2.0 provisioning guide](/enterprise/scim-admin) for IdP configuration.

## Token management

Multi-IdP token refresh via `client.auth`. Use this in programmatic integrations that maintain long-lived access tokens.

```typescript theme={null}
// List available IdP connections
const connections = await client.auth.listIdpConnections();
// [{ id: "idp_okta_prod", name: "Okta Production", provider: "okta", isDefault: true, ... }]

// Refresh using the default IdP
const tokens = await client.auth.refresh(currentRefreshToken);
// { accessToken: "at_...", refreshToken: "rt_...", expiresIn: 3600 }

// Refresh against a specific IdP (multi-IdP orgs)
const tokens = await client.auth.refreshWithIdp("idp_okta_prod", currentRefreshToken);
```

## Evidence bundles

Create and download compliance evidence bundles.

```typescript theme={null}
// Create a bundle
const bundle = await client.evidenceBundles.create("org_abc", {
  incidentId: "inc_001",
  includeOverrides: true,
});

// Poll until ready
let b = bundle;
while (b.status === "generating") {
  await new Promise(r => setTimeout(r, 2000));
  b = await client.evidenceBundles.get("org_abc", b.bundleId);
}

// Download as PDF
const pdfBuffer = await client.evidenceBundles.download("org_abc", b.bundleId, {
  format: "pdf",
});
await fs.writeFile("evidence.pdf", Buffer.from(pdfBuffer));
```

## Links

* Source: [`atlasent-sdk/typescript`](https://github.com/Atlasent/atlasent-sdk/tree/main/typescript)
* Changelog: [`/changelog/sdks#typescript`](/changelog/sdks#typescript)
* Bundle size: 11 KB gzipped (core); 14 KB with audit iterators.


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