---
title: "Publish and embed a Speak AI voice agent"
description: "Enable or disable an agent's public share link, configure the embeddable widget's look and copy, and upload a company logo for it to display."
---

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

# Publish and embed a Speak AI voice agent


import EndpointIndex from "@/components/api/EndpointIndex.astro";

The Speak AI API exposes 4 sharing endpoints under the base URL `https://api.speakai.co/v1`. Every request needs the `x-speakai-key` and `x-access-token` headers described in [Authentication](/api/authentication/).

## What can you do with the sharing endpoints?

Speak AI groups these 4 endpoints under the sharing resource. Each entry below links to the full reference for that endpoint further down this page.

| Method | Path | What it does |
| --- | --- | --- |
| `POST` | [`/voice/sharing/{agentId}/share`](#post-voice-sharing-agent-id-share) | Enable Share |
| `DELETE` | [`/voice/sharing/{agentId}/share`](#delete-voice-sharing-agent-id-share) | Disable Share |
| `POST` | [`/voice/sharing/{agentId}/company-logo/sign`](#post-voice-sharing-agent-id-company-logo-sign) | Sign Company Logo Upload |
| `PATCH` | [`/voice/sharing/{agentId}/widget-config`](#patch-voice-sharing-agent-id-widget-config) | Update Widget Config |

<h2 id="post-voice-sharing-agent-id-share">Enable Share</h2>

#### Enable Share

Requires the OWNER or ADMIN role. Turns on the agent's public share link and embeddable widget. Safe to call again to change isPublic/allowedDomains/expiresAt -- the existing shareToken and shortCode are kept rather than rotated.

### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `agentId` | path | string | Yes |  |

### Request body

Fields marked **required** are the ones the server rejects the request without. Anything conditional, where a field becomes required only alongside another, is described under Request rules above rather than marked here.

| Field | Type | Description |
| --- | --- | --- |
| `isPublic` | boolean | Defaults to false. |
| `allowedDomains` | string[] | Restrict the embeddable widget to these domains. Defaults to empty (no restriction). |
| `expiresAt` | string (date-time) |  |

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X POST 'https://api.speakai.co/v1/voice/sharing/AGENT_ID/share' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
  -H 'Content-Type: application/json' \
  -d '{}'
```

</CodePanel>

<ResponsePanel id="post-voice-sharing-agent-id-share" statuses={[{"code":"200","description":"OK"},{"code":"403","description":"Caller's role is not OWNER or ADMIN"},{"code":"404","description":"Agent not found"}]}>

<div slot="status-200">

**`200` OK**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `success` . |
| `data` | object |  |

<details>
<summary>`data` -- 6 more fields</summary>

| Field | Type | Description |
| --- | --- | --- |
| `agentId` | string |  |
| `isPublic` | boolean |  |
| `shareToken` | string | Reused across repeated enable calls once generated. Part of the public share link and the widget embed. |
| `shortCode` | string | Short, URL-friendly alias for shareToken. |
| `allowedDomains` | string[] |  |
| `expiresAt` | string (date-time) |  |

</details>

Example response, `application/json`.

```json
{
  "status": "success",
  "data": {
"agentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"isPublic": true,
"shareToken": "9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a9f8e",
"shortCode": "aB3dE7fG9h",
"allowedDomains": [],
"expiresAt": null
  }
}
```

</div>

<div slot="status-403">

**`403` Caller's role is not OWNER or ADMIN**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `message` | string |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "message": "You do not have permission to manage sharing"
}
```

</div>

<div slot="status-404">

**`404` Agent not found**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `requestId` | string |  |
| `code` | integer |  |
| `message` | string |  |
| `hints` | string[] |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "code": 404,
  "message": "Agent not found",
  "hints": []
}
```

</div>

</ResponsePanel>

</div>

<h2 id="delete-voice-sharing-agent-id-share">Disable Share</h2>

#### Disable Share

Requires the OWNER or ADMIN role. Turns off public sharing and clears shareToken/shortCode -- a later Enable Share issues new ones, so any previously distributed share link stops working permanently, not just while disabled.

### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `agentId` | path | string | Yes |  |

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X DELETE 'https://api.speakai.co/v1/voice/sharing/AGENT_ID/share' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'
```

</CodePanel>

<ResponsePanel id="delete-voice-sharing-agent-id-share" statuses={[{"code":"200","description":"OK"},{"code":"403","description":"Caller's role is not OWNER or ADMIN"},{"code":"404","description":"Agent not found"}]}>

<div slot="status-200">

**`200` OK**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `success` . |
| `message` | string |  |

Example response, `application/json`.

```json
{
  "status": "success",
  "message": "Share disabled successfully"
}
```

</div>

<div slot="status-403">

**`403` Caller's role is not OWNER or ADMIN**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `message` | string |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "message": "You do not have permission to manage sharing"
}
```

</div>

<div slot="status-404">

**`404` Agent not found**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `requestId` | string |  |
| `code` | integer |  |
| `message` | string |  |
| `hints` | string[] |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "code": 404,
  "message": "Agent not found",
  "hints": []
}
```

</div>

</ResponsePanel>

</div>

<h2 id="post-voice-sharing-agent-id-company-logo-sign">Sign Company Logo Upload</h2>

#### Sign Company Logo Upload

Requires the OWNER or ADMIN role. Issues a presigned S3 URL for uploading a widget company logo -- you upload the file bytes directly to S3 with a PUT request, then persist the returned companyLogoUrl by calling Update Widget Config yourself. This endpoint does not update the agent.

### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `agentId` | path | string | Yes |  |

### Request body

Fields marked **required** are the ones the server rejects the request without. Anything conditional, where a field becomes required only alongside another, is described under Request rules above rather than marked here.

| Field | Type | Description |
| --- | --- | --- |
| `contentType` | string, **required** | Required. One of: `image/png`, `image/jpeg`, `image/webp`, `image/svg+xml` . |

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X POST 'https://api.speakai.co/v1/voice/sharing/AGENT_ID/company-logo/sign' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
  -H 'Content-Type: application/json' \
  -d '{}'
```

</CodePanel>

<ResponsePanel id="post-voice-sharing-agent-id-company-logo-sign" statuses={[{"code":"200","description":"OK"},{"code":"400","description":"Validation failed"},{"code":"403","description":"Caller's role is not OWNER or ADMIN"},{"code":"404","description":"Agent not found"}]}>

<div slot="status-200">

**`200` OK**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `success` . |
| `data` | object |  |
| `data.uploadUrl` | string | Presigned S3 PUT URL, valid briefly. PUT the file's bytes here directly with the same contentType. |
| `data.companyLogoUrl` | string | The public CDN URL the file will be reachable at once uploaded. Save this via Update Widget Config's companyLogoUrl -- uploading here does not update the agent by itself. |
| `data.key` | string | The S3 object key the file was signed for. |

Example response, `application/json`.

```json
{
  "status": "success",
  "data": {
"uploadUrl": "https://s3.amazonaws.com/speak-uploads/voice-agents/a1b2c3d4/company-logo/1735689600000.png?X-Amz-Signature=...",
"companyLogoUrl": "https://cdn.speakai.co/voice-agents/a1b2c3d4/company-logo/1735689600000.png",
"key": "voice-agents/a1b2c3d4/company-logo/1735689600000.png"
  }
}
```

</div>

<div slot="status-400">

**`400` Validation failed**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `message` | string |  |

</div>

<div slot="status-403">

**`403` Caller's role is not OWNER or ADMIN**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `message` | string |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "message": "You do not have permission to manage sharing"
}
```

</div>

<div slot="status-404">

**`404` Agent not found**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `requestId` | string |  |
| `code` | integer |  |
| `message` | string |  |
| `hints` | string[] |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "code": 404,
  "message": "Agent not found",
  "hints": []
}
```

</div>

</ResponsePanel>

</div>

<h2 id="patch-voice-sharing-agent-id-widget-config">Update Widget Config</h2>

#### Update Widget Config

Requires the OWNER or ADMIN role. Partial update of the embeddable widget's appearance and copy -- send at least one of the 24 keys listed here. Rejects any key outside this list (`.unknown(false)` in the server's validation) rather than silently ignoring it, unlike most other partial-update endpoints in this API.

### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `agentId` | path | string | Yes |  |

### Request body

Fields marked **required** are the ones the server rejects the request without. Anything conditional, where a field becomes required only alongside another, is described under Request rules above rather than marked here.

| Field | Type | Description |
| --- | --- | --- |
| `displayMode` | string | One of: `floating`, `inline` . |
| `position` | string | One of: `bottom-left`, `bottom-right` . |
| `bubbleIconType` | string | One of: `avatar`, `generic` . |
| `buttonSize` | string | One of: `small`, `medium`, `large` . |
| `buttonColor` | string |  |
| `theme` | string | One of: `light`, `dark`, `auto` . |
| `compactMode` | boolean |  |
| `defaultMode` | string | One of: `voice`, `avatar` . |
| `showAvatar` | boolean |  |
| `fontFamily` | string |  |
| `borderRadius` | number |  |
| `backgroundColor` | string |  |
| `voiceEnabled` | boolean |  |
| `enableUserCamera` | boolean |  |
| `chatOpenByDefault` | boolean |  |
| `launcherTitle` | string |  |
| `launcherSubtitle` | string |  |
| `showLauncher` | boolean |  |
| `beforeCallTitle` | string |  |
| `beforeCallDescription` | string |  |
| `startButtonLabel` | string |  |
| `afterCallTitle` | string |  |
| `afterCallDescription` | string |  |
| `feedbackEnabled` | boolean |  |
| `companyLogoUrl` | string |  |

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X PATCH 'https://api.speakai.co/v1/voice/sharing/AGENT_ID/widget-config' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
  -H 'Content-Type: application/json' \
  -d '{}'
```

</CodePanel>

<ResponsePanel id="patch-voice-sharing-agent-id-widget-config" statuses={[{"code":"200","description":"OK"},{"code":"400","description":"No widget configuration fields provided"},{"code":"403","description":"Caller's role is not OWNER or ADMIN"},{"code":"404","description":"Agent not found"}]}>

<div slot="status-200">

**`200` OK**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `success` . |
| `data` | object |  |
| `data.widgetConfig` | object |  |

<details>
<summary>`data.widgetConfig` -- 25 more fields</summary>

| Field | Type | Description |
| --- | --- | --- |
| `displayMode` | string | One of: `floating`, `inline` . |
| `position` | string | One of: `bottom-left`, `bottom-right` . |
| `bubbleIconType` | string | One of: `avatar`, `generic` . |
| `buttonSize` | string | One of: `small`, `medium`, `large` . |
| `buttonColor` | string |  |
| `theme` | string | One of: `light`, `dark`, `auto` . |
| `compactMode` | boolean |  |
| `defaultMode` | string | One of: `voice`, `avatar` . |
| `showAvatar` | boolean |  |
| `fontFamily` | string |  |
| `borderRadius` | number |  |
| `backgroundColor` | string |  |
| `voiceEnabled` | boolean |  |
| `enableUserCamera` | boolean |  |
| `chatOpenByDefault` | boolean |  |
| `launcherTitle` | string |  |
| `launcherSubtitle` | string |  |
| `showLauncher` | boolean |  |
| `beforeCallTitle` | string |  |
| `beforeCallDescription` | string |  |
| `startButtonLabel` | string |  |
| `afterCallTitle` | string |  |
| `afterCallDescription` | string |  |
| `feedbackEnabled` | boolean |  |
| `companyLogoUrl` | string |  |

</details>

Example response, `application/json`.

```json
{
  "status": "success",
  "data": {
"widgetConfig": {
  "displayMode": "floating",
  "position": "bottom-right",
  "theme": "auto"
}
  }
}
```

</div>

<div slot="status-400">

**`400` No widget configuration fields provided**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `message` | string |  |

</div>

<div slot="status-403">

**`403` Caller's role is not OWNER or ADMIN**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `message` | string |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "message": "You do not have permission to manage sharing"
}
```

</div>

<div slot="status-404">

**`404` Agent not found**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | One of: `failed` . |
| `requestId` | string |  |
| `code` | integer |  |
| `message` | string |  |
| `hints` | string[] |  |

Example response, `application/json`.

```json
{
  "status": "failed",
  "code": 404,
  "message": "Agent not found",
  "hints": []
}
```

</div>

</ResponsePanel>

</div>

## Related pages

- [API reference](/api/) for the base URL, authentication, and the error format.
- [Authenticate with the Speak AI API using access tokens](/api/authentication/)
- [Upload audio and video to Speak AI and read insights](/api/media/)
- [Create and update live transcription sessions](/api/live-transcription/)
- [Analyze text notes with the Speak AI text endpoints](/api/text/)

Get an API key on the [Speak AI developer page](https://app.speakai.co/developers?utm_source=docs&utm_medium=referral&utm_campaign=api-reference&utm_content=api-voice-agents/sharing).

Source: https://docs.speakai.co/api/voice-agents/sharing/index.mdx
