# Client API

## `createPromptlyClient(config?)`

Creates a new Promptly client instance.

```typescript
import { createPromptlyClient } from '@promptlycms/prompts';

const promptly = createPromptlyClient({
  apiKey: process.env.PROMPTLY_API_KEY,
});
```

### Config options

| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| `apiKey` | `string` | No | `process.env.PROMPTLY_API_KEY` | Your Promptly API key |
| `baseUrl` | `string` | No | `https://api.promptlycms.com` | API base URL |
| `model` | `(modelId: string) => LanguageModel` | No | Auto-detection | Custom model resolver - overrides built-in auto-detection |

### Return type

Returns a `PromptlyClient` with four methods: `getPrompt()`, `getPrompts()`, `getComposer()`, and `getComposers()`.

### Throws

- `PromptlyError` with code `UNAUTHORIZED` if no API key is provided and `PROMPTLY_API_KEY` is not set in the environment.

---

## `client.getPrompt(promptId, options?)`

Fetches a single prompt by ID.

```typescript
const result = await promptly.getPrompt('my-prompt');
```

### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `promptId` | `PromptId` | Yes | The prompt ID to fetch. Autocompletes known IDs if codegen types are present. |
| `options.version` | `string` | No | Specific version to fetch (default: latest) |

### Return type: `PromptResult<V>`

| Property | Type | Description |
|----------|------|-------------|
| `promptId` | `string` | The prompt ID |
| `promptName` | `string` | Human-readable prompt name |
| `version` | `string` | The resolved version |
| `systemMessage` | `string` | System message from the CMS |
| `userMessage` | `PromptMessage<V>` | Callable function for template interpolation |
| `temperature` | `number` | Temperature setting |
| `model` | `LanguageModel` | Auto-resolved AI SDK model |
| `config` | `PromptConfig` | Full config including schema fields |
| `publishedVersions` | `PublishedVersion[]` | Available published versions (if present) |

### `userMessage` usage

The `userMessage` property is both callable and stringifiable:

```typescript
// Interpolate variables
const text = result.userMessage({ name: 'Alice' });

// Get raw template
const template = String(result.userMessage);
// => 'Hello ${name}!'
```

### Generic type parameters

```typescript
client.getPrompt<
  T extends string,             // Prompt ID
  V extends PromptVersion<T>    // Version (default: 'latest')
>(promptId: T, options?: GetOptions<V>)
  => Promise<PromptResult<VariablesFor<T, V>>>
```

When codegen types are present, `T` narrows to a known prompt ID and `VariablesFor<T, V>` resolves to the typed variable shape. For unknown IDs, it falls back to `Record<string, string>`.

---

## `client.getPrompts(entries)`

Fetches multiple prompts in parallel.

```typescript
const [first, second] = await promptly.getPrompts([
  { promptId: 'prompt-a' },
  { promptId: 'prompt-b', version: '2.0.0' },
]);
```

### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `entries` | `readonly PromptRequest[]` | Yes | Array of prompt requests |

Each `PromptRequest`:

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `promptId` | `string` | Yes | The prompt ID |
| `version` | `string` | No | Specific version (default: latest) |

### Return type

Returns `GetPromptsResults<T>` - a mapped tuple where each position is typed to the corresponding request's prompt variables.

### Generic type parameters

```typescript
client.getPrompts<const T extends readonly PromptRequest[]>(entries: T)
  => Promise<GetPromptsResults<T>>
```

The `const` type parameter preserves literal string types from the input array.

---

## `client.getComposer(composerId, options?)`

Fetches a single composer by ID. Resolves all prompt segments into AI SDK-compatible objects and returns a `formatComposer()` function to reassemble the final output.

```typescript
const composer = await promptly.getComposer('my-composer', {
  input: { text: 'Hello world', targetLang: 'French' },
});
```

### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `composerId` | `ComposerId` | Yes | The composer ID to fetch. Narrows to generated composer IDs if codegen types are present. |
| `options.version` | `string` | No | Specific version to fetch (default: latest) |
| `options.input` | `Record<string, unknown>` | No | Input variables to interpolate into static segments and prompt user messages |

### Return type: `ComposerResult<Names>`

