# Scout Client SDK

> How to use the Uniform Scout Client to invoke Uniform's AI agent from server-side code, continue threads, and get structured results back.

Source: https://docs.uniform.app/docs/sdk/scout-client

`ScoutClient` invokes [Scout](https://docs.uniform.app/docs/guides/ai/scout), Uniform's AI agent, from server-side code.
It lets you put an agent that already understands your project behind a job, a service, or a bot
of your own.

> **Call Scout from a server, not from the browser:**
>
> `ScoutClient` needs an API key or bearer token that carries real permissions on your project,
> so putting it in client-side code exposes those credentials.
>
> The Uniform AI API also allows cross-origin browser requests only from the Uniform dashboard, so
> a call made from your own site's origin is blocked by the browser before it reaches Scout. To
> reach Scout from a browser app, [proxy the request](#streaming-to-a-chat-interface) through your
> own backend.

## Installation

`ScoutClient` ships in `@uniformdev/automations-sdk`.

```bash
npm install @uniformdev/automations-sdk
```

## Creating a client

A client needs a project and credentials. Pass either an `apiKey` or a `bearerToken`.

```ts
import { ScoutClient } from '@uniformdev/automations-sdk/ai';

const scout = new ScoutClient({
  projectId: process.env.UNIFORM_PROJECT_ID,
  apiKey: process.env.UNIFORM_API_KEY,
});

const { text } = await scout.invoke({
  message: 'Review the homepage hero copy against our brand guidelines.',
});
```

`invoke` runs one turn and resolves when the turn completes, which is what you want when no one
is watching output arrive. It returns the final assistant message as `text`, along with the full
thread so far as `messages`.

Scout runs with the permissions of the credentials you pass and consumes
[AI credits](https://docs.uniform.app/docs/guides/ai/ai-credits) like any other Scout usage. If the team is out of
credits, `invoke` throws.

Set `aiApiHost` if your project is not in the default US region.

## Continuing a conversation

Omit `threadId` for a one-shot turn and Scout starts a new thread. Pass the same `threadId`
across calls to build a multi-turn conversation, where Scout keeps the context of everything
said earlier in the thread.

```ts
const threadId = crypto.randomUUID();

await scout.invoke({ threadId, message: 'Find entries missing a meta description.' });
const { text } = await scout.invoke({ threadId, message: 'Now draft one for each of them.' });
```

## Getting a structured result

Pass an `outputSchema` when you need a machine-readable result rather than prose. Scout must
record a conforming result as its final action, and `invoke` throws if it finishes without one.

```ts
import * as z from 'zod';

const { structuredOutput } = await scout.invoke({
  message: 'Give me 5 jokes about content management',
  // you can use zod 4 schemas or JSON schema; a zod schema also types `structuredOutput`
  outputSchema: z.object({ jokes: z.array(z.string()) }),
});

// structuredOutput is typed as { jokes: string[] }
structuredOutput.jokes.forEach((joke) => console.log(joke));
```

## Streaming to a chat interface

`invoke` buffers the whole turn, which is the wrong shape when a person is waiting on the other
end. The endpoint speaks the [Vercel AI SDK](https://sdk.vercel.ai) UI message stream protocol
when you call it with `Accept: text/event-stream`, so you can relay Scout's output to a chat UI
or a Slack bot as it arrives.

Use `getRequest` to get the endpoint URL and auth headers for a thread, then forward the stream
on to your client from a route you own. Keeping this on your server is what holds the
credentials back and avoids the cross-origin restriction described above.

```ts
// in a route on your own backend
const { url, headers } = scout.getRequest(threadId);

const upstream = await fetch(url, {
  method: 'POST',
  // getRequest does not set accept, so ask for the stream yourself
  headers: { ...headers, accept: 'text/event-stream' },
  body: JSON.stringify({ messages }),
});

return new Response(upstream.body, { headers: { 'content-type': 'text/event-stream' } });
```

Point the AI SDK's `DefaultChatTransport`, or `readUIMessageStream`, at your own route to consume
the relayed stream.

## Calling the endpoint directly

The client is a convenience over a single endpoint,
`POST /projects/{projectId}/threads/{threadId}/messages`, which you can call from any language.

_For the US region:_

```bash
curl -X POST https://ai.uniform.global/projects/$PROJECT_ID/threads/$THREAD_ID/messages \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"messages":[{"id":"1","role":"user","parts":[{"type":"text","text":"Tell me a joke"}]}]}'
```

_For the EU region:_

```bash
curl -X POST https://ai.eu.uniform.global/projects/$PROJECT_ID/threads/$THREAD_ID/messages \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"messages":[{"id":"1","role":"user","parts":[{"type":"text","text":"Tell me a joke"}]}]}'
```

The response is buffered JSON by default. Send `Accept: text/event-stream` instead to receive a
Vercel AI SDK UI message stream, as described in
[streaming to a chat interface](#streaming-to-a-chat-interface). Authenticate with either an
`Authorization: Bearer` header or an `x-api-key` header.

## Using ScoutClient in an automation

A [code automation](https://docs.uniform.app/docs/guides/automations/code-automations) constructs the client from its own
identity rather than from environment variables. See
[calling Scout from an automation](https://docs.uniform.app/docs/guides/automations/code-automations#calling-scout).

> **Use an AI coding assistant:**
>
> The [`uniform-automations`](https://github.com/uniformdev/agent-skills/tree/main/skills/uniform-automations) agent skill teaches coding assistants such as Claude Code, Codex, Cursor, and Copilot how to write, test, and deploy code automations with the Automations SDK, including `ScoutClient` calls and syncs with external systems. To install it, see [Uniform agent skills](https://docs.uniform.app/docs/guides/ai/agent-skills).
