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

# Server

> Server-side client for calling Akapulu APIs from your backend.

## Overview

`@akapulu/server` is the server-side package in the Akapulu Web SDK.

Use it in your backend to call Akapulu APIs from your own server routes.

This is the recommended way to keep your API key on the server while letting your frontend call simple local endpoints.

## Installation

```bash theme={null}
npm install @akapulu/server
```

## Client constructor

The entry point is `createAkapuluServerClient()`.

```ts theme={null}
import { createAkapuluServerClient } from "@akapulu/server";

const akapulu = createAkapuluServerClient();
```

The client reads `AKAPULU_API_KEY` from the environment. If it is missing, client creation throws an error.

## Main responsibility

`@akapulu/server` sits between your application routes and the Akapulu API.

It is responsible for:

* creating the server client
* calling the connect endpoint
* polling conversation updates
* fetching conversation detail data
* fetching conversation recording responses
* mapping your app-specific request body into Akapulu request fields like `scenario_id`, `avatar_id`, `runtime_vars`, and `record_conversation`

## Method reference

### <span id="connect-conversation">`connectConversation(body)`</span>

```ts theme={null}
connectConversation(body: ConnectConversationRequest): Promise<ConnectConversationResponse>
```

`ConnectConversationRequest`:

<ParamField path="scenario_id" type="string" required>
  Scenario id to launch.
</ParamField>

<ParamField path="avatar_id" type="string" required>
  Avatar id to use for the conversation.
</ParamField>

<ParamField path="runtime_vars" type="Record<string, string>">
  Optional runtime variables passed into the scenario.
</ParamField>

<ParamField path="stt_keywords" type="string[]">
  Optional speech-to-text keyword hints.
</ParamField>

<ParamField path="record_conversation" type="boolean">
  Optional recording toggle.
</ParamField>

`ConnectConversationResponse`:

* `room_url`
* `token`
* `conversation_session_id`

### `pollConversationUpdates(conversationSessionId)`

```ts theme={null}
pollConversationUpdates(conversationSessionId: string): Promise<ConversationUpdatesResponse>
```

`ConversationUpdatesResponse`:

* `conversation_session_id`
* `call_is_ready`
* `completion_percent`
* `latest_update_text`

### `getConversationDetail(conversationSessionId)`

```ts theme={null}
getConversationDetail(conversationSessionId: string): Promise<ConversationDetailResponse>
```

`ConversationDetailResponse` includes:

* `id`
* `created_at`
* `created_at_text`
* `duration_text`
* `avatar`
* `scenario_id`
* `recording`
* `transcript_rows`

### `getConversationRecording(conversationSessionId)`

```ts theme={null}
getConversationRecording(conversationSessionId: string): Promise<ConversationRecordingResponse>
```

`ConversationRecordingResponse` is a union of:

* redirect response: `{ kind: "redirect", status, location }`
* json response: `{ kind: "json", status, payload }`
* binary response: `{ kind: "binary", status, body, contentType, contentDisposition }`

## Main types

The package also exports the main request and response types used by those methods, including:

* `ConnectConversationRequest`
* `ConnectConversationResponse`
* `ConversationUpdatesResponse`
* `ConversationDetailResponse`
* `ConversationRecordingResponse`
* `AkapuluApiErrorResponse`

The package also exports `AkapuluApiError`, which is thrown for non-success API responses from `connectConversation`, `pollConversationUpdates`, and `getConversationDetail`.

## Typical route pattern

Most apps create local routes like:

* `POST /api/akapulu/connect`
* `GET /api/akapulu/updates?conversation_session_id=...`
* `GET /api/akapulu/conversation-details?conversation_session_id=...`
* `GET /api/akapulu/view-recording?conversation_session_id=...`

Those routes then call the Akapulu APIs with `@akapulu/server`.

## How it fits with the rest of the SDK

In a typical app:

