> ## Documentation Index
> Fetch the complete documentation index at: https://velt-v6-0-0-beta-6.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Node SDK for self-hosting and REST API calls

> Integrate the Velt Node SDK in Node.js or TypeScript to self-host data with MongoDB and S3, or call Velt's REST APIs from your backend.

## Overview

The [Velt Node SDK](https://www.npmjs.com/package/@veltdev/node) exposes two independent backends:

| Backend      | Namespace           | Use case                                         |
| ------------ | ------------------- | ------------------------------------------------ |
| Self-hosting | `sdk.selfHosting.*` | Store Velt data in your own MongoDB + AWS S3     |
| REST API     | `sdk.api.*`         | Call Velt's REST APIs directly from your backend |

**Self-hosting backend** (`sdk.selfHosting.*`) simplifies backend implementation by **90%**. Instead of writing custom database queries and storage logic, you:

1. Pass your DB and storage configs to the SDK
2. Call the relevant SDK method with the raw request payload
3. Return the resulting response directly to the client

**REST API backend** (`sdk.api.*`) provides parity with [Velt's REST APIs](/api-reference/rest-apis/v2/organizations/get-organizations-v2). 18 services, fully-typed TypeScript request objects, and raw Velt API responses. API calls do not read or write MongoDB or AWS S3.

<Note>
  In `@veltdev/node@1.0.7`, `VeltSDK.initialize` still validates the top-level
  `database` config. Pass your database config during initialization even when
  you only call `sdk.api.*`; the REST API methods themselves do not use it.
</Note>

## Installation

```bash theme={null}
npm install @veltdev/node
```

For self-hosting, also install the optional peer dependencies you plan to use:

```bash theme={null}
npm install mongodb              # required for self-hosting backend
npm install @aws-sdk/client-s3   # required only if using S3 for attachments
npm install jose@^5              # required only for the built-in JWT/JWKS verifyToken path
```

### Requirements

* Node.js 18+
* TypeScript 5.x (optional — JavaScript is fully supported)
* MongoDB 6+ (Percona Server or MongoDB Atlas) for self-hosting
* `mongodb` ^6, `@aws-sdk/client-s3` ^3, and `jose` ^5 (for [`verifyToken`](#verifytoken)) as optional peer dependencies

## Quick Start

### Initialize the SDK

<Tabs>
  <Tab title="Self-hosting">
    Self-hosting initialization (MongoDB + optional AWS):

    ```ts theme={null}
    import { VeltSDK } from '@veltdev/node';

    const sdk = VeltSDK.initialize({
      database: {
        host: 'localhost:27017',
        username: 'your-username',
        password: 'your-password',
        auth_database: 'admin',
        database_name: 'velt-integration',
      },
      apiKey: 'YOUR_VELT_API_KEY',
      authToken: 'YOUR_VELT_AUTH_TOKEN'
    });
    ```
  </Tab>

  <Tab title="REST API">
    REST API service initialization:

    ```ts theme={null}
    import { VeltSDK } from '@veltdev/node';

    const sdk = VeltSDK.initialize({
      database: {
        host: 'localhost:27017',
        username: 'your-username',
        password: 'your-password',
        auth_database: 'admin',
        database_name: 'velt-integration',
      },
      apiKey: 'YOUR_VELT_API_KEY',
      authToken: 'YOUR_VELT_AUTH_TOKEN'
    });

    // All sdk.api.* services are now available
    const result = await sdk.api.organizations.getOrganizations({ organizationIds: ['org-123'] });
    ```

    Set `VELT_API_KEY` and `VELT_AUTH_TOKEN` environment variables as an alternative to passing credentials in the config object.
  </Tab>
</Tabs>

### Shutdown

Call `await sdk.close()` when your process exits to release the database connection pool.

## Configuration

### Environment Variables

| Variable                    | Config key equivalent | Purpose                                              |
| --------------------------- | --------------------- | ---------------------------------------------------- |
| `VELT_API_KEY`              | `apiKey`              | Velt API key for authenticating REST API calls       |
| `VELT_AUTH_TOKEN`           | `authToken`           | Velt auth token for authenticating REST API calls    |
| `VELT_WORKSPACE_ID`         | —                     | Default workspace ID for workspace-scoped operations |
| `VELT_WORKSPACE_AUTH_TOKEN` | —                     | Auth token scoped to a specific workspace            |
| `AWS_ACCESS_KEY_ID`         | —                     | AWS access key for S3 attachments                    |
| `AWS_SECRET_ACCESS_KEY`     | —                     | AWS secret key for S3 attachments                    |
| `AWS_REGION`                | —                     | AWS region for S3                                    |
| `AWS_S3_BUCKET_NAME`        | —                     | S3 bucket name for attachments                       |
| `AWS_S3_ENDPOINT_URL`       | —                     | Custom S3 endpoint (e.g., for MinIO)                 |

### Self-Hosting Configuration

<Tabs>
  <Tab title="Database">
    Configure MongoDB connection for storing comments, reactions, and user data.

    ```ts theme={null}
    const sdk = VeltSDK.initialize({
      database: {
        host: 'localhost:27017',
        username: 'your-username',
        password: 'your-password',
        auth_database: 'admin',
        database_name: 'velt-db',

        // Optional fields:
        // type: 'mongodb',       // defaults to 'mongodb'
        // use_srv: true,         // use mongodb+srv:// (auto-detected for *.mongodb.net hosts)
      }
    });
    ```
  </Tab>

  <Tab title="AWS (Attachments)">
    Configure AWS S3 for storing file attachments. Alternatively, set the AWS environment variables listed above.

    ```ts theme={null}
    const sdk = VeltSDK.initialize({
      database: {
        host: 'localhost:27017',
        username: 'your-username',
        password: 'your-password',
        auth_database: 'admin',
        database_name: 'velt-db',
      },
      // AWS credentials can also be provided via environment variables
      apiKey: 'YOUR_VELT_API_KEY',
      authToken: 'YOUR_VELT_AUTH_TOKEN'
    });
    ```
  </Tab>

  <Tab title="Complete Example">
    Full configuration with database and credentials.

    ```ts theme={null}
    import { VeltSDK } from '@veltdev/node';

    const sdk = VeltSDK.initialize({
      database: {
        host: 'localhost:27017',
        username: 'your-username',
        password: 'your-password',
        auth_database: 'admin',
        database_name: 'velt-db',
      },
      apiKey: 'YOUR_VELT_API_KEY',
      authToken: 'YOUR_VELT_AUTH_TOKEN'
    });

    // Graceful shutdown
    process.on('SIGTERM', async () => {
      await sdk.close();
      process.exit(0);
    });
    ```
  </Tab>

  <Tab title="Resolver Auth">
    Configure `resolverAuth` to verify the credential the Velt frontend forwards to endpoint-based resolvers. See [`verifyToken`](#verifytoken) for the full reference, error codes, and security guarantees.

    <Note>
      In `@veltdev/node@1.0.7`, `VeltSDK.initialize` still validates the top-level
      `database` config. Pass your self-hosting database config during
      initialization; `verifyToken` itself does not open a database connection.
    </Note>

    ```ts theme={null}
    const sdk = VeltSDK.initialize({
      database: {
        host: 'localhost:27017',
        username: 'your-username',
        password: 'your-password',
        auth_database: 'admin',
        database_name: 'velt-db',
      },
      apiKey: 'YOUR_VELT_API_KEY',
      authToken: 'YOUR_VELT_AUTH_TOKEN',
      resolverAuth: {
        jwt: { algorithms: ['RS256'], jwksUrl: 'https://your-idp.example.com/.well-known/jwks.json' },
        // or: verify: async (token, headers) => claims
      },
    });
    ```
  </Tab>
</Tabs>

## Self-Hosting Backend

Each self-hosting service is loaded asynchronously on first access via `await sdk.selfHosting.getXxx()`. The service is cached after the first call. The `token` service is the exception — it is available as a direct synchronous property via `sdk.selfHosting.token`.

### Comments

Access via `await sdk.selfHosting.getComments()`.

#### `getComments`

* Fetches comments for a document from your MongoDB store.
* Params: [GetCommentsRequest (SH)](/api-reference/sdk/models/data-models#getcommentsrequest-sh-node)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)

```ts theme={null}
const commentsService = await sdk.selfHosting.getComments();
const result = await commentsService.getComments({
  organizationId: 'org-123',
  documentIds: ['doc-1'],
});
```

**Response**

```ts theme={null}
{
  success: true,
  statusCode: 200,
  data: { /* comment annotations keyed by annotationId */ }
}
```

#### `saveComments`

* Saves (creates or updates) comment annotations to MongoDB.
* Params: [SaveCommentsRequest (SH)](/api-reference/sdk/models/data-models#savecommentsrequest-sh-node)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)

```ts theme={null}
const commentsService = await sdk.selfHosting.getComments();
const result = await commentsService.saveComments({
  metadata: { organizationId: 'org-123', documentId: 'doc-1' },
  commentAnnotation: {
    'annotation-1': {
      annotationId: 'annotation-1',
      comments: { '123456': { commentId: '123456', commentText: 'Hello' } },
      metadata: {},
    },
  },
});
```

**Response**

```ts theme={null}
{ success: true, statusCode: 200, data: { saved: true } }
```

> **Resolver contract:** When `saveComments` runs as a save-resolver handler, the inbound `event` may be a [`ResolverActions`](/api-reference/sdk/models/data-models#resolveractions) **or** a [`CommentResolverSaveEvent`](/api-reference/sdk/models/data-models#commentresolversaveevent) member (or a raw string), and the request may include `targetComment?: PartialComment` — the comment the action occurred on (request context only, never persisted). See [`SaveCommentResolverRequest`](/api-reference/sdk/models/data-models#savecommentresolverrequest).

#### `deleteComment`

* Deletes a single comment annotation from MongoDB.
* Params: [DeleteCommentRequest (SH)](/api-reference/sdk/models/data-models#deletecommentrequest-sh-node)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)

```ts theme={null}
const commentsService = await sdk.selfHosting.getComments();
const result = await commentsService.deleteComment({
  commentAnnotationId: 'annotation-1',
  metadata: { organizationId: 'org-123' },
});
```

**Response**

```ts theme={null}
{ success: true, statusCode: 200, data: { deleted: true } }
```

### Reactions

Access via `await sdk.selfHosting.getReactions()`.

#### `getReactions`

* Fetches reactions for a document from MongoDB.
* Params: [GetReactionsRequest (SH)](/api-reference/sdk/models/data-models#getreactionsrequest-sh-node)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)

```ts theme={null}
const reactionsService = await sdk.selfHosting.getReactions();
const result = await reactionsService.getReactions({
  organizationId: 'org-123',
  documentIds: ['doc-1'],
});
```

**Response**

```ts theme={null}
{ success: true, statusCode: 200, data: { /* reaction annotations */ } }
```

#### `saveReactions`

* Persists reactions to MongoDB.
* Params: [SaveReactionsRequest (SH)](/api-reference/sdk/models/data-models#savereactionsrequest-sh-node)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)
* The reacting user is set via `from` (renamed from `user` in **v1.0.5**, matching the frontend `PartialReactionAnnotation.from`).

```ts theme={null}
const reactionsService = await sdk.selfHosting.getReactions();
const result = await reactionsService.saveReactions({
  metadata: { organizationId: 'org-123', documentId: 'doc-1' },
  reactionAnnotation: {
    'reaction-1': { annotationId: 'reaction-1', icon: 'thumbsup', from: { userId: 'u-1' }, metadata: {} },
  },
});
```

**Response**

```ts theme={null}
{ success: true, statusCode: 200, data: { saved: true } }
```

#### `deleteReaction`

* Deletes a single reaction from MongoDB.
* Params: [DeleteReactionRequest (SH)](/api-reference/sdk/models/data-models#deletereactionrequest-sh-node)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)

```ts theme={null}
const reactionsService = await sdk.selfHosting.getReactions();
const result = await reactionsService.deleteReaction({
  reactionAnnotationId: 'reaction-1',
  metadata: { organizationId: 'org-123' },
});
```

**Response**

```ts theme={null}
{ success: true, statusCode: 200, data: { deleted: true } }
```

### Attachments

Access via `await sdk.selfHosting.getAttachments()`.

#### `getAttachment`

* Fetches an attachment record by organization and attachment ID.
* Params: positional — `organizationId` (string), `attachmentId` (number)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)

```ts theme={null}
const attachmentsService = await sdk.selfHosting.getAttachments();
const result = await attachmentsService.getAttachment('org-123', 12345);
```

**Response**

```ts theme={null}
{
  success: true,
  statusCode: 200,
  data: {
    attachmentId: 12345,
    name: 'document.pdf',
    mimeType: 'application/pdf',
    size: 1024,
    url: 'https://s3.amazonaws.com/...'
  }
}
```

#### `saveAttachment`

* Persists attachment metadata, optionally uploading the file body to S3.
* Params: [SaveAttachmentRequest (SH)](/api-reference/sdk/models/data-models#saveattachmentrequest-sh-node) plus optional positional `fileData` (Buffer), `fileName` (string), `mimeType` (string)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)

```ts theme={null}
const attachmentsService = await sdk.selfHosting.getAttachments();
const result = await attachmentsService.saveAttachment(
  {
    metadata: { organizationId: 'org-123', documentId: 'doc-1' },
    attachment: {
      attachmentId: 12345,
      name: 'document.pdf',
      mimeType: 'application/pdf',
      size: 1024,
    },
  },
  fileBuffer,        // optional Buffer for S3 upload
  'document.pdf',    // optional
  'application/pdf'  // optional
);
```

**Response**

```ts theme={null}
{ success: true, statusCode: 200, data: { saved: true, attachmentId: 12345 } }
```

#### `deleteAttachment`

* Deletes an attachment record (and S3 object, if applicable).
* Params: [DeleteAttachmentRequest (SH)](/api-reference/sdk/models/data-models#deleteattachmentrequest-sh-node)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)

```ts theme={null}
const attachmentsService = await sdk.selfHosting.getAttachments();
const result = await attachmentsService.deleteAttachment({
  attachmentId: 12345,
  metadata: { organizationId: 'org-123' },
});
```

**Response**

```ts theme={null}
{ success: true, statusCode: 200, data: { deleted: true } }
```

### Users

Access via `await sdk.selfHosting.getUsers()`.

#### `getUsers`

* Fetches users from MongoDB.
* Params: [GetUsersSelfHostingRequest (SH)](/api-reference/sdk/models/data-models#getusersselfhostingrequest-sh-node)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)

```ts theme={null}
const usersService = await sdk.selfHosting.getUsers();
const result = await usersService.getUsers({
  organizationId: 'org-123',
  userIds: ['user-1'],
});
```

**Response**

```ts theme={null}
{
  success: true,
  statusCode: 200,
  data: [{ userId: 'user-1', name: 'John Doe', email: 'john@example.com' }]
}
```

### Recorder

Access via `await sdk.selfHosting.getRecorder()`.

#### `getRecorderAnnotations`

* Fetches recorder annotations from MongoDB.
* Params: [GetRecorderAnnotationsRequest (SH)](/api-reference/sdk/models/data-models#getrecorderannotationsrequest-sh-node)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)

```ts theme={null}
const recorderService = await sdk.selfHosting.getRecorder();
const result = await recorderService.getRecorderAnnotations({
  organizationId: 'org-123',
  documentIds: ['doc-1'],
});
```

**Response**

```ts theme={null}
{ success: true, statusCode: 200, data: { /* recorder annotations */ } }
```

#### `saveRecorderAnnotation`

* Saves a single recorder annotation.
* Params: [SaveRecorderAnnotationRequest (SH)](/api-reference/sdk/models/data-models#saverecorderannotationrequest-sh-node)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)

```ts theme={null}
const recorderService = await sdk.selfHosting.getRecorder();
const result = await recorderService.saveRecorderAnnotation({
  metadata: { organizationId: 'org-123', documentId: 'doc-1' },
  recorderAnnotation: { annotationId: 'rec-1', metadata: {} },
});
```

**Response**

```ts theme={null}
{ success: true, statusCode: 200, data: { saved: true } }
```

#### `deleteRecorderAnnotation`

* Deletes a recorder annotation.
* Params: [DeleteRecorderAnnotationRequest (SH)](/api-reference/sdk/models/data-models#deleterecorderannotationrequest-sh-node)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)

```ts theme={null}
const recorderService = await sdk.selfHosting.getRecorder();
const result = await recorderService.deleteRecorderAnnotation({
  recorderAnnotationId: 'rec-1',
  metadata: { organizationId: 'org-123' },
});
```

**Response**

```ts theme={null}
{ success: true, statusCode: 200, data: { deleted: true } }
```

### Notifications

Access via `await sdk.selfHosting.getNotifications()`.

#### `getNotifications`

* Fetches notifications for a user from MongoDB.
* Params: [GetNotificationsSelfHostingRequest (SH)](/api-reference/sdk/models/data-models#getnotificationsselfhostingrequest-sh-node)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)

```ts theme={null}
const notificationsService = await sdk.selfHosting.getNotifications();
const result = await notificationsService.getNotifications({
  organizationId: 'org-123',
  userId: 'user-1',
});
```

**Response**

```ts theme={null}
{ success: true, statusCode: 200, data: [/* notifications */] }
```

#### `saveNotifications`

* Persists one or more notifications to MongoDB.
* Params: [SaveNotificationsRequest (SH)](/api-reference/sdk/models/data-models#savenotificationsrequest-sh-node)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)

```ts theme={null}
const notificationsService = await sdk.selfHosting.getNotifications();
await notificationsService.saveNotifications({
  metadata: { organizationId: 'org-123', documentId: 'doc-1' },
  notifications: [{ id: 'n-1', actionUser: { userId: 'user-1' }, displayBodyMessage: 'Hello' }],
});
```

**Response**

```ts theme={null}
{ success: true, statusCode: 200, data: { saved: true } }
```

#### `deleteNotification`

* Deletes a notification from MongoDB.
* Params: [DeleteNotificationRequest (SH)](/api-reference/sdk/models/data-models#deletenotificationrequest-sh-node)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)

```ts theme={null}
const notificationsService = await sdk.selfHosting.getNotifications();
await notificationsService.deleteNotification({
  notificationId: 'n-1',
  metadata: { organizationId: 'org-123', userId: 'user-1' },
});
```

**Response**

```ts theme={null}
{ success: true, statusCode: 200, data: { deleted: true } }
```

### Activities

Access via `await sdk.selfHosting.getActivities()`.

#### `getActivities`

* Fetches activity logs from MongoDB.
* Params: [GetActivitiesSelfHostingRequest (SH)](/api-reference/sdk/models/data-models#getactivitiesselfhostingrequest-sh-node)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)

```ts theme={null}
const activitiesService = await sdk.selfHosting.getActivities();
const result = await activitiesService.getActivities({
  organizationId: 'org-123',
  documentId: 'doc-1',
});
```

**Response**

```ts theme={null}
{ success: true, statusCode: 200, data: [/* activities */] }
```

#### `saveActivities`

* Persists activity log entries to MongoDB.
* Params: [SaveActivitiesRequest (SH)](/api-reference/sdk/models/data-models#saveactivitiesrequest-sh-node)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)

```ts theme={null}
const activitiesService = await sdk.selfHosting.getActivities();
await activitiesService.saveActivities({
  metadata: { organizationId: 'org-123', documentId: 'doc-1' },
  activities: [{ id: 'activity-1', featureType: 'comment', actionType: 'comment.add' }],
});
```

**Response**

```ts theme={null}
{ success: true, statusCode: 200, data: { saved: true } }
```

### Token

Access via the synchronous `sdk.selfHosting.token` property (no `await`).

#### `getToken`

* Generates a Velt auth token for a user.
* Params: positional — `organizationId` (string), `userId` (string), `email` (string, optional), `isAdmin` (boolean, optional)
* Returns: [VeltSelfHostingResponse](/api-reference/sdk/models/data-models#veltselfhostingresponse-node)

> Note: `getToken` takes positional arguments — not a request object.

```ts theme={null}
const tokenResult = await sdk.selfHosting.token.getToken(
  'org-123',
  'user-1',
  'john@example.com',
  false
);
const token = (tokenResult.data as { token: string }).token;
```

**Response**

```ts theme={null}
{ success: true, statusCode: 200, data: { token: 'eyJhbGciOiJIUzI1NiIs...' } }
```

### verifyToken

`sdk.selfHosting.verifyToken` verifies the auth credential the Velt frontend forwards to endpoint-based resolvers. It is **database-free** (it does not open or query MongoDB), **fail-closed** (every error path returns `{ verified: false }`, never throws), and **authentication only** (claims are returned verbatim; it makes no authorization decision).

<Note>
  In `@veltdev/node@1.0.7`, `VeltSDK.initialize` still validates the top-level
  `database` config before you can access `sdk.selfHosting`. Include your
  self-hosting database config when initializing the SDK. The `verifyToken`
  method itself remains database-free after initialization.
</Note>

> **Optional dependency:** `npm install jose@^5` (optional `peerDependency`, pinned `^5` for Node 18 — jose v6 needs the WebCrypto global absent on Node 18). If `jose` is missing and the built-in JWT path is used, `verifyToken` returns `{ verified: false, errorCode: 'DEPENDENCY_MISSING' }`. The custom `verify` callback path needs no dependency.

#### Configuration

Configure `resolverAuth` on `VeltSDK.initialize`. Provide the built-in `jwt` path, a custom `verify` callback, or both. When both are set, the custom callback takes priority.

<Tabs>
  <Tab title="JWT / JWKS">
    ```ts theme={null}
    const sdk = VeltSDK.initialize({
      database: {
        host: 'localhost:27017',
        username: 'your-username',
        password: 'your-password',
        auth_database: 'admin',
        database_name: 'velt-db',
      },
      apiKey: 'YOUR_VELT_API_KEY',
      authToken: 'YOUR_VELT_AUTH_TOKEN',
      resolverAuth: {
        jwt: {
          algorithms: ['RS256'],        // REQUIRED — algorithm allowlist (pinning)
          jwksUrl: 'https://your-idp.example.com/.well-known/jwks.json',
          // publicKey: '-----BEGIN PUBLIC KEY-----\n...',  // OR a static PEM
          // secret: 'hs256-shared-secret',                 // OR an HMAC secret for HS*
          issuer: 'https://your-idp.example.com/',
          audience: 'your-api-audience',
          leeway: 5,                    // clock tolerance in seconds
          require: ['exp'],             // required claims
        },
        header: 'Authorization',        // default
        scheme: 'Bearer',              // default
      },
    });
    ```
  </Tab>

  <Tab title="Custom verify callback">
    ```ts theme={null}
    const sdk = VeltSDK.initialize({
      database: {
        host: 'localhost:27017',
        username: 'your-username',
        password: 'your-password',
        auth_database: 'admin',
        database_name: 'velt-db',
      },
      apiKey: 'YOUR_VELT_API_KEY',
      authToken: 'YOUR_VELT_AUTH_TOKEN',
      resolverAuth: {
        verify: async (token, headers) => {
          // Validate the token yourself; return the claims, or a falsy value to fail closed.
          const claims = await myValidate(token);
          return claims; // { sub: 'user-1', ... }
        },
        header: 'Authorization',
        scheme: 'Bearer',
      },
    });
    ```
  </Tab>
</Tabs>

#### Usage

Call `verifyToken` from your endpoint-based resolver route, then branch on `result.verified`.

```ts theme={null}
const result = await sdk.selfHosting.verifyToken({ headers: request.headers });

if (!result.verified) {
  // result.error is a generic, code-based message; result.errorCode is machine-readable
  return response.status(401).json({ error: result.error, code: result.errorCode });
}

const userId = result.claims?.sub;
```

Per-call `options` override the configured `resolverAuth` (the `jwt` block is deep-merged):

```ts theme={null}
// Pass a raw token directly instead of headers
await sdk.selfHosting.verifyToken({ token: rawToken });

// Override a single jwt option for this call (deep-merged with configured jwt)
await sdk.selfHosting.verifyToken({ headers: request.headers, jwt: { leeway: 30 } });
```

**Signature**

```ts theme={null}
verifyToken(
  options?: {
    headers?: Record<string, string | string[] | undefined> | Headers | Iterable<[string, string]>;
    token?: string;
  } & Partial<ResolverAuthConfig>
) => Promise<VerifyTokenResult>
```

* Params: inline options object with `headers`, `token`, and per-call [`ResolverAuthConfig`](/api-reference/sdk/models/data-models#resolverauthconfig-node) overrides. This parameter shape is not exported as a named SDK type.
* Returns: [`VerifyTokenResult`](/api-reference/sdk/models/data-models#verifytokenresult-node)

The verification logic is implemented by the `ResolverAuthService` class (also exported from `@veltdev/node`) for advanced callers who need to construct it directly; `sdk.selfHosting.verifyToken` is the standard entry point.

#### Error codes

On failure, `result.errorCode` is one of the [`ResolverAuthErrorCode`](/api-reference/sdk/models/data-models#resolverautherrorcode-node) values (also exported as the readonly `string[]` const `RESOLVER_AUTH_ERROR_CODES`).

| Code                    | When                                                                                                      |
| ----------------------- | --------------------------------------------------------------------------------------------------------- |
| `NOT_CONFIGURED`        | Neither `jwt` nor a `verify` callback is configured.                                                      |
| `MISSING_TOKEN`         | No token found in headers, or wrong scheme.                                                               |
| `EXPIRED`               | JWT `exp` claim has passed (beyond `leeway`).                                                             |
| `INVALID_SIGNATURE`     | Signature verification failed, or malformed token.                                                        |
| `CLAIM_MISMATCH`        | Configured `issuer`/`audience` wrong or absent; or a required claim is missing.                           |
| `ALGORITHM_NOT_ALLOWED` | `alg=none`; algorithm outside the allowlist; mixed symmetric+asymmetric allowlist; missing allowlist.     |
| `KEY_RESOLUTION_FAILED` | No usable key; JWKS URL is not HTTPS; JWKS fetch failed or returned non-2xx; redirect downgrade detected. |
| `DEPENDENCY_MISSING`    | Built-in JWT path used but `jose` is not installed.                                                       |
| `VERIFICATION_FAILED`   | Custom callback threw or returned falsy; unexpected internal error (fail-closed catch-all).               |

#### Security guarantees

* `alg=none` is always rejected, even if present in the allowlist.
* Mixed symmetric+asymmetric allowlists are refused (prevents HS/RS confusion).
* A PEM placed in `jwt.secret` is refused.
* JWKS is fetched only over HTTPS — `http` is rejected pre-fetch, and a redirect downgrade is rejected.
* JWKS responses are cached per URL with a 5-minute TTL (not per `kid`).
* The token-header `alg` is checked against the allowlist before any JWKS fetch.
* The `error` field is generic and code-based — it never contains the token or secret.

## REST API Backend

The `sdk.api.*` namespace gives you parity with [Velt's REST APIs](/api-reference/rest-apis/v2/organizations/get-organizations-v2) across 18 services. Each method takes a fully-typed TypeScript request object and returns the raw Velt API response. API methods need `apiKey` and `authToken` and do not use MongoDB or AWS S3, although `@veltdev/node@1.0.7` still validates the top-level `database` config at initialization.

Every `sdk.api.*` method requires an `organizationId` in its request payload. Velt enforces data isolation per-organization server-side.

### Field Allowlist

The REST add/update methods on `activities`, `commentAnnotations`, and `notifications` accept an optional second argument, `FieldFilterOptions`. Pass `{ filterUnknownFields: true }` to narrow the request to exactly the fields the corresponding Velt backend endpoint accepts. See the note at the top of each service section for the behavior summary; the per-endpoint field lists are below.

```ts theme={null}
interface FieldFilterOptions {
  /** When true, narrow the request to only the fields the Velt backend endpoint
   *  accepts, silently dropping unknown keys. Fail-open. Defaults to false. */
  filterUnknownFields?: boolean;
}
```

#### Exported filter utilities

The field-allowlist module is exported from `@veltdev/node` so advanced callers can reuse the same logic.

```ts theme={null}
// Functions
pickKnownFields<T extends object>(data: T, keys: readonly string[]): Partial<T>;
filterRequest<T extends object>(request: T, spec: FilterSpec): T;

// Types
interface FilterSpec {
  keys: readonly string[];              // allowed top-level keys at this level (everything else dropped)
  arrays?: Record<string, FilterSpec>;  // known array-of-object fields, filtered per item
  objects?: Record<string, FilterSpec>; // known single-object fields, filtered recursively
}
```

* `pickKnownFields(data, keys)` — keeps only own-enumerable keys present in `keys`; values kept by reference (no recursion); non-object/array/null inputs returned unchanged.
* `filterRequest(request, spec)` — applies a `FilterSpec` recursively; never mutates input; fail-open (returns the original request on any error).
* Eight per-method specs are exported: `ADD_ACTIVITIES_SPEC`, `UPDATE_ACTIVITIES_SPEC`, `ADD_COMMENT_ANNOTATIONS_SPEC`, `UPDATE_COMMENT_ANNOTATIONS_SPEC`, `ADD_COMMENTS_SPEC`, `UPDATE_COMMENTS_SPEC`, `ADD_NOTIFICATIONS_SPEC`, `UPDATE_NOTIFICATIONS_SPEC`.

#### Allowlisted fields per endpoint

When `filterUnknownFields: true`, only these keys survive (unknown keys dropped):

| Spec (endpoint)                                                     | Top-level keys                                                                                                                                                                                                                                                                                                                                                                                                                                     | Nested item filtering                                                                                                                                                                                                                                                                                                                                                                                                           |
| ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ADD_ACTIVITIES_SPEC` (`/v2/activities/add`)                        | `organizationId`, `documentId`, `activities`                                                                                                                                                                                                                                                                                                                                                                                                       | `activities[]`: `id`, `featureType`, `actionType`, `actionUser`, `targetEntityId`, `isActivityResolverUsed`, `targetSubEntityId`, `changes`, `entityData`, `entityTargetData`, `displayMessageTemplate`, `displayMessageTemplateData`, `actionIcon`                                                                                                                                                                             |
| `UPDATE_ACTIVITIES_SPEC` (`/v2/activities/update`)                  | `organizationId`, `activities`                                                                                                                                                                                                                                                                                                                                                                                                                     | `activities[]`: `id`, `changes`, `entityData`, `entityTargetData`, `displayMessageTemplate`, `displayMessageTemplateData`, `actionIcon`                                                                                                                                                                                                                                                                                         |
| `ADD_COMMENT_ANNOTATIONS_SPEC` (`/v2/commentannotations/add`)       | `organizationId`, `documentId`, `commentAnnotations`, `verifyUserPermissions`, `createDocument`, `createOrganization`                                                                                                                                                                                                                                                                                                                              | `commentAnnotations[]`: `location`, `annotationId`, `targetElement`, `commentData`, `comments`, `status`, `commentType`, `type`, `assignedTo`, `priority`, `context`, `visibility`, `lastUpdated`, `createdAt`, `pageInfo`, `isPageAnnotation`, `positionX`, `positionY`, `screenWidth`, `screenHeight`, `screenScrollHeight`, `screenScrollTop` — its `comments[]`/`commentData[]` use the annotation comment-data set (below) |
| `UPDATE_COMMENT_ANNOTATIONS_SPEC` (`/v2/commentannotations/update`) | `organizationId`, `documentId`, `annotationIds`, `locationIds`, `userIds`, `updateUsers`, `updatedData`                                                                                                                                                                                                                                                                                                                                            | `updatedData`: `location`, `targetElement`, `from`, `status`, `assignedTo`, `priority`, `context`, `visibility`, `createdAt`, `lastUpdated`                                                                                                                                                                                                                                                                                     |
| `ADD_COMMENTS_SPEC` (`/v2/commentannotations/comments/add`)         | `organizationId`, `documentId`, `annotationId`, `commentData`, `comments`, `verifyUserPermissions`, `createDocument`, `createOrganization`                                                                                                                                                                                                                                                                                                         | `commentData[]`/`comments[]`: comment-data set (below)                                                                                                                                                                                                                                                                                                                                                                          |
| `UPDATE_COMMENTS_SPEC` (`/v2/commentannotations/comments/update`)   | `organizationId`, `documentId`, `annotationId`, `commentIds`, `userIds`, `updatedData`                                                                                                                                                                                                                                                                                                                                                             | `updatedData`: `commentText`, `commentHtml`, `from`, `createdAt`, `taggedUserContacts`, `isCommentResolverUsed`, `isCommentTextAvailable`, `lastUpdated`, `context`, `attachments`                                                                                                                                                                                                                                              |
| `ADD_NOTIFICATIONS_SPEC` (`/v2/notifications/add`)                  | `organizationId`, `documentId`, `notificationId`, `actionUser`, `isNotificationResolverUsed`, `displayHeadlineMessageTemplate`, `displayHeadlineMessageTemplateData`, `displayBodyMessage`, `displayBodyMessageTemplate`, `displayBodyMessageTemplateData`, `notifyUsers`, `notificationSourceData`, `location`, `metadata`, `notifyAll`, `createDocument`, `createOrganization`, `verifyUserPermissions`, `context`, `isCrossOrganizationEnabled` | the notification is the request itself — nested open-typed dicts pass through whole                                                                                                                                                                                                                                                                                                                                             |
| `UPDATE_NOTIFICATIONS_SPEC` (`/v2/notifications/update`)            | `organizationId`, `documentId`, `locationId`, `userId`, `verifyUserPermissions`, `notifications`                                                                                                                                                                                                                                                                                                                                                   | `notifications[]`: `id`, `actionUser`, `displayHeadlineMessageTemplate`, `displayHeadlineMessageTemplateData`, `displayBodyMessage`, `displayBodyMessageTemplate`, `displayBodyMessageTemplateData`, `notifyUsers`, `notificationSourceData`, `location`, `metadata`, `context`, `readByUserIds`, `persistReadForUsers`                                                                                                         |

The comment-annotation specs above reference two comment-data key sets:

* Annotation-level `commentData`/`comments` (`ANNOTATION_COMMENT_DATA_KEYS`): `commentText`, `commentHtml`, `commentId`, `from`, `lastUpdated`, `createdAt`, `taggedUserContacts`, `isCommentResolverUsed`, `isCommentTextAvailable`, `triggerNotification`, `triggerActivities`, `agent` — carries the trigger flags but not `context`/`attachments`.
* Comments-endpoint `commentData`/`comments` (`COMMENT_DATA_KEYS`): `commentText`, `commentHtml`, `commentId`, `from`, `lastUpdated`, `createdAt`, `taggedUserContacts`, `isCommentResolverUsed`, `isCommentTextAvailable`, `context`, `attachments`, `agent` — carries `context`/`attachments` but not the trigger flags.

<Note>
  `UPDATE_NOTIFICATIONS_SPEC` intentionally excludes `isRead`/`isArchived` — they are absent from the backend `UpdateNotificationsSchemaV2` and unsupported by `/v2/notifications/update`, so they are dropped when filtering is on.
</Note>

### Organizations

**Namespace:** `sdk.api.organizations`

#### `addOrganizations`

* Creates one or more organizations.
* Params: [AddOrganizationsRequest](/api-reference/sdk/models/data-models#addorganizationsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.organizations.addOrganizations({
  organizations: [{ organizationId: 'org-123', organizationName: 'My Organization' }],
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Organization(s) added successfully.',
    data: {
      'org-123': { success: true, id: '02cf91e5...', message: 'Added Successfully' }
    }
  }
}
```

#### `getOrganizations`

* Retrieves organizations. Optionally filter by IDs with pagination.
* Params: [GetOrganizationsRequest](/api-reference/sdk/models/data-models#getorganizationsrequest-node)
* Returns: [GetOrganizationsResponse](/api-reference/sdk/models/data-models#getorganizationsresponse-node)

```ts theme={null}
await sdk.api.organizations.getOrganizations({
  organizationIds: ['org-123'],
  pageSize: 10,
  pageToken: 'next-page-token',
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Organization(s) retrieved successfully.',
    data: [
      { id: 'org-123', organizationName: 'Your Organization Name', disabled: false }
    ],
    nextPageToken: 'pageToken'
  }
}
```

#### `updateOrganizations`

* Updates organization properties.
* Params: [UpdateOrganizationsRequest](/api-reference/sdk/models/data-models#updateorganizationsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.organizations.updateOrganizations({
  organizations: [{ organizationId: 'org-123', organizationName: 'Updated Name' }],
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Organization(s) updated successfully.', data: {} } }
```

#### `deleteOrganizations`

* Deletes one or more organizations.
* Params: [DeleteOrganizationsRequest](/api-reference/sdk/models/data-models#deleteorganizationsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.organizations.deleteOrganizations({ organizationIds: ['org-123'] });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Organization(s) deleted successfully.', data: {} } }
```

#### `updateOrganizationDisableState`

* Enables or disables one or more organizations.
* Params: [UpdateOrganizationDisableStateRequest](/api-reference/sdk/models/data-models#updateorganizationdisablestaterequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.organizations.updateOrganizationDisableState({
  organizationIds: ['org-123'],
  disabled: true,
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Organization(s) disable state updated.', data: {} } }
```

### Folders

**Namespace:** `sdk.api.folders`

#### `addFolder`

* Creates one or more folders inside an organization.
* Params: [AddFolderRequest](/api-reference/sdk/models/data-models#addfolderrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.folders.addFolder({
  organizationId: 'org-123',
  folders: [{ folderId: 'folder-1', folderName: 'My Folder' }],
  createOrganization: true,
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Folder(s) added successfully.', data: {} } }
```

#### `getFolders`

* Retrieves folders. Optionally filter by folder ID with depth control.
* Params: [GetFoldersRequest](/api-reference/sdk/models/data-models#getfoldersrequest-node)
* Returns: [GetFoldersResponse](/api-reference/sdk/models/data-models#getfoldersresponse-node)

```ts theme={null}
await sdk.api.folders.getFolders({
  organizationId: 'org-123',
  folderId: 'folder-1',
  maxDepth: 3,
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Folders retrieved successfully.',
    data: [
      {
        folderId: 'folder-1',
        folderName: 'Folder 1',
        organizationId: 'org-123',
        parentFolderId: 'root',
        createdAt: 1738695615706,
        lastUpdated: 1738696287859
      }
    ]
  }
}
```

#### `updateFolder`

* Updates folder properties.
* Params: [UpdateFolderRequest](/api-reference/sdk/models/data-models#updatefolderrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.folders.updateFolder({
  organizationId: 'org-123',
  folders: [{ folderId: 'folder-1', folderName: 'Renamed' }],
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Folder(s) updated successfully.', data: {} } }
```

#### `deleteFolder`

* Deletes a folder.
* Params: [DeleteFolderRequest](/api-reference/sdk/models/data-models#deletefolderrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.folders.deleteFolder({ organizationId: 'org-123', folderId: 'folder-1' });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Folder deleted successfully.', data: {} } }
```

#### `updateFolderAccess`

* Updates access type for one or more folders.
* Params: [UpdateFolderAccessRequest](/api-reference/sdk/models/data-models#updatefolderaccessrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.folders.updateFolderAccess({
  organizationId: 'org-123',
  folderIds: ['folder-1'],
  accessType: 'restricted',
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Folder access updated successfully.', data: {} } }
```

### Documents

**Namespace:** `sdk.api.documents`

#### `addDocuments`

* Creates one or more documents.
* Params: [AddDocumentsRequest](/api-reference/sdk/models/data-models#adddocumentsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.documents.addDocuments({
  organizationId: 'org-123',
  documents: [
    { documentId: 'doc-1', documentName: 'My Document' },
    { documentId: 'doc-2', documentName: 'Another Document' },
  ],
  createOrganization: true,
  folderId: 'folder-1',
  createFolder: true,
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Document(s) added successfully.',
    data: {
      'doc-1': { success: true, message: 'Added Successfully' },
      'doc-2': { success: true, message: 'Added Successfully' }
    }
  }
}
```

#### `getDocuments`

* Retrieves documents with optional filters and pagination.
* Params: [GetDocumentsRequest](/api-reference/sdk/models/data-models#getdocumentsrequest-node)
* Returns: [GetDocumentsResponse](/api-reference/sdk/models/data-models#getdocumentsresponse-node)

```ts theme={null}
await sdk.api.documents.getDocuments({
  organizationId: 'org-123',
  documentIds: ['doc-1'],
  folderId: 'folder-1',
  pageSize: 10,
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Document(s) retrieved successfully.',
    data: [
      { documentName: 'yourDocumentName', disabled: false, accessType: 'public', id: 'yourDocumentId' }
    ],
    pageToken: 'nextPageToken'
  }
}
```

#### `updateDocuments`

* Updates document properties.
* Params: [UpdateDocumentsRequest](/api-reference/sdk/models/data-models#updatedocumentsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.documents.updateDocuments({
  organizationId: 'org-123',
  documents: [{ documentId: 'doc-1', documentName: 'Renamed' }],
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Document(s) updated successfully.', data: {} } }
```

#### `deleteDocuments`

* Deletes one or more documents.
* Params: [DeleteDocumentsRequest](/api-reference/sdk/models/data-models#deletedocumentsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.documents.deleteDocuments({
  organizationId: 'org-123',
  documentIds: ['doc-1', 'doc-2'],
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Document(s) deleted successfully.', data: {} } }
```

#### `moveDocuments`

* Moves documents into a folder.
* Params: [MoveDocumentsRequest](/api-reference/sdk/models/data-models#movedocumentsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.documents.moveDocuments({
  organizationId: 'org-123',
  documentIds: ['doc-1', 'doc-2'],
  folderId: 'target-folder-id',
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Document(s) moved successfully.', data: {} } }
```

#### `updateDocumentAccess`

* Updates access type for one or more documents.
* Params: [UpdateDocumentAccessRequest](/api-reference/sdk/models/data-models#updatedocumentaccessrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.documents.updateDocumentAccess({
  organizationId: 'org-123',
  documentIds: ['doc-1', 'doc-2'],
  accessType: 'organizationPrivate',
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Document access updated successfully.', data: {} } }
```

#### `updateDocumentDisableState`

* Enables or disables one or more documents.
* Params: [UpdateDocumentDisableStateRequest](/api-reference/sdk/models/data-models#updatedocumentdisablestaterequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.documents.updateDocumentDisableState({
  organizationId: 'org-123',
  documentIds: ['doc-1'],
  disabled: true,
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Document(s) disable state updated.', data: {} } }
```

#### `migrateDocuments`

* Starts a document migration job.
* Params: [MigrateDocumentsRequest](/api-reference/sdk/models/data-models#migratedocumentsrequest-node)
* Returns: [MigrateDocumentsResponse](/api-reference/sdk/models/data-models#migratedocumentsresponse-node)

> Note: `migrateDocuments` is asynchronous and returns a `migrationId`. Poll completion with `migrateDocumentsStatus`.

```ts theme={null}
await sdk.api.documents.migrateDocuments({
  organizationId: 'org-123',
  documentId: 'old-doc-id',
  newDocumentId: 'new-doc-id',
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Document migration started successfully.',
    data: { migrationId: 'yourMigrationId' }
  }
}
```

#### `migrateDocumentsStatus`

* Polls the status of a migration job.
* Params: [MigrateDocumentsStatusRequest](/api-reference/sdk/models/data-models#migratedocumentsstatusrequest-node)
* Returns: [MigrateDocumentsStatusResponse](/api-reference/sdk/models/data-models#migratedocumentsstatusresponse-node)

```ts theme={null}
await sdk.api.documents.migrateDocumentsStatus({
  organizationId: 'org-123',
  migrationId: 'migration-1',
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Migration status retrieved successfully.',
    data: { migrationId: 'yourMigrationId', status: 'completed' }
  }
}
```

#### `getDocumentsCount`

* Return a document count for an organization (optionally scoped to a folder).
* Params: [GetDocumentsCountRequest](/api-reference/sdk/models/data-models#getdocumentscountrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.documents.getDocumentsCount({ organizationId: 'org-123' });
await sdk.api.documents.getDocumentsCount({ organizationId: 'org-123', folderId: 'folder-1', excludeFolderDocs: true });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Documents count retrieved successfully.', data: { count: 42 } } }
```

### Users

**Namespace:** `sdk.api.users`

#### `addUsers`

* Adds one or more users to an organization, document, or folder.
* Params: [AddUsersRequest](/api-reference/sdk/models/data-models#addusersrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.users.addUsers({
  organizationId: 'org-123',
  users: [{ userId: 'user-1', name: 'John Doe', email: 'john@example.com', accessRole: 'editor' }],
  createOrganization: true,
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'User(s) added successfully.', data: {} } }
```

#### `getUsers`

* Retrieves users with optional filters and pagination.
* Params: [GetUsersRequest](/api-reference/sdk/models/data-models#getusersrequest-node)
* Returns: [GetUsersResponse](/api-reference/sdk/models/data-models#getusersresponse-node)

```ts theme={null}
await sdk.api.users.getUsers({
  organizationId: 'org-123',
  userIds: ['user-1'],
  documentId: 'doc-1',
  pageSize: 100,
  allDocuments: true,
  groupByDocumentId: true,
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'User(s) retrieved successfully.',
    data: [
      { email: 'userEmail@domain.com', name: 'userName', userId: 'yourUserId' }
    ],
    nextPageToken: 'pageToken'
  }
}
```

#### `updateUsers`

* Updates user properties.
* Params: [UpdateUsersRequest](/api-reference/sdk/models/data-models#updateusersrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.users.updateUsers({
  organizationId: 'org-123',
  users: [{ userId: 'user-1', name: 'Jane Doe', accessRole: 'viewer' }],
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'User(s) updated successfully.', data: {} } }
```

#### `deleteUsers`

* Removes users from an organization.
* Params: [DeleteUsersRequest](/api-reference/sdk/models/data-models#deleteusersrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.users.deleteUsers({ organizationId: 'org-123', userIds: ['user-1'] });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'User(s) deleted successfully.', data: {} } }
```

#### `getUsersCount`

* Count users in an org (optionally scoped to a document or all documents).
* Params: [GetUsersCountRequest](/api-reference/sdk/models/data-models#getuserscountrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.users.getUsersCount({ organizationId: 'org-123', allDocuments: true });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Users count retrieved successfully.', data: { count: 12 } } }
```

#### `getDocUsers`

* Get document-level users (optionally including involved documents).
* Params: [GetDocUsersRequest](/api-reference/sdk/models/data-models#getdocusersrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.users.getDocUsers({ organizationId: 'org-123', userIds: ['user-1'] });
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Document users retrieved successfully.',
    data: [{ userId: 'user-1', name: 'John Doe', email: 'john@example.com' }]
  }
}
```

#### `addUserInvite`

* Create an org/doc/folder user invite.
* Params: [AddUserInviteRequest](/api-reference/sdk/models/data-models#adduserinviterequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.users.addUserInvite({ email: 'invitee@example.com', organizationId: 'org-123', type: 'doc_user', documentId: 'doc-1' });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'User invite created successfully.', data: {} } }
```

#### `respondToUserInvite`

* Accept/reject/withdraw an invite.
* Params: [RespondToUserInviteRequest](/api-reference/sdk/models/data-models#respondtouserinviterequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.users.respondToUserInvite({ email: 'invitee@example.com', organizationId: 'org-123', type: 'doc_user', documentId: 'doc-1', status: 'ACCEPTED', user: { userId: 'user-9', name: 'New User', email: 'invitee@example.com' }, authToken: 'user-auth-token' });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Invite response recorded successfully.', data: {} } }
```

#### `getUserInvites`

* List invites for an org (paged).
* Params: [GetUserInvitesRequest](/api-reference/sdk/models/data-models#getuserinvitesrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.users.getUserInvites({ organizationId: 'org-123' });
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'User invites retrieved successfully.',
    data: [{ email: 'invitee@example.com', type: 'doc_user', status: 'PENDING' }],
    nextPageToken: 'pageToken'
  }
}
```

#### `getUserInvitations`

* List invitations for a specific email (paged, filterable).
* Params: [GetUserInvitationsRequest](/api-reference/sdk/models/data-models#getuserinvitationsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.users.getUserInvitations({ organizationId: 'org-123', email: 'invitee@example.com' });
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'User invitations retrieved successfully.',
    data: [{ organizationId: 'org-123', type: 'doc_user', status: 'PENDING' }],
    nextPageToken: 'pageToken'
  }
}
```

#### `getInvitedPendingUsersCount`

* Count pending invited users by invite type.
* Params: [GetInvitedPendingUsersCountRequest](/api-reference/sdk/models/data-models#getinvitedpendinguserscountrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.users.getInvitedPendingUsersCount({ organizationId: 'org-123', type: 'org_user' });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Pending invited users count retrieved successfully.', data: { count: 3 } } }
```

### User Groups

**Namespace:** `sdk.api.userGroups`

#### `addUserGroups`

* Creates one or more user groups.
* Params: [AddUserGroupsRequest](/api-reference/sdk/models/data-models#addusergroupsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.userGroups.addUserGroups({
  organizationId: 'org-123',
  organizationUserGroups: [{ groupId: 'engineering', groupName: 'Engineering' }],
  createOrganization: true,
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'User group(s) added successfully.', data: {} } }
```

#### `addUsersToGroup`

* Adds users to an existing group.
* Params: [AddUsersToGroupRequest](/api-reference/sdk/models/data-models#adduserstogrouprequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.userGroups.addUsersToGroup({
  organizationId: 'org-123',
  organizationUserGroupId: 'engineering',
  userIds: ['user-1', 'user-2'],
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Users added to group successfully.', data: {} } }
```

#### `deleteUsersFromGroup`

* Removes users from a group, or removes all users.
* Params: [DeleteUsersFromGroupRequest](/api-reference/sdk/models/data-models#deleteusersfromgrouprequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.userGroups.deleteUsersFromGroup({
  organizationId: 'org-123',
  organizationUserGroupId: 'engineering',
  userIds: ['user-1'],
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Users removed from group successfully.', data: {} } }
```

### Notifications

**Namespace:** `sdk.api.notifications`

<Note>
  **Filtering unknown fields**: The add/update methods below accept an optional `options?: FieldFilterOptions` second argument. When `{ filterUnknownFields: true }` is passed, unknown/custom keys are dropped before the request is sent, narrowing the payload to exactly the fields the Velt backend endpoint accepts. Open-typed objects (`actionUser`, `context`, `metadata`, user objects) pass through whole. Filtering is fail-open: if it errors, the original payload is sent, so a write is never blocked. `UPDATE_NOTIFICATIONS_SPEC` intentionally excludes `isRead`/`isArchived` — they are unsupported by `/v2/notifications/update`, so they are dropped when filtering is on. See [Field Allowlist](#field-allowlist) for the full per-endpoint field list.
</Note>

#### `addNotifications`

* Adds one or more notifications targeted to specific users.
* Params: [AddNotificationsRequest](/api-reference/sdk/models/data-models#addnotificationsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)
* Accepts an optional `options?: FieldFilterOptions` second argument — pass `{ filterUnknownFields: true }` to drop unknown fields before sending (see the note above).

```ts theme={null}
await sdk.api.notifications.addNotifications({
  organizationId: 'org-123',
  documentId: 'doc-1',
  actionUser: { userId: 'user-1', name: 'John Doe', email: 'john@example.com' },
  displayHeadlineMessageTemplate: '{actionUser} commented on your document',
  displayBodyMessage: 'Check out the new comment',
  notifyUsers: [{ userId: 'user-2', name: 'Jane Doe' }],
  createOrganization: true,
  createDocument: true,
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Notification(s) added successfully.', data: {} } }
```

#### `getNotifications`

* Gets notifications for a user with optional filters and pagination.
* Params: [GetNotificationsRequest](/api-reference/sdk/models/data-models#getnotificationsrequest-node)
* Returns: [GetNotificationsResponse](/api-reference/sdk/models/data-models#getnotificationsresponse-node)

```ts theme={null}
await sdk.api.notifications.getNotifications({
  organizationId: 'org-123',
  userId: 'user-2',
  pageSize: 10,
  order: 'desc',
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Notification(s) retrieved successfully.',
    data: [
      {
        id: 'notificationId',
        notificationSource: 'custom',
        actionUser: { userId: 'user-1', name: 'John Doe', email: 'john@example.com' },
        displayBodyMessage: 'Check out the new comment',
        displayHeadlineMessageTemplate: '{actionUser} commented on your document',
        timestamp: 1722409519944
      }
    ],
    pageToken: 'nextPageToken'
  }
}
```

#### `updateNotifications`

* Updates existing notifications (e.g., mark as read).
* Params: [UpdateNotificationsRequest](/api-reference/sdk/models/data-models#updatenotificationsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)
* Accepts an optional `options?: FieldFilterOptions` second argument — pass `{ filterUnknownFields: true }` to drop unknown fields before sending (see the note above).

```ts theme={null}
await sdk.api.notifications.updateNotifications({
  organizationId: 'org-123',
  userId: 'user-2',
  notifications: [{ notificationId: 'n-1', read: true }],
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Notification(s) updated successfully.', data: {} } }
```

#### `deleteNotifications`

* Deletes one or more notifications.
* Params: [DeleteNotificationsRequest](/api-reference/sdk/models/data-models#deletenotificationsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.notifications.deleteNotifications({
  organizationId: 'org-123',
  userId: 'user-2',
  notificationIds: ['n-1'],
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Notification(s) deleted successfully.', data: {} } }
```

#### `getNotificationConfig`

* Gets notification preferences for a user.
* Params: [GetNotificationConfigRequest](/api-reference/sdk/models/data-models#getnotificationconfigrequest-node)
* Returns: [GetNotificationConfigResponse](/api-reference/sdk/models/data-models#getnotificationconfigresponse-node)

```ts theme={null}
await sdk.api.notifications.getNotificationConfig({
  organizationId: 'org-123',
  userId: 'user-1',
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'User config fetched successfully.',
    data: [
      {
        config: { inbox: 'ALL', email: 'ALL' },
        metadata: { organizationId: 'org-123', apiKey: 'API_KEY', documentId: 'doc-1', userId: 'user-1' }
      }
    ]
  }
}
```

#### `setNotificationConfig`

* Sets notification preferences for one or more users.
* Params: [SetNotificationConfigRequest](/api-reference/sdk/models/data-models#setnotificationconfigrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.notifications.setNotificationConfig({
  organizationId: 'org-123',
  userIds: ['user-1', 'user-2'],
  config: { inbox: 'ALL', email: 'MINE', slack: 'NONE' },
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'User config set successfully.', data: {} } }
```

### Comment Annotations

**Namespace:** `sdk.api.commentAnnotations`

<Note>
  **Filtering unknown fields**: The add/update methods below accept an optional `options?: FieldFilterOptions` second argument. When `{ filterUnknownFields: true }` is passed, request entity collections are narrowed to only the fields the Velt backend endpoint accepts, dropping unknown/custom keys before the request is sent. Open-typed objects (`from`, `context`, `metadata`, user objects) pass through whole — their nested contents are never filtered. Filtering is fail-open: if it errors, the original payload is sent, so a write is never blocked. See [Field Allowlist](#field-allowlist) for the full per-endpoint field list.
</Note>

```ts theme={null}
// Extra fields (internalId) are dropped before the request is sent.
await sdk.api.commentAnnotations.addCommentAnnotations(
  {
    organizationId: 'org-123',
    documentId: 'doc-1',
    commentAnnotations: [
      {
        location: { id: 'section-1' },
        commentData: [{ commentText: 'Review this', from: { userId: 'user-1' } }],
        internalId: 'app-specific-field', // dropped when filtering is enabled
      },
    ],
  },
  { filterUnknownFields: true },
);
```

#### `addCommentAnnotations`

* Creates comment annotations on a document.
* Params: [AddCommentAnnotationsRequest](/api-reference/sdk/models/data-models#addcommentannotationsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)
* Accepts an optional `options?: FieldFilterOptions` second argument — pass `{ filterUnknownFields: true }` to drop unknown fields before sending (see the note above).

```ts theme={null}
await sdk.api.commentAnnotations.addCommentAnnotations({
  organizationId: 'org-123',
  documentId: 'doc-1',
  commentAnnotations: [{
    location: { id: 'section-1', locationName: 'Introduction' },
    commentData: [{
      commentText: 'This needs review',
      commentHtml: '<p>This needs review</p>',
      from: { userId: 'user-1', name: 'John Doe', email: 'john@example.com' },
    }],
  }],
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Annotation(s) added successfully.', data: {} } }
```

#### `getCommentAnnotations`

* Retrieves comment annotations for a document with optional filters and pagination.
* Params: [GetCommentAnnotationsRequest](/api-reference/sdk/models/data-models#getcommentannotationsrequest-node)
* Returns: [GetCommentAnnotationsResponse](/api-reference/sdk/models/data-models#getcommentannotationsresponse-node)

```ts theme={null}
await sdk.api.commentAnnotations.getCommentAnnotations({
  organizationId: 'org-123',
  documentId: 'doc-1',
  statusIds: ['OPEN'],
  pageSize: 10,
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Annotations fetched successfully.',
    data: [
      {
        type: 'comment',
        comments: [
          {
            commentId: 123456,
            commentText: 'This is a sample comment text.',
            commentHtml: '<p>This is a sample comment text.</p>',
            from: { userId: 'user123', name: 'John Doe', email: 'john.doe@example.com' },
            createdAt: 1687344600000
          }
        ],
        status: { id: 'OPEN', name: 'Open', type: 'default' }
      }
    ]
  }
}
```

#### `getCommentAnnotationsCount`

* Gets total and unread annotation counts per document.
* Params: [GetCommentAnnotationsCountRequest](/api-reference/sdk/models/data-models#getcommentannotationscountrequest-node)
* Returns: [GetCommentAnnotationsCountResponse](/api-reference/sdk/models/data-models#getcommentannotationscountresponse-node)

```ts theme={null}
await sdk.api.commentAnnotations.getCommentAnnotationsCount({
  organizationId: 'org-123',
  documentIds: ['doc-1', 'doc-2'],
  userId: 'user-1',
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Comment count retrieved successfully.',
    data: {
      'doc-1': { total: 4, unread: 2 },
      'doc-2': { total: 2, unread: 0 }
    }
  }
}
```

#### `updateCommentAnnotations`

* Updates fields on existing annotations (e.g., resolve them).
* Params: [UpdateCommentAnnotationsRequest](/api-reference/sdk/models/data-models#updatecommentannotationsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)
* Accepts an optional `options?: FieldFilterOptions` second argument — pass `{ filterUnknownFields: true }` to drop unknown fields before sending (see the note above).

```ts theme={null}
await sdk.api.commentAnnotations.updateCommentAnnotations({
  organizationId: 'org-123',
  documentId: 'doc-1',
  annotationIds: ['annotation-id-1'],
  updatedData: { status: { id: 'RESOLVED', name: 'Resolved', type: 'terminal' } },
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Annotation(s) updated successfully.', data: {} } }
```

#### `deleteCommentAnnotations`

* Deletes comment annotations.
* Params: [DeleteCommentAnnotationsRequest](/api-reference/sdk/models/data-models#deletecommentannotationsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.commentAnnotations.deleteCommentAnnotations({
  organizationId: 'org-123',
  documentId: 'doc-1',
  annotationIds: ['annotation-id-1'],
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Annotation(s) deleted successfully.', data: {} } }
```

#### `addComments`

* Adds comments to an existing annotation.
* Params: [AddCommentsRequest](/api-reference/sdk/models/data-models#addcommentsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)
* Accepts an optional `options?: FieldFilterOptions` second argument — pass `{ filterUnknownFields: true }` to drop unknown fields before sending (see the note above).

```ts theme={null}
await sdk.api.commentAnnotations.addComments({
  organizationId: 'org-123',
  documentId: 'doc-1',
  annotationId: 'annotation-id-1',
  commentData: [{
    commentText: 'I agree, let me fix this',
    from: { userId: 'user-2', name: 'Jane Doe' },
  }],
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Comment(s) added successfully.', data: {} } }
```

#### `getComments`

* Retrieves comments within a specific annotation.
* Params: [GetCommentsRequest](/api-reference/sdk/models/data-models#getcommentsrequest-node)
* Returns: [GetCommentsResponse](/api-reference/sdk/models/data-models#getcommentsresponse-node)

```ts theme={null}
await sdk.api.commentAnnotations.getComments({
  organizationId: 'org-123',
  documentId: 'doc-1',
  annotationId: 'annotation-id-1',
  userIds: ['user-1'],
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Comments(s) retrieved successfully.',
    data: [
      {
        commentId: 153783,
        commentText: 'Sample Comment Text',
        commentHtml: '<div>Hello Updated 2</div>',
        from: { userId: 'yourUserId', name: 'User Name', email: 'user@example.com' },
        lastUpdated: '2024-06-20T09:53:42.258Z',
        status: 'added',
        type: 'text'
      }
    ]
  }
}
```

#### `updateComments`

* Updates comments within a specific annotation.
* Params: [UpdateCommentsRequest](/api-reference/sdk/models/data-models#updatecommentsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)
* Accepts an optional `options?: FieldFilterOptions` second argument — pass `{ filterUnknownFields: true }` to drop unknown fields before sending (see the note above).

```ts theme={null}
await sdk.api.commentAnnotations.updateComments({
  organizationId: 'org-123',
  documentId: 'doc-1',
  annotationId: 'annotation-id-1',
  commentIds: [123456],
  updatedData: { commentText: 'Updated text', commentHtml: '<p>Updated text</p>' },
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Comment(s) updated successfully.', data: {} } }
```

#### `deleteComments`

* Deletes individual comments from an annotation.
* Params: [DeleteCommentsRequest](/api-reference/sdk/models/data-models#deletecommentsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.commentAnnotations.deleteComments({
  organizationId: 'org-123',
  documentId: 'doc-1',
  annotationId: 'annotation-id-1',
  commentIds: [123456],
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Comment(s) deleted successfully.', data: {} } }
```

### Activities

**Namespace:** `sdk.api.activities`

<Note>
  **Filtering unknown fields**: The add/update methods below accept an optional `options?: FieldFilterOptions` second argument. When `{ filterUnknownFields: true }` is passed, request entity collections are narrowed to only the fields the Velt backend endpoint accepts, dropping unknown/custom keys before the request is sent. Open-typed objects (`actionUser`, `entityData`, `context`, `metadata`) pass through whole — their nested contents are never filtered. Filtering is fail-open: if it errors, the original payload is sent, so a write is never blocked. See [Field Allowlist](#field-allowlist) for the full per-endpoint field list.
</Note>

#### `addActivities`

* Logs activity events.
* Params: [AddActivitiesRequest](/api-reference/sdk/models/data-models#addactivitiesrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)
* Accepts an optional `options?: FieldFilterOptions` second argument — pass `{ filterUnknownFields: true }` to drop unknown fields before sending (see the note above).

```ts theme={null}
await sdk.api.activities.addActivities({
  organizationId: 'org-123',
  documentId: 'doc-456',
  activities: [{
    featureType: 'comment',
    actionType: 'comment.add',
    actionUser: { userId: 'user-1', name: 'John Doe', email: 'john@example.com' },
    displayMessageTemplate: '{{user}} added a comment',
  }],
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Activity(s) added successfully.', data: {} } }
```

#### `getActivities`

* Retrieves activity events with optional filters and pagination.
* Params: [GetActivitiesRequest](/api-reference/sdk/models/data-models#getactivitiesrequest-node)
* Returns: [GetActivitiesResponse](/api-reference/sdk/models/data-models#getactivitiesresponse-node)

```ts theme={null}
await sdk.api.activities.getActivities({
  organizationId: 'org-123',
  documentId: 'doc-456',
  featureTypes: ['comment'],
  order: 'desc',
  pageSize: 50,
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Activity(s) retrieved successfully.',
    data: [
      {
        id: 'activity-001',
        featureType: 'comment',
        actionType: 'comment.add',
        actionUser: { userId: 'user-1', email: 'user@example.com', name: 'User Name' },
        targetEntityId: 'annotation-789',
        displayMessageTemplate: '{{user}} added a comment',
        metadata: { apiKey: 'yourApiKey', documentId: 'doc-456', organizationId: 'org-123' },
        timestamp: 1722409519944
      }
    ],
    pageToken: 'nextPageToken'
  }
}
```

#### `updateActivities`

* Updates existing activity events.
* Params: [UpdateActivitiesRequest](/api-reference/sdk/models/data-models#updateactivitiesrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)
* Accepts an optional `options?: FieldFilterOptions` second argument — pass `{ filterUnknownFields: true }` to drop unknown fields before sending (see the note above).

```ts theme={null}
await sdk.api.activities.updateActivities({
  organizationId: 'org-123',
  activities: [{ id: 'activity-001', displayMessageTemplate: '{{user}} updated the comment' }],
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Activity(s) updated successfully.', data: {} } }
```

#### `deleteActivities`

* Deletes activity events for a document.
* Params: [DeleteActivitiesRequest](/api-reference/sdk/models/data-models#deleteactivitiesrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.activities.deleteActivities({
  organizationId: 'org-123',
  documentId: 'doc-456',
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Activity(s) deleted successfully.', data: {} } }
```

### Access Control

**Namespace:** `sdk.api.accessControl`

#### `addPermissions`

* Grants a user access to one or more resources.
* Params: [AddPermissionsRequest](/api-reference/sdk/models/data-models#addpermissionsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.accessControl.addPermissions({
  user: { userId: 'user-1' },
  permissions: {
    resources: [
      { type: 'organization', id: 'org-123', accessRole: 'editor' },
      { type: 'document', id: 'doc-1', organizationId: 'org-123', accessRole: 'viewer', expiresAt: 1728902400 },
    ],
  },
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Permissions added successfully.', data: {} } }
```

#### `getPermissions`

* Retrieves permissions for users on resources.
* Params: [GetPermissionsRequest](/api-reference/sdk/models/data-models#getpermissionsrequest-node)
* Returns: [GetPermissionsResponse](/api-reference/sdk/models/data-models#getpermissionsresponse-node)

```ts theme={null}
await sdk.api.accessControl.getPermissions({
  organizationId: 'org-123',
  userIds: ['user-1'],
  documentIds: ['doc-1'],
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'User permissions retrieved successfully.',
    data: {
      'user-1': {
        documents: { 'doc-1': { accessRole: 'viewer', accessType: 'restricted' } },
        organization: { 'org-123': { accessRole: 'editor' } }
      }
    }
  }
}
```

#### `removePermissions`

* Revokes a user's access to one or more resources.
* Params: [RemovePermissionsRequest](/api-reference/sdk/models/data-models#removepermissionsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.accessControl.removePermissions({
  userId: 'user-1',
  permissions: {
    resources: [
      { type: 'organization', id: 'org-123' },
      { type: 'document', id: 'doc-1', organizationId: 'org-123' },
    ],
  },
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Permissions removed successfully.', data: {} } }
```

#### `generateSignature`

* Generates a signed payload for the Permission Provider flow.
* Params: [GenerateSignatureRequest](/api-reference/sdk/models/data-models#generatesignaturerequest-node)
* Returns: [GenerateSignatureResponse](/api-reference/sdk/models/data-models#generatesignatureresponse-node)

```ts theme={null}
await sdk.api.accessControl.generateSignature({
  permissions: [{
    userId: 'user-1',
    resourceId: 'doc-1',
    type: 'document',
    hasAccess: true,
    accessRole: 'viewer',
  }],
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Signature generated successfully.',
    data: { signature: 'a1b2c3d4e5f6...' }
  }
}
```

#### `generateToken`

* Generates a JWT auth token for a user with embedded permissions.
* Params: [GenerateTokenRequest](/api-reference/sdk/models/data-models#generatetokenrequest-node)
* Returns: [GenerateTokenResponse](/api-reference/sdk/models/data-models#generatetokenresponse-node)

```ts theme={null}
await sdk.api.accessControl.generateToken({
  userId: 'user-1',
  userProperties: { name: 'John Doe', email: 'john@example.com', isAdmin: false },
  permissions: {
    resources: [
      { type: 'organization', id: 'org-123', accessRole: 'viewer' },
      { type: 'document', id: 'doc-1', organizationId: 'org-123', accessRole: 'editor' },
    ],
  },
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Token generated successfully.',
    data: { token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' }
  }
}
```

### CRDT

**Namespace:** `sdk.api.crdt`

Supports `text`, `map`, `array`, and `xml` CRDT data types.

#### `addCrdtData`

* Stores CRDT (Yjs) editor data for a document.
* Params: [AddCrdtDataRequest](/api-reference/sdk/models/data-models#addcrdtdatarequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.crdt.addCrdtData({
  organizationId: 'org-123',
  documentId: 'doc-1',
  editorId: 'my-collab-note',
  data: 'Hello, collaborative world!',
  type: 'text',
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'CRDT data added successfully.', data: {} } }
```

#### `getCrdtData`

* Retrieves CRDT data for a document, optionally filtered by editor.
* Params: [GetCrdtDataRequest](/api-reference/sdk/models/data-models#getcrdtdatarequest-node)
* Returns: [GetCrdtDataResponse](/api-reference/sdk/models/data-models#getcrdtdataresponse-node)

```ts theme={null}
await sdk.api.crdt.getCrdtData({
  organizationId: 'org-123',
  documentId: 'doc-1',
  editorId: 'my-collab-note',
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'CRDT data retrieved successfully.',
    data: [
      {
        data: 'Hello, collaborative world!',
        id: 'my-collab-note',
        lastUpdate: '2025-01-20T10:30:00.000Z',
        lastUpdatedBy: 'user-123',
        sessionId: 'session-abc-456'
      }
    ]
  }
}
```

#### `updateCrdtData`

* Updates existing CRDT editor data.
* Params: [UpdateCrdtDataRequest](/api-reference/sdk/models/data-models#updatecrdtdatarequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.crdt.updateCrdtData({
  organizationId: 'org-123',
  documentId: 'doc-1',
  editorId: 'my-collab-note',
  data: 'Updated collaborative content!',
  type: 'text',
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'CRDT data updated successfully.', data: {} } }
```

#### `deleteCrdtData`

* Delete CRDT editor data for a document. Omit `editorIds` to delete CRDT data for all editors of the document.
* Params: [DeleteCrdtDataRequest](/api-reference/sdk/models/data-models#deletecrdtdatarequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.crdt.deleteCrdtData({ organizationId: 'org-123', documentId: 'doc-1', editorIds: ['my-collab-note'] });
await sdk.api.crdt.deleteCrdtData({ organizationId: 'org-123', documentId: 'doc-1' });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'CRDT data deleted successfully.', data: {} } }
```

### Presence

**Namespace:** `sdk.api.presence`

#### `addPresence`

* Adds users to presence on a document.
* Params: [AddPresenceRequest](/api-reference/sdk/models/data-models#addpresencerequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.presence.addPresence({
  organizationId: 'org-123',
  documentId: 'doc-456',
  users: [
    { userId: 'user-1', name: 'John Doe', email: 'john@example.com', status: 'online' },
    { userId: 'user-2', name: 'Jane Doe', status: 'away' },
  ],
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Presence added successfully.', data: {} } }
```

#### `updatePresence`

* Updates presence status for one or more users.
* Params: [UpdatePresenceRequest](/api-reference/sdk/models/data-models#updatepresencerequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.presence.updatePresence({
  organizationId: 'org-123',
  documentId: 'doc-456',
  users: [{ userId: 'user-1', status: 'away' }],
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Presence updated successfully.', data: {} } }
```

#### `deletePresence`

* Removes users from presence on a document.
* Params: [DeletePresenceRequest](/api-reference/sdk/models/data-models#deletepresencerequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.presence.deletePresence({
  organizationId: 'org-123',
  documentId: 'doc-456',
  userIds: ['user-1', 'user-2'],
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Presence removed successfully.', data: {} } }
```

### Livestate

**Namespace:** `sdk.api.livestate`

#### `broadcastEvent`

* Broadcasts an ephemeral live-state event to all clients on a document.
* Params: [BroadcastEventRequest](/api-reference/sdk/models/data-models#broadcasteventrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.livestate.broadcastEvent({
  organizationId: 'org-123',
  documentId: 'doc-1',
  liveStateDataId: 'my-live-state',
  data: { status: 'active', message: 'Hello World' },
  merge: true,
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Event broadcast successfully.', data: {} } }
```

### Recordings

**Namespace:** `sdk.api.recordings`

#### `getRecordings`

* Retrieves recording metadata for an organization or document with optional filtering and pagination.
* Params: [GetRecordingsRequest](/api-reference/sdk/models/data-models#getrecordingsrequest-node)
* Returns: [GetRecordingsResponse](/api-reference/sdk/models/data-models#getrecordingsresponse-node)

```ts theme={null}
await sdk.api.recordings.getRecordings({
  organizationId: 'org-123',
  documentId: 'doc-456',
  recordingIds: ['rec-1'],
  pageSize: 10,
  pageToken: 'next-page-token',
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Recorder annotations retrieved successfully.',
    data: [
      {
        type: 'recorder',
        recordingType: 'screen',
        mode: 'floating',
        metadata: { apiKey: 'YOUR_API_KEY', documentId: 'doc-456', organizationId: 'org-123' },
        recordedTime: { duration: 4204.55, display: '00:00:04' },
        displayName: 'Screen Recording 1773814490242.mp4',
        annotationId: 'ypvmVTROaNU1qP4kq7Cc',
        attachments: [{
          attachmentId: 875113,
          url: 'https://storage.googleapis.com/...',
          mimeType: 'video/mp4',
          name: 'recording_1773814490242.mp4',
          type: 'mp4',
          size: 103551
        }],
        latestVersion: 5
      }
    ],
    pageToken: 'nextPageToken'
  }
}
```

### Rewriter

**Namespace:** `sdk.api.rewriter`

Supports OpenAI, Anthropic, and Gemini models.

#### `askAi`

* Calls the Velt AI rewriter with a model, prompt, and text.
* Params: [AskAiRequest](/api-reference/sdk/models/data-models#askairequest-node)
* Returns: [AskAiResponse](/api-reference/sdk/models/data-models#askairesponse-node)

> Note: `askAi` may take several seconds to respond. The SDK does not enforce a client-side timeout.

```ts theme={null}
await sdk.api.rewriter.askAi({
  model: 'gpt-4o',
  prompt: 'Fix the grammar and spelling in the following text.',
  text: 'Their going to the store to by some apples.',
});
```

**Response**

```ts theme={null}
{
  result: {
    success: true,
    text: 'The rewritten or processed text returned by the model.'
  }
}
```

### GDPR

**Namespace:** `sdk.api.gdpr`

#### `deleteAllUserData`

* Requests deletion of all data associated with one or more users.
* Params: [DeleteAllUserDataRequest](/api-reference/sdk/models/data-models#deletealluserdatarequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

> Note: `deleteAllUserData` is asynchronous and returns a job ID. Poll completion with `getDeleteUserDataStatus`.

```ts theme={null}
await sdk.api.gdpr.deleteAllUserData({
  userIds: ['user-1', 'user-2'],
  organizationIds: ['org-123'],
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'User data deletion job queued.',
    data: { jobId: 'delete-job-id' }
  }
}
```

#### `getAllUserData`

* Exports all data for a single user (paginated).
* Params: [GetAllUserDataRequest](/api-reference/sdk/models/data-models#getalluserdatarequest-node)
* Returns: [GetAllUserDataResponse](/api-reference/sdk/models/data-models#getalluserdataresponse-node)

```ts theme={null}
await sdk.api.gdpr.getAllUserData({
  organizationId: 'org-123',
  userId: 'user-1',
  pageToken: 'next-page-token',
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Data fetched successfully.',
    data: {
      comments: [/* up to 100 items */],
      reactions: [/* up to 100 items */],
      recordings: [/* up to 100 items */],
      notifications: [/* up to 100 items */]
    },
    nextPageToken: 'bhdwdqwjs298e39e479ddkeuw==329'
  }
}
```

#### `getDeleteUserDataStatus`

* Polls the status of a deletion job.
* Params: [GetDeleteUserDataStatusRequest](/api-reference/sdk/models/data-models#getdeleteuserdatastatusrequest-node)
* Returns: [GetDeleteUserDataStatusResponse](/api-reference/sdk/models/data-models#getdeleteuserdatastatusresponse-node)

```ts theme={null}
await sdk.api.gdpr.getDeleteUserDataStatus({ jobId: 'job-id-from-delete-call' });
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Data fetched successfully.',
    data: {
      isDeleteCompleted: true,
      tasksLeft: 0,
      lastTaskCompletedTime: 1748972106739
    }
  }
}
```

### Workspace

**Namespace:** `sdk.api.workspace`

#### `createWorkspace`

* Creates a new Velt workspace.
* Params: [CreateWorkspaceRequest](/api-reference/sdk/models/data-models#createworkspacerequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.createWorkspace({
  ownerEmail: 'owner@example.com',
  workspaceName: 'My Workspace',
  name: 'John Doe',
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Workspace created successfully.', data: {} } }
```

#### `getWorkspace`

* Retrieves details for the current workspace.
* Params: empty object `{}`
* Returns: [GetWorkspaceResponse](/api-reference/sdk/models/data-models#getworkspaceresponse-node)

```ts theme={null}
await sdk.api.workspace.getWorkspace({});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Workspace retrieved successfully.',
    data: {
      id: 'workspace_abc123',
      name: 'My Workspace',
      owner: { email: 'owner@example.com', id: 'owner_id_123', name: 'John Doe', avatar: '' },
      authToken: 'eyJhbGciOiJSUzI1NiIs...',
      apiKeyList: {
        velt_api_key_1: { apiKeyName: 'John Doe Test API Key', id: 'velt_api_key_1', type: 'testing' }
      }
    }
  }
}
```

#### `createApiKey`

* Creates a new API key for the workspace.
* Params: [CreateApiKeyRequest](/api-reference/sdk/models/data-models#createapikeyrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.createApiKey({
  ownerEmail: 'owner@example.com',
  type: 'production',
  createAuthToken: true,
  apiKeyName: 'Production Key',
  allowedDomains: ['example.com', '*.example.com'],
});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'API key created successfully.',
    data: { apiKey: 'velt_api_key_2', authToken: 'eyJhbGciOi...' }
  }
}
```

#### `updateApiKey`

* Renames an existing API key.
* Params: [UpdateApiKeyRequest](/api-reference/sdk/models/data-models#updateapikeyrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.updateApiKey({ apiKey: 'velt_api_key_1', apiKeyName: 'Renamed Key' });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'API key updated successfully.', data: {} } }
```

#### `getApiKeys`

* Lists all API keys with optional pagination.
* Params: [GetApiKeysRequest](/api-reference/sdk/models/data-models#getapikeysrequest-node)
* Returns: [GetApiKeysResponse](/api-reference/sdk/models/data-models#getapikeysresponse-node)

```ts theme={null}
await sdk.api.workspace.getApiKeys({ pageSize: 50, pageToken: 'next-page-token' });
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'API keys retrieved successfully.',
    data: [
      { id: 'velt_api_key_1', apiKeyName: 'Production Key', type: 'production' },
      { id: 'velt_api_key_2', apiKeyName: 'Testing Key', type: 'testing' }
    ],
    nextPageToken: 'eyJsYXN0SWQiOiJ2ZWx0X2FwaV9rZXlfMiJ9'
  }
}
```

#### `getApiKeyMetadata`

* Gets metadata for the current API key.
* Now posts to `/v2/workspace/apikeyconfig/get` (previously `/v2/workspace/apikeymetadata/get`); the method name and signature (`GetApiKeyMetadataRequest`, which is `{}`) are unchanged — no caller changes required.
* Params: empty object `{}`
* Returns: [GetApiKeyMetadataResponse](/api-reference/sdk/models/data-models#getapikeymetadataresponse-node)

```ts theme={null}
await sdk.api.workspace.getApiKeyMetadata({});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'API key metadata retrieved successfully.',
    data: {
      defaultDocumentAccessType: 'public',
      planInfo: { type: 'sdk test' },
      ownerEmail: 'owner@example.com'
    }
  }
}
```

#### `resetAuthToken`

* Rotates the auth token for an API key.
* Params: [ResetAuthTokenRequest](/api-reference/sdk/models/data-models#resetauthtokenrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.resetAuthToken({ apiKey: 'velt_api_key_1' });
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Auth token reset successfully.',
    data: { authToken: 'eyJhbGciOi...' }
  }
}
```

#### `getAuthTokens`

* Lists auth tokens for an API key.
* Params: [GetAuthTokensRequest](/api-reference/sdk/models/data-models#getauthtokensrequest-node)
* Returns: [GetAuthTokensResponse](/api-reference/sdk/models/data-models#getauthtokensresponse-node)

```ts theme={null}
await sdk.api.workspace.getAuthTokens({ apiKey: 'velt_api_key_1' });
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Auth tokens retrieved successfully.',
    data: {
      apiKey: 'velt_api_key_1',
      authTokens: [
        { token: 'eyJhbGciOiJSUzI1NiIs...', name: 'Default Auth Token', domains: [] }
      ]
    }
  }
}
```

#### `addDomains`

* Adds allowed domains to the workspace allow-list.
* Params: [AddDomainsRequest](/api-reference/sdk/models/data-models#adddomainsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.addDomains({ domains: ['example.com', '*.firebase.com'] });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Domains added successfully.', data: {} } }
```

#### `deleteDomains`

* Removes allowed domains.
* Params: [DeleteDomainsRequest](/api-reference/sdk/models/data-models#deletedomainsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.deleteDomains({ domains: ['example.com'] });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Domains deleted successfully.', data: {} } }
```

#### `getDomains`

* Lists allowed domains.
* Params: empty object `{}`
* Returns: [GetDomainsResponse](/api-reference/sdk/models/data-models#getdomainsresponse-node)

```ts theme={null}
await sdk.api.workspace.getDomains({});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Allowed domains retrieved successfully.',
    data: { allowedDomains: ['localhost', '127.0.0.1', 'example.com'] }
  }
}
```

#### `getEmailStatus`

* Checks email verification status for an owner.
* Params: [GetEmailStatusRequest](/api-reference/sdk/models/data-models#getemailstatusrequest-node)
* Returns: [GetEmailStatusResponse](/api-reference/sdk/models/data-models#getemailstatusresponse-node)

```ts theme={null}
await sdk.api.workspace.getEmailStatus({ ownerEmail: 'owner@example.com' });
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Email verified and workspace reactivated',
    data: { verified: true }
  }
}
```

#### `sendLoginLink`

* Sends a passwordless login link to a user.
* Params: [SendLoginLinkRequest](/api-reference/sdk/models/data-models#sendloginlinkrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.sendLoginLink({
  email: 'user@example.com',
  continueUrl: 'https://app.example.com/dashboard',
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Login link sent.', data: {} } }
```

#### `getEmailConfig`

* Retrieves email service configuration.
* Params: empty object `{}`
* Returns: [GetEmailConfigResponse](/api-reference/sdk/models/data-models#getemailconfigresponse-node)

```ts theme={null}
await sdk.api.workspace.getEmailConfig({});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Email configuration retrieved successfully.',
    data: {
      useEmailService: false,
      emailServiceConfig: { type: 'default', apiKey: '', fromEmail: '', fromCompany: '', commentTemplateId: '', tagTemplateId: '' }
    }
  }
}
```

#### `updateEmailConfig`

* Updates email service configuration.
* Params: [UpdateEmailConfigRequest](/api-reference/sdk/models/data-models#updateemailconfigrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.updateEmailConfig({
  useEmailService: true,
  emailServiceConfig: { type: 'sendgrid', apiKey: 'sg-key', fromEmail: 'noreply@example.com' },
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Email config updated successfully.', data: {} } }
```

#### `getWebhookConfig`

* Retrieves webhook configuration.
* Params: empty object `{}`
* Returns: [GetWebhookConfigResponse](/api-reference/sdk/models/data-models#getwebhookconfigresponse-node)

```ts theme={null}
await sdk.api.workspace.getWebhookConfig({});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Webhook configuration retrieved successfully.',
    data: {
      useWebhookService: false,
      webhookServiceConfig: { authToken: '', rawNotificationUrl: '', processedNotificationUrl: '' }
    }
  }
}
```

#### `updateWebhookConfig`

* Updates webhook configuration.
* Params: [UpdateWebhookConfigRequest](/api-reference/sdk/models/data-models#updatewebhookconfigrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.updateWebhookConfig({
  useWebhookService: true,
  webhookServiceConfig: {
    authToken: 'webhook-secret',
    rawNotificationUrl: 'https://example.com/webhook/raw',
    processedNotificationUrl: 'https://example.com/webhook/processed',
  },
});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Webhook config updated successfully.', data: {} } }
```

#### `getRequestedDomains`

* List requested additional domains.
* Params: [GetRequestedDomainsRequest](/api-reference/sdk/models/data-models#getrequesteddomainsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.getRequestedDomains({});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Requested domains retrieved successfully.', data: [] } }
```

#### `acceptRejectAdditionalUrlRequest`

* Accept/reject an additional-URL request.
* Params: [AcceptRejectAdditionalUrlRequest](/api-reference/sdk/models/data-models#acceptrejectadditionalurlrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.acceptRejectAdditionalUrlRequest({ action: 'accept', id: 'req-1', clientDocId: 'doc-1' });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Additional URL request updated successfully.', data: {} } }
```

#### `createDomainRequest`

* Create a domain request.
* Params: [CreateDomainRequest](/api-reference/sdk/models/data-models#createdomainrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.createDomainRequest({ domain: 'https://example.com', clientDocumentId: 'doc-1', organizationId: 'org-123' });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Domain request created successfully.', data: {} } }
```

#### `copyApiKey`

* Copy configuration from one API key to another.
* Params: [CopyApiKeyRequest](/api-reference/sdk/models/data-models#copyapikeyrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.copyApiKey({ sourceApiKey: 'velt_api_key_1', targetApiKey: 'velt_api_key_2' });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'API key configuration copied successfully.', data: {} } }
```

#### `updateApiKeyConfig`

* Update API key config (private comments, JWT, AI model keys, etc.).
* Params: [UpdateApiKeyConfigRequest](/api-reference/sdk/models/data-models#updateapikeyconfigrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.updateApiKeyConfig({ requireJwtToken: true, defaultDocumentAccessType: 'organizationPrivate' });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'API key config updated successfully.', data: {} } }
```

#### `getNotificationConfig`

* Get workspace notification service config.
* Params: [GetWorkspaceNotificationConfigRequest](/api-reference/sdk/models/data-models#getworkspacenotificationconfigrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.getNotificationConfig({});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Notification config retrieved successfully.', data: {} } }
```

#### `updateNotificationConfig`

* Update notification service config.
* Params: [UpdateWorkspaceNotificationConfigRequest](/api-reference/sdk/models/data-models#updateworkspacenotificationconfigrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.updateNotificationConfig({ useNotificationService: true });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Notification config updated successfully.', data: {} } }
```

#### `getPermissionProviderConfig`

* Get permission-provider config.
* Params: [GetPermissionProviderConfigRequest](/api-reference/sdk/models/data-models#getpermissionproviderconfigrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.getPermissionProviderConfig({});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Permission provider config retrieved successfully.', data: {} } }
```

#### `updatePermissionProviderConfig`

* Update permission-provider config.
* Params: [UpdatePermissionProviderConfigRequest](/api-reference/sdk/models/data-models#updatepermissionproviderconfigrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.updatePermissionProviderConfig({ usePermissionProvider: true });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Permission provider config updated successfully.', data: {} } }
```

#### `getActivityConfig`

* Get activity service config.
* Params: [GetActivityConfigRequest](/api-reference/sdk/models/data-models#getactivityconfigrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.getActivityConfig({});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Activity config retrieved successfully.', data: {} } }
```

#### `updateActivityConfig`

* Update activity service config.
* Params: [UpdateActivityConfigRequest](/api-reference/sdk/models/data-models#updateactivityconfigrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.updateActivityConfig({ activityServiceConfig: { isEnabled: true } });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Activity config updated successfully.', data: {} } }
```

#### `ensureWorkspaceAuthToken`

* Ensure a workspace auth token exists.
* Params: [EnsureWorkspaceAuthTokenRequest](/api-reference/sdk/models/data-models#ensureworkspaceauthtokenrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.ensureWorkspaceAuthToken({});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Workspace auth token ensured successfully.', data: { authToken: 'eyJhbGciOi...' } } }
```

#### `getAdvancedWebhookConfig`

* Get advanced webhook config.
* Params: [GetAdvancedWebhookConfigRequest](/api-reference/sdk/models/data-models#getadvancedwebhookconfigrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.getAdvancedWebhookConfig({});
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Advanced webhook config retrieved successfully.', data: {} } }
```

#### `updateAdvancedWebhookConfig`

* Update advanced webhook config.
* Params: [UpdateAdvancedWebhookConfigRequest](/api-reference/sdk/models/data-models#updateadvancedwebhookconfigrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.updateAdvancedWebhookConfig({ isEnabled: true });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Advanced webhook config updated successfully.', data: {} } }
```

#### `getAdvancedWebhookEndpoints`

* List advanced webhook endpoints (paged).
* Params: [GetAdvancedWebhookEndpointsRequest](/api-reference/sdk/models/data-models#getadvancedwebhookendpointsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.getAdvancedWebhookEndpoints({});
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Advanced webhook endpoints retrieved successfully.',
    data: [{ id: 'endpoint-1', url: 'https://example.com/webhooks/velt' }],
    nextPageToken: 'pageToken'
  }
}
```

#### `createAdvancedWebhookEndpoint`

* Create an advanced webhook endpoint.
* Params: [CreateAdvancedWebhookEndpointRequest](/api-reference/sdk/models/data-models#createadvancedwebhookendpointrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.createAdvancedWebhookEndpoint({ url: 'https://example.com/webhooks/velt', filterTypes: ['comment.added', 'comment.resolved'] });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Advanced webhook endpoint created successfully.', data: { id: 'endpoint-1' } } }
```

#### `updateAdvancedWebhookEndpoint`

* Update an advanced webhook endpoint.
* Params: [UpdateAdvancedWebhookEndpointRequest](/api-reference/sdk/models/data-models#updateadvancedwebhookendpointrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.updateAdvancedWebhookEndpoint({ endpointId: 'endpoint-1', url: 'https://example.com/webhooks/velt-v2' });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Advanced webhook endpoint updated successfully.', data: {} } }
```

#### `deleteAdvancedWebhookEndpoint`

* Delete an advanced webhook endpoint.
* Params: [DeleteAdvancedWebhookEndpointRequest](/api-reference/sdk/models/data-models#deleteadvancedwebhookendpointrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.deleteAdvancedWebhookEndpoint({ endpointId: 'endpoint-1' });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Advanced webhook endpoint deleted successfully.', data: {} } }
```

#### `getAdvancedWebhookEndpointSecret`

* Get an endpoint's signing secret.
* Params: [GetAdvancedWebhookEndpointSecretRequest](/api-reference/sdk/models/data-models#getadvancedwebhookendpointsecretrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.workspace.getAdvancedWebhookEndpointSecret({ endpointId: 'endpoint-1' });
```

**Response**

```ts theme={null}
{ result: { status: 'success', message: 'Endpoint secret retrieved successfully.', data: { secret: 'whsec_...' } } }
```

### Token

**Namespace:** `sdk.api.token`

#### `getToken`

* Generates a Velt auth token for a user via REST.
* Params: positional — `organizationId` (string), `userId` (string), `email` (string, optional), `isAdmin` (boolean, optional)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

> Note: `getToken` takes positional arguments — not a request object.

```ts theme={null}
await sdk.api.token.getToken('org-123', 'user-1', 'john@example.com', false);
```

**Response**

```ts theme={null}
{
  result: {
    status: 'success',
    message: 'Token generated successfully.',
    data: { token: 'eyJhbGciOiJIUzI1NiIs...' }
  }
}
```

### Approval Workflows

**Namespace:** `sdk.api.approval`

A new service for defining approval/review workflows (graphs of agent, human, and webhook nodes) and dispatching/resolving their executions and steps. 14 methods (routes live under `/v2/workflow/*`). Every type is `Approval`-prefixed. `ApprovalService` is also exported directly from `@veltdev/node`.

#### `createDefinition`

* Create a workflow definition (nodes, edges, groups, loops, triggers).
* Params: [ApprovalCreateDefinitionRequest](/api-reference/sdk/models/data-models#approvalcreatedefinitionrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.approval.createDefinition({
  definitionId: 'doc-publish-review',
  name: 'Document Publish Review',
  scope: { level: 'organization', organizationId: 'org-123' },
  nodes: [
    { nodeId: 'review', type: 'human', config: { reviewers: [{ userId: 'user-1', mandatory: true }], commentBody: 'Please review before publishing.' } },
  ],
  edges: [],
});
```

#### `updateDefinition`

* Update a definition (create shape + optimistic `ifVersion`).
* Params: [ApprovalUpdateDefinitionRequest](/api-reference/sdk/models/data-models#approvalupdatedefinitionrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.approval.updateDefinition({
  definitionId: 'doc-publish-review',
  name: 'Document Publish Review v2',
  nodes: [
    { nodeId: 'review', type: 'human', config: { reviewers: [{ userId: 'user-1', mandatory: true }], commentBody: 'Please review before publishing.' } },
  ],
  edges: [],
  ifVersion: 1,
});
```

#### `deleteDefinition`

* Delete a definition.
* Params: [ApprovalDeleteDefinitionRequest](/api-reference/sdk/models/data-models#approvaldeletedefinitionrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.approval.deleteDefinition({ definitionId: 'doc-publish-review' });
```

#### `getDefinition`

* Get a single definition.
* Params: [ApprovalGetDefinitionRequest](/api-reference/sdk/models/data-models#approvalgetdefinitionrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.approval.getDefinition({ definitionId: 'doc-publish-review' });
```

#### `listDefinitions`

* List definitions (paged).
* Params: [ApprovalListDefinitionsRequest](/api-reference/sdk/models/data-models#approvallistdefinitionsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.approval.listDefinitions({});
```

#### `dispatchExecution`

* Dispatch (start) an execution of a definition.
* Params: [ApprovalDispatchExecutionRequest](/api-reference/sdk/models/data-models#approvaldispatchexecutionrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
const exec = await sdk.api.approval.dispatchExecution({ definitionId: 'doc-publish-review', organizationId: 'org-123', documentId: 'doc-1' });
```

#### `cancelExecution`

* Cancel a running execution.
* Params: [ApprovalCancelExecutionRequest](/api-reference/sdk/models/data-models#approvalcancelexecutionrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.approval.cancelExecution({ executionId: 'exec-1' });
```

#### `getExecution`

* Get a single execution.
* Params: [ApprovalGetExecutionRequest](/api-reference/sdk/models/data-models#approvalgetexecutionrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.approval.getExecution({ executionId: 'exec-1' });
```

#### `getExecutionEvents`

* Get a page of an execution's lifecycle events.
* Params: [ApprovalGetExecutionEventsRequest](/api-reference/sdk/models/data-models#approvalgetexecutioneventsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.approval.getExecutionEvents({ executionId: 'exec-1' });
```

#### `listExecutions`

* List executions.
* Params: [ApprovalListExecutionsRequest](/api-reference/sdk/models/data-models#approvallistexecutionsrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.approval.listExecutions({});
```

#### `cancelStep`

* Admin step-level cancel.
* Params: [ApprovalCancelStepRequest](/api-reference/sdk/models/data-models#approvalcancelsteprequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.approval.cancelStep({ executionId: 'exec-1', stepId: 'review', actorId: 'user-1' });
```

#### `resolveStep`

* Admin force-resolve a hung step.
* Params: [ApprovalResolveStepRequest](/api-reference/sdk/models/data-models#approvalresolvesteprequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.approval.resolveStep({ executionId: 'exec-1', stepId: 'review', action: 'force-approve', actorId: 'user-1' });
```

#### `recordAgentResolution`

* Record one resolution against a blocking-agent step's aggregator.
* Params: [ApprovalRecordAgentResolutionRequest](/api-reference/sdk/models/data-models#approvalrecordagentresolutionrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.approval.recordAgentResolution({ executionId: 'exec-1', stepId: 'review', responseId: 'resp-1', resolution: 'resolved', actorId: 'user-1' });
```

#### `recordReviewerDecision`

* Record one reviewer's decision against a human step's aggregator.
* Params: [ApprovalRecordReviewerDecisionRequest](/api-reference/sdk/models/data-models#approvalrecordreviewerdecisionrequest-node)
* Returns: [VeltApiResponse](/api-reference/sdk/models/data-models#veltapiresponse-node)

```ts theme={null}
await sdk.api.approval.recordReviewerDecision({ executionId: 'exec-1', stepId: 'review', reviewerId: 'user-1', decision: 'approve' });
```

#### Node & Graph Types

These node/graph/config interfaces are referenced by the request types above. They are not standalone data-model entries.

```ts theme={null}
interface ApprovalScope { level: 'apiKey' | 'organization' | 'document'; organizationId?: string; documentId?: string; }
interface ApprovalAgentResolutionPolicy { kind: 'allResolved' | 'minResolved'; minCount?: number; } // minCount req. when 'minResolved' (1..500)
interface ApprovalAgentNodeConfig { agentId: string; urlPath: string; /* 1..500 */ crossPageExecute?: boolean; maxUrlsToProcess?: number; /* positive int, max 500 */ userContextMapping?: Record<string, string>; pollIntervalMs?: number; /* 5000..60000 */ promptOverride?: string; /* max 8000 */ inputMapping?: Record<string, string>; requireNonEmptyOutput?: boolean; agentMaxRuntimeMs?: number; /* positive int, max 24h */ blocking?: boolean; resolutionPolicy?: ApprovalAgentResolutionPolicy; /* required when blocking */ }
interface ApprovalHumanReviewer { userId: string; mandatory: boolean; }
interface ApprovalHumanNodeOnRejectLoopBack { toNodeId: string; /* 1..64 */ maxIterations?: number; /* 1..20 */ onExhausted?: { routeToNodeId?: string }; when?: string; /* max 2000 */ }
interface ApprovalHumanNodeOnReject { routeToNodeId?: string; loopBack?: ApprovalHumanNodeOnRejectLoopBack; } // exactly one
interface ApprovalHumanNodeConfig { reviewerIds?: string[]; /* mutually exclusive with reviewers; 1..50 */ reviewers?: ApprovalHumanReviewer[]; /* unique userIds; >=1 mandatory; 1..50 */ reviewerEmails?: string[]; /* max 50 */ commentBody?: string; /* max 8000 */ onReject?: ApprovalHumanNodeOnReject; }
interface ApprovalWebhookNodeConfig { url: string; /* https, allowed host, max 2000 */ method?: 'GET' | 'POST'; mode?: 'sync' | 'async'; authMode?: 'hmac' | 'token' | 'none'; authTokenHeader?: string; /* required when authMode === 'token'; max 500 */ timeoutMs?: number; /* 1000..60000 */ expectedStatusCodes?: number[]; /* each 100..599; max 20 */ bodyTemplate?: 'envelope' | 'pass-through' | 'none'; /* 'none' requires method GET */ requestHeaders?: Record<string, string>; /* reserved/x-velt-* headers rejected; values max 2000 */ }
interface ApprovalAgentNode { nodeId: string; type: 'agent'; config: ApprovalAgentNodeConfig; slaMs?: number; requireNonEmptyOutput?: boolean; }
interface ApprovalHumanNode { nodeId: string; type: 'human'; config: ApprovalHumanNodeConfig; slaMs?: number; requireNonEmptyOutput?: boolean; }
interface ApprovalWebhookNode { nodeId: string; type: 'webhook'; config: ApprovalWebhookNodeConfig; slaMs?: number; requireNonEmptyOutput?: boolean; }
type ApprovalNode = ApprovalAgentNode | ApprovalHumanNode | ApprovalWebhookNode;
interface ApprovalEdge { from: string; to: string; when?: string; } // from/to 1..64; when max 1000
type ApprovalQuorumPolicy = 'waitAll' | 'cancelOnQuorum' | 'joinOnQuorum';
interface ApprovalGroup { groupId: string; /* 1..64 */ memberNodeIds: string[]; /* 1..500 */ expectedSteps: number; /* 1..500 */ quorum: number; /* 1..500 */ onQuorumMet?: ApprovalQuorumPolicy; requiredNodeIds?: string[]; /* max 500 */ }
interface ApprovalInboundWebhookTrigger { authMode: 'hmac' | 'bearer'; secret: string; /* 16..512 */ idempotencyHeader?: string; /* 1..128 */ idempotencyBodyPath?: string; /* 1..256 */ payloadMapping?: 'pass-through' | 'wrap'; }
interface ApprovalTrigger { triggerId: string; /* 1..128 */ eventName?: string; /* max 128 */ filters?: Record<string, unknown>; inboundWebhook?: ApprovalInboundWebhookTrigger; }
interface ApprovalLoopRejectTrigger { when?: string; } // max 2000
interface ApprovalLoopExhaustionPolicy { routeToNodeId?: string; } // 1..64
interface ApprovalLoop { loopId: string; /* 1..64 */ entryNodeId: string; /* 1..64 */ bodyNodeIds: string[]; /* 1..50 */ onIterationReject?: ApprovalLoopRejectTrigger; onExhausted?: ApprovalLoopExhaustionPolicy; maxIterations: number; /* 1..20 */ }
interface ApprovalWebhookConfig { url: string; secret: string; eventTypes?: string[]; } // url https/max2000; secret 16..512; eventTypes each 1..128, max 50
```

## Error Handling

The SDK exports five typed error classes:

| Class                 | Extends        | When thrown                           |
| --------------------- | -------------- | ------------------------------------- |
| `VeltSDKError`        | `Error`        | Base class for all SDK errors         |
| `VeltDatabaseError`   | `VeltSDKError` | MongoDB connection or query failures  |
| `VeltValidationError` | `VeltSDKError` | Missing or invalid request parameters |
| `VeltTokenError`      | `VeltSDKError` | Token generation failures             |
| `VeltApiError`        | `VeltSDKError` | REST API call failures                |

```ts theme={null}
import { VeltSDK, VeltDatabaseError, VeltApiError } from '@veltdev/node';

try {
  const result = await sdk.api.organizations.getOrganizations({ organizationIds: ['org-123'] });
} catch (err) {
  if (err instanceof VeltApiError) {
    console.error('API call failed:', err.message);
  } else if (err instanceof VeltDatabaseError) {
    console.error('Database error:', err.message);
  } else {
    throw err;
  }
}
```

Common self-hosting error codes returned in the response `errorCode` field:

* `INVALID_INPUT` — Request data is malformed or missing required fields
* `INTERNAL_ERROR` — Server-side error during processing
* `NOT_FOUND` — Requested resource was not found

## Data Models

Key interfaces and serializer helpers exported from `@veltdev/node`. All symbols are available at the package top level:

```ts theme={null}
import {
  PartialCommentAnnotation, partialCommentAnnotationFromDict, partialCommentAnnotationToDict,
  PartialTargetTextRange, partialTargetTextRangeFromDict, partialTargetTextRangeToDict,
  PartialComment, partialCommentFromDict, partialCommentToDict,
  BaseMetadata, baseMetadataFromDict, baseMetadataToDict,
} from '@veltdev/node';
```

<Note>
  `PartialUser` and `PartialAttachment` referenced in the interfaces below are minimal pass-through shapes. `PartialUser` is `{ userId: string }` (any additional fields the frontend sends are preserved but not strongly typed). Full user/attachment shapes are documented in the central [Data Models reference](/api-reference/sdk/models/data-models#partialuser).
</Note>

### PartialCommentAnnotation

Represents a single comment annotation thread. Used as the payload shape when reading or writing annotation data in self-hosting event handlers.

```ts theme={null}
interface PartialCommentAnnotation {
  annotationId: string;
  metadata?: BaseMetadata;
  comments?: Record<string, PartialComment>;
  from?: PartialUser;              // Typed in v1.0.2; previously pass-through
  assignedTo?: PartialUser;        // Typed in v1.0.2; previously pass-through
  targetTextRange?: PartialTargetTextRange;  // Typed in v1.0.2; previously pass-through
  resolvedByUserId?: string | null; // Three-state — see resolvedByUserId Semantics below
  [key: string]: unknown;          // Unknown keys are preserved on round-trip
}
```

| Field              | Type                             | Notes                                                  |
| ------------------ | -------------------------------- | ------------------------------------------------------ |
| `annotationId`     | `string`                         | Required. Stable identifier for the annotation thread. |
| `metadata`         | `BaseMetadata`                   | Optional context (document, org, API key).             |
| `comments`         | `Record<string, PartialComment>` | Keyed by `commentId` string.                           |
| `from`             | `PartialUser`                    | User who created the annotation.                       |
| `assignedTo`       | `PartialUser`                    | User currently assigned to the annotation.             |
| `targetTextRange`  | `PartialTargetTextRange`         | Text selection the annotation is anchored to.          |
| `resolvedByUserId` | `string \| null`                 | Three-state field — see below.                         |
| `[key]`            | `unknown`                        | Extra keys pass through serialization unchanged.       |

**Serializers:**

```ts theme={null}
declare function partialCommentAnnotationFromDict(data: Record<string, unknown>): PartialCommentAnnotation;
declare function partialCommentAnnotationToDict(annotation: PartialCommentAnnotation): Record<string, unknown>;
```

### PartialTargetTextRange

The text selection an annotation is anchored to. New interface in v1.0.2.

```ts theme={null}
interface PartialTargetTextRange {
  text: string;
}
```

| Field  | Type     | Notes                                                          |
| ------ | -------- | -------------------------------------------------------------- |
| `text` | `string` | Required. The selected text content the annotation references. |

**Serializers:**

```ts theme={null}
declare function partialTargetTextRangeFromDict(data: Record<string, unknown>): PartialTargetTextRange;
declare function partialTargetTextRangeToDict(range: PartialTargetTextRange): Record<string, unknown>;
```

### resolvedByUserId Semantics

`resolvedByUserId` on `PartialCommentAnnotation` is a three-state field. TypeScript has no sentinel value, so the three states map to property presence and value:

| State               | Representation                 | Meaning                                                  |
| ------------------- | ------------------------------ | -------------------------------------------------------- |
| **Absent**          | Property not set on the object | No resolution information — do not write to the database |
| **Explicit `null`** | `resolvedByUserId: null`       | Annotation was un-resolved (cleared)                     |
| **String**          | `resolvedByUserId: "user-123"` | Resolved by the user with this ID                        |

Use `Object.hasOwn` (or `'resolvedByUserId' in annotation`) to distinguish absent from explicit `null`:

```ts theme={null}
const annotation = partialCommentAnnotationFromDict(payload);

if (!Object.hasOwn(annotation, 'resolvedByUserId')) {
  // Field absent — skip; do not overwrite existing resolution state
} else if (annotation.resolvedByUserId === null) {
  // Explicit null — un-resolve the annotation
} else {
  // String — mark resolved by annotation.resolvedByUserId
}
```

### PartialComment

A single comment within an annotation thread. Round-trip serializers preserve unknown keys, so custom fields added by your application are not dropped.

```ts theme={null}
interface PartialComment {
  commentId: string | number;
  commentHtml?: string;
  commentText?: string;
  attachments?: Record<string, PartialAttachment>;
  from?: PartialUser;
  to?: PartialUser[];
  taggedUserContacts?: PartialTaggedUserContacts[];
  [key: string]: unknown;   // Pass-through for unknown keys
}
```

| Field                | Type                                | Notes                                               |
| -------------------- | ----------------------------------- | --------------------------------------------------- |
| `commentId`          | `string \| number`                  | Required. Unique within the annotation.             |
| `commentHtml`        | `string`                            | Raw HTML body of the comment.                       |
| `commentText`        | `string`                            | Plain-text body, used for search and notifications. |
| `attachments`        | `Record<string, PartialAttachment>` | Keyed by attachment ID.                             |
| `from`               | `PartialUser`                       | Author of the comment.                              |
| `to`                 | `PartialUser[]`                     | Explicit recipients (direct-mention replies).       |
| `taggedUserContacts` | `PartialTaggedUserContacts[]`       | Users @-mentioned in the comment body.              |
| `[key]`              | `unknown`                           | Extra keys pass through serialization unchanged.    |

**Serializers (new in v1.0.2):**

```ts theme={null}
declare function partialCommentFromDict(data: Record<string, unknown>): PartialComment;
declare function partialCommentToDict(comment: PartialComment): Record<string, unknown>;
```

### BaseMetadata

Contextual identifiers attached to annotations and events. Provides both Velt-internal IDs and your application's client-facing IDs for the same resources.

```ts theme={null}
interface BaseMetadata {
  apiKey?: string;
  documentId?: string;
  clientDocumentId?: string;
  organizationId?: string;
  clientOrganizationId?: string;
  folderId?: string;
  veltFolderId?: string;
  documentMetadata?: Record<string, unknown>;
  sdkVersion?: string | null;   // New in v1.0.2
}
```

| Field                  | Type                      | Notes                                                                          |
| ---------------------- | ------------------------- | ------------------------------------------------------------------------------ |
| `apiKey`               | `string`                  | Velt project API key.                                                          |
| `documentId`           | `string`                  | Velt-internal document identifier.                                             |
| `clientDocumentId`     | `string`                  | Your application's document identifier.                                        |
| `organizationId`       | `string`                  | Velt-internal organization identifier.                                         |
| `clientOrganizationId` | `string`                  | Your application's organization identifier.                                    |
| `folderId`             | `string`                  | Your application's folder identifier.                                          |
| `veltFolderId`         | `string`                  | Velt-internal folder identifier.                                               |
| `documentMetadata`     | `Record<string, unknown>` | Arbitrary key-value metadata attached to the document.                         |
| `sdkVersion`           | `string \| null`          | SDK version string. `null` means version is known to be absent. New in v1.0.2. |

**Serializers (exposed on public surface in v1.0.2):**

```ts theme={null}
declare function baseMetadataFromDict(data: Record<string, unknown>): BaseMetadata;
declare function baseMetadataToDict(meta: BaseMetadata): Record<string, unknown>;
```

## Distribution

The package ships both CommonJS (CJS) and ES Module (ESM) bundles with rolled-up `.d.ts` type definitions, making it compatible with all modern Node.js project setups.

```ts theme={null}
// ESM
import { VeltSDK } from '@veltdev/node';

// CommonJS
const { VeltSDK } = require('@veltdev/node');
```