| Property | Type | Description |
|----------|------|-------------|
| `composerId` | `string` | The composer ID |
| `composerName` | `string` | Human-readable composer name |
| `version` | `string` | The resolved version |
| `config` | `ComposerConfig` | Full config including schema fields |
| `segments` | `ComposerSegment[]` | Raw segment data from the API |
| `prompts` | `ComposerPrompt[]` | De-duplicated array of resolved prompts |
| `formatComposer` | `ComposerFormatFn<Names>` | Function to reassemble the final output from prompt results |
| `compose` | `(generate: ComposerGenerateFn) => Promise<string>` | Convenience method — runs all prompts through your generate function in parallel and assembles the output |
| `[promptName]` | `ComposerPrompt` | Each prompt is also available as a named property (camelCase of the prompt name) |

### Named prompt properties

Each prompt segment in the composer is exposed as a camelCase property on the result. For example, a prompt named "Intro Prompt" becomes `introPrompt`:

```typescript
const { introPrompt, reviewPrompt, formatComposer } = await promptly.getComposer(
  'my-composer',
  { input: { topic: 'TypeScript' } },
);

// Each named prompt is a ComposerPrompt - spread directly into generateText()
const introResult = await generateText(introPrompt);
const reviewResult = await generateText(reviewPrompt);

// Reassemble the final output
const output = formatComposer({
  introPrompt: introResult,
  reviewPrompt: reviewResult,
});
```

### `ComposerPrompt` shape

Each prompt object has properties that map directly to AI SDK function parameters:

```typescript
type ComposerPrompt = {
  model: LanguageModel;
  system: string | undefined;
  prompt: string;
  temperature: number;
  promptId: string;
  promptName: string;
};
```

### `compose()` usage

The simplest way to run a composer. Pass your AI SDK generate function and get back the assembled string:

```typescript
// Simple — runs all prompts in parallel
const output = await composer.compose(generateText);

// With overrides per prompt
const output = await composer.compose((prompt) =>
  generateText({ ...prompt, maxTokens: 500 })
);
```

`compose()` accepts any function matching `(prompt: ComposerPrompt) => Promise<FormatInput>`. String and `{ text }` results preserve newlines as `<br>` tags and keep visible paragraph gaps when inserted into standalone rich-text paragraphs. Return `{ html }` only when you want trusted HTML inserted unchanged.

### `formatComposer()` usage

The `formatComposer()` function accepts a record mapping prompt names to results. Each value can be a string, an object with a `text` property (matching the `generateText()` return shape), or an object with an `html` property:

```typescript
// Pass generateText() results directly
const output = formatComposer({
  introPrompt: await generateText(introPrompt),
  reviewPrompt: await generateText(reviewPrompt),
});

// Or pass plain strings
const output = formatComposer({
  introPrompt: 'Hello, world!',
  reviewPrompt: 'Looks good.',
});

// Or pass trusted HTML without newline conversion
const output = formatComposer({
  introPrompt: { html: '<p>Hello<br>world</p>' },
  reviewPrompt: { html: '<strong>Looks good.</strong>' },
});
```

### Generic type parameters

```typescript
client.getComposer<
  T extends ComposerId,         // Composer ID
  V extends ComposerVersion<T>  // Version (default: 'latest')
>(composerId: T, options?: GetComposerOptions<T, V>)
  => Promise<ComposerResult<ComposerPromptNamesFor<T>>>
```

When codegen types are present, `T` narrows to a known composer ID, `ComposerInputFor<T, V>` types the `input` option, and `ComposerPromptNamesFor<T>` types the named prompt keys on the result.

---

## `client.getComposers(entries)`

Fetches multiple composers in parallel.

```typescript
const [first, second] = await promptly.getComposers([
  { composerId: 'composer-a', input: { text: 'Hello' } },
  { composerId: 'composer-b', version: '2.0.0' },
]);
```

### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `entries` | `readonly ComposerRequest[]` | Yes | Array of composer requests |

Each `ComposerRequest`:

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `composerId` | `ComposerId` | Yes | The composer ID |
| `input` | `Record<string, unknown>` | No | Input variables for interpolation |
| `version` | `string` | No | Specific version (default: latest) |

### Return type

Returns `GetComposersResults<T>` - a mapped tuple where each position is typed to the corresponding request's composer prompt names.

### Generic type parameters

```typescript
client.getComposers<const T extends readonly ComposerRequest[]>(entries: T)
  => Promise<GetComposersResults<T>>
```

The `const` type parameter preserves literal string types from the input array.