> For the complete documentation index, see [llms.txt](https://docs.uptiq.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.uptiq.ai/ai-workbench-qore/platform-resources/the-agent-development-sdk.md).

# Agent SDK

Run a Qore agent from your own TypeScript code, in Node or in the browser, with @uptiqai/agent-sdk.

The Agent SDK is a TypeScript package, `@uptiqai/agent-sdk`, for running Qore agents from your own code. The same package works in a Node backend and in the browser.

It calls an agent that already exists. You build the agent in the Agent Builder; the SDK starts runs, uploads documents, and reads conversations.

When you select **TypeScript SDK** as the **Client** on the agent's **Agent API** tab, the generated code sample uses this package. See [Integrate Agent](/ai-workbench-qore/console/agent-builder/deploy/agent-integration.md#agent-api).

### Which SDK is this?

| SDK                       | Package                | What it is for                                                                    | Runs in         |
| ------------------------- | ---------------------- | --------------------------------------------------------------------------------- | --------------- |
| **Agent SDK**             | `@uptiqai/agent-sdk`   | Running an agent from your code: start runs, upload documents, read conversations | Node or browser |
| **Widgets SDK**           | `@uptiqai/widgets-sdk` | Dropping a prebuilt chat interface into your web app                              | Browser         |
| **Headless Agent JS SDK** | `@uptiqai/widgets-sdk` | Building your own chat UI, with sockets and events handled for you                | Browser         |

The Widgets SDK and the Headless Agent JS SDK are documented on [Integrate Agent](/ai-workbench-qore/console/agent-builder/deploy/agent-integration.md).

## Install

```bash
npm install @uptiqai/agent-sdk
```

## Create a client

Create one `AgentClient` with your server URL and one set of credentials. `baseUrl` is required. The code sample on the **Agent API** tab fills it in.

{% tabs %}
{% tab title="API key and secret (server)" %}
For server-to-server calls. Keep the secret server-side.

```ts
import { AgentClient } from '@uptiqai/agent-sdk';

const client = new AgentClient({
    apiKey: process.env.AGENT_API_KEY!,
    apiSecret: process.env.AGENT_API_SECRET!,
    baseUrl: 'https://api.example.com'
});
```

Sent as the `x-api-key` and `x-api-key-secret` headers.
{% endtab %}

{% tab title="Widget key (browser)" %}
For the browser. A widget key works only from a whitelisted domain. Never ship the API secret to a browser.

```ts
const client = new AgentClient({ widgetKey: '...', baseUrl: 'https://api.example.com' });
```

Sent as the `x-widget-key` header.
{% endtab %}

{% tab title="Logged-in user (access token)" %}
To act as a logged-in user, pass their access token instead of a key. Requests then carry that user's own permissions, so an action a key could perform may still be denied. `projectId` is required.

```ts
// Node: the refresh token stays server-side.
const client = new AgentClient({
    accessToken,
    refreshToken,
    projectId: 'my-project',
    baseUrl: 'https://api.example.com',
    onTokenRefreshed: ({ accessToken }) => saveToken(accessToken)
});

// Browser: the host app owns the session. Never ship a refresh token.
const client = new AgentClient({
    accessToken: session.accessToken,
    getAccessToken: () => session.getFreshAccessToken(),
    projectId: 'my-project',
    baseUrl: 'https://api.example.com'
});
```

Access tokens expire after an hour. On a 401, the SDK gets a new token, from `getAccessToken` if you gave one and otherwise by using the refresh token, and retries the request once. Without either option, a rejected token raises `AuthenticationError`.

From a browser, the token's `origin` claim must match the page's origin, or the request is rejected.

Sent as `Authorization: Bearer` with a `project-id` header.
{% endtab %}
{% endtabs %}

### Other client options

| Option                 | What it does                                                                                                                             |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `agentExecutorVersion` | The agent executor version, for example `'1.3'`. Added to the request path.                                                              |
| `authToken`            | The end user's identity-provider token, for an agent with caller authentication. See below.                                              |
| `timeout`              | Request timeout in milliseconds. Defaults to 60000.                                                                                      |
| `defaultHeaders`       | Extra headers added to every request.                                                                                                    |
| `transport`            | `'rest'` (the default) or `'socket'`. `'socket'` also opens the shared socket when the client is created. The REST methods work in both. |

### Authenticate the end user to the agent

For an agent with caller authentication turned on, pass the end user's identity-provider token, such as a Microsoft Entra, Google or Okta access token, as `authToken`. It's sent with every run and socket message. The agent verifies it and passes it to its MCP connections as `$auth.token`. Agents without authentication ignore it.

```ts
const client = new AgentClient({
    apiKey,
    apiSecret,
    baseUrl: 'https://api.example.com',
    authToken: userAccessToken
});
```

`authToken` is separate from `accessToken`, which signs the SDK in to the platform. The token is fixed for the client's lifetime, so create one client per user, and a new one when the token is refreshed.

## Upload documents

```ts
const documents = await client.documents.upload({
    agentId: 'precision-loan-guide',
    files: [{ fileName: 'rates.pdf', mimeType: 'application/pdf', data: fileOrBufferOrBase64 }]
});
```

If you already have a signed URL, skip the upload and pass it to `run`:

```ts
await client.agents.run({
    agentId,
    query: 'Compare a 15-year and 30-year fixed mortgage.',
    documents: [{ signedUrl, fileName: 'rates.pdf', mimeType: 'application/pdf' }]
});
```

## Run an agent

{% tabs %}
{% tab title="Sync" %}
Waits for the agent and returns its result.

```ts
const run = await client.agents.run({
    agentId,
    query: 'Compare a 15-year and 30-year fixed mortgage.',
    payload: { creditScore: 720 },
    documents,               // refs from documents.upload()
    uid: 'user_123',
    executionMode: 'sync'
});
console.log(run.result?.content);
```

{% endtab %}

{% tab title="Async" %}
Returns at once. The result is sent to your webhooks.

```ts
const { executionId, conversationId } = await client.agents.run({
    agentId,
    query: 'Summarize the attached mortgage doc.',
    documents,
    executionMode: 'async',
    webhooks: [
        { url: 'https://my-app.example.com/agent-callback', metadata: { orderId: '123' } }
    ]
});
```

`webhooks` is a list of `{ url, metadata? }`. The platform POSTs the execution result to each `url` and echoes your `metadata` back in the payload.
{% endtab %}

{% tab title="Realtime" %}
Streams each conversation element as the agent produces it.

```ts
import { AgentClient, AgentEventType } from '@uptiqai/agent-sdk';

const client = new AgentClient({ apiKey, apiSecret, baseUrl, transport: 'socket' });

const session = client.socket.createSession({
    agentId,
    uid: 'user_123',
    onEvent: event => {
        if (event.type === AgentEventType.AgentMessage) console.log(event.content);
    }
});

// Or subscribe to one element type
session.on(AgentEventType.ToolCall, event => console.log('tool call:', event.toolName));

// Send a message and wait for the run to finish or pause for input
const result = await session.run({ query: 'Compare a 15-year and 30-year fixed mortgage.' });
console.log(result?.content);
session.close();
```

{% endtab %}
{% endtabs %}

Documents can be passed as the refs from `documents.upload()`, as `{ signedUrl }`, or as `{ documentId }`.

On agent executor 1.4 and later, `run` also returns a `conversationId`. It identifies the whole thread: the main execution plus one per sub-agent. Pass it to `conversations.getConversation` to read the thread, or to `client.socket.createSession({ agentId, conversationId, executionId })` to stream a run you started in async mode.

## Verify a webhook

Each webhook carries an `X-Webhook-Signature` header: a hex-encoded RSA-SHA256 signature of the raw body. `verifyWebhookSignature` checks it against the public key shown on the agent's **Agent API** tab, and throws `WebhookVerificationError` when it doesn't match. Verify against the raw body bytes, because parsing and re-serializing the JSON can change the signature.

```ts
import { verifyWebhookSignature } from '@uptiqai/agent-sdk';
import express from 'express';

const app = express();

app.post('/agent-callback', express.raw({ type: 'application/json' }), (req, res) => {
    try {
        verifyWebhookSignature({
            rawBody: req.body,
            signatureHex: req.header('X-Webhook-Signature') ?? '',
            publicKey: process.env.AGENT_WEBHOOK_PUBLIC_KEY ?? ''
        });
    } catch {
        return res.sendStatus(401);
    }

    const { executionId, success, result, error, metadata } = JSON.parse(req.body.toString());
    res.sendStatus(200);
});
```

## Read conversations

```ts
const { conversations, total } = await client.conversations.list({ agentId, page: 1, pageSize: 20 });
const conversation = await client.conversations.getConversation({ agentId, conversationId });
```

On agent executor 1.4 and later, pass `conversationId`. Earlier executor versions address the thread by `executionId`.

## Errors

Every failure is a typed subclass of `AgentSDKError`: `AuthenticationError`, `NotFoundError`, `RateLimitError`, `BadRequestError`, `InternalServerError` or `APIConnectionError`. Each carries `.status`, `.headers` and `.body`. `verifyWebhookSignature` throws `WebhookVerificationError`.

```ts
import { AuthenticationError } from '@uptiqai/agent-sdk';

try {
    await client.agents.run({ agentId, query });
} catch (error) {
    if (error instanceof AuthenticationError) {
        // bad credentials
    }
}
```

## Related pages

* [Integrate Agent](/ai-workbench-qore/console/agent-builder/deploy/agent-integration.md): API keys, widget keys, whitelisted domains, and the generated code samples.
* [Custom skills](/ai-workbench-qore/platform-resources/skill-library/custom-skills.md): adding your own skill to an agent.

***

{% columns %}
{% column width="83.33333333333334%" %}

<p align="right"><em>Maintained by</em> <mark style="color:green;">Abhishek Paul</mark><br><code>AI-assisted, human-approved</code></p>
{% endcolumn %}

{% column width="16.666666666666664%" %}

<div align="left"><img src="https://1326225582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F0qmgQjJ5aArDTj2ACFHG%2Fuploads%2Fgit-blob-d034645b4f8f8ee661985f5306b60ded3289653c%2Fmaintainer-abhishek-paul.png?alt=media" alt="" width="60"></div>
{% endcolumn %}
{% endcolumns %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.uptiq.ai/ai-workbench-qore/platform-resources/the-agent-development-sdk.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