1. The browser calls your local backend routes.
2. Your backend uses `@akapulu/server`.
3. `@akapulu/server` calls Akapulu APIs.
4. Your frontend then uses `@akapulu/react` or `@akapulu/react-ui` to join and render the conversation.

## What your local routes return

**`AkapuluProvider`** (from [`@akapulu/react`](/web-sdk/react-sdk)) reads **`config.endpoints.connectPath`** and **`config.endpoints.updatesPath`**, sends a **POST** request to the connect URL, then sends **GET** requests to the updates URL while the session is joining.

Those handlers must respond with **snake\_case** JSON in the same shape Akapulu returns. See [Customize Conversation UI](/guides/conversations/customize-conversation-ui). The usual pattern is to **`return` the resolved promise** from **`@akapulu/server`** so the response types stay aligned.

### Connect route → `connectConversation`

Your **`POST`** connect handler should return a body typed as **`ConnectConversationResponse`**:

* `room_url` — Daily room URL for media
* `token` — join token for that room
* `conversation_session_id` — id used for updates polling

```ts theme={null}
return await akapulu.connectConversation({ ... }); // satisfies ConnectConversationResponse
```

### Updates route → `pollConversationUpdates`

Your **`GET`** updates handler should return a body typed as **`ConversationUpdatesResponse`**:

* `conversation_session_id` — same session the client is polling
* `call_is_ready` — when `true`, the client can treat the call as ready
* `completion_percent` — `0`–`100` progress from the scenario
* `latest_update_text` — short status string for UI

```ts theme={null}
return await akapulu.pollConversationUpdates(conversationSessionId); // satisfies ConversationUpdatesResponse
```

Both response shapes are exported from **`@akapulu/server`**. The **`AkapuluProvider`** contract for those URLs is summarized under **Connect and updates response shape** in [Customize Conversation UI](/guides/conversations/customize-conversation-ui).

## Custom connect payloads

One common pattern is to accept app-specific JSON in your local connect route, then map it into Akapulu request fields like `runtime_vars` and `record_conversation`.

```tsx theme={null}
/* ===================== Client ===================== */

"use client";

import { AkapuluProvider } from "@akapulu/react";
import { AkapuluConversation } from "@akapulu/react-ui";

// `connectBody` is sent as the POST JSON body to `connectPath` when starting a session.
export function App() {
  return (
    <AkapuluProvider
      config={{
        endpoints: {
          connectPath: "/api/akapulu/connect",
          updatesPath: "/api/akapulu/updates",
        },
        connectBody: {
          tenant_id: "acme-health",
          patient_id: "patient_001",
          should_record: true,
        },
      }}
    >
      <AkapuluConversation title="Akapulu demo" />
    </AkapuluProvider>
  );
}

/* ===================== Server ===================== */

import { createAkapuluServerClient } from "@akapulu/server";

const akapulu = createAkapuluServerClient();

// Matches the JSON sent by client (e.g. `AkapuluProvider` `config.connectBody`) so the route can parse it safely.
type LocalConnectBody = {
  tenant_id?: string;
  patient_id?: string;
  should_record?: boolean;
};

// Use the incoming request body to build the Akapulu connect call; return that result as-is so the browser gets `room_url`, `token`, and `conversation_session_id`.
export async function connectRoute(body: LocalConnectBody) {
  return await akapulu.connectConversation({
    scenario_id: "YOUR_SCENARIO_ID",
    avatar_id: "YOUR_AVATAR_ID",
    runtime_vars: {
      patient_id: body.patient_id || "patient_001",
      tenant_id: body.tenant_id || "default_tenant",
    },
    record_conversation: body.should_record === true,
  });
}
```

## Related docs

* [`@akapulu/react`](/web-sdk/react-sdk)
* [Customize Conversation UI](/guides/conversations/customize-conversation-ui)
* [Connect API](/api-reference/conversations/connect)
* [Updates API](/api-reference/conversations/updates)
* [Conversation Detail API](/api-reference/conversations/detail)
* [Conversation Recording API](/api-reference/conversations/recording)
