# Releases

> Create, configure, publish, and archive AI assistant Releases.

A **Release** is a deployable AI assistant configuration — its model, prompt, RAG
indexes, theme, and access rules. Your end users chat against a Release id;
`divinci.releases` is how you create and manage them.

## Listing releases in a workspace

```typescript
const { data, hasMore } = await divinci.releases.list("ws_123", { limit: 20 });
```

## Creating a release

```typescript
const release = await divinci.releases.create({
  workspaceId: "ws_123",
  name: "Support Assistant",
  // ...model, prompt, and config fields
});

console.log(release._id); // the releaseId your client/embed uses
```

## Reading and updating

```typescript
const release = await divinci.releases.get("rel_abc123");

// Update a release (top-level)
await divinci.releases.update("rel_abc123", { name: "Support Assistant v2" });

// Or update a release within an explicit workspace
await divinci.releases.updateInWorkspace("ws_123", "rel_abc123", { name: "v2" });
```

<Aside type="caution" title="Partial updates and RAG">
  Send only the fields you intend to change. The update path treats RAG fields
  (`ragIndexes`, `ragVectorGroupId`) as present-only — supplying a partial body
  that omits them will not clear them, but do verify your update payloads when
  scripting bulk changes to release configuration.
</Aside>

## Anonymous chat

A Release can serve **anonymous, no-login chat** — the pattern behind public
marketing pages and embedded landing-page widgets (see the
[Deploy to Cloudflare Workers](/guides/cloudflare-workers/) guide). Three fields
control it, and they layer:

| Field | Default | Purpose |
| --- | --- | --- |
| `allowAnonymousChat` | `false` | **Master switch.** While `false`, anonymous chat is rejected with `403 FORBIDDEN`. |
| `requireSignedAnonymousChat` | `false` | When `true`, anonymous-chat calls must carry a valid landing-page HMAC signature, so only your signing proxy (e.g. a Cloudflare Worker) can reach the endpoint. Only has an effect while `allowAnonymousChat` is `true`. |
| `maxAnonymousChatMessages` | `1` | Per-conversation message cap. Raise it for multi-turn. |

```typescript
// Secure public anonymous chat: on, signed, multi-turn
await divinci.releases.updateInWorkspace(workspaceId, "rel_abc123", {
  allowAnonymousChat: true,
  requireSignedAnonymousChat: true,
  maxAnonymousChatMessages: 12,
});
```

<Aside type="caution" title="update() is not a partial PATCH">
For single-field changes, always use `updateInWorkspace(workspaceId, releaseId,
options)` — it GET-then-merges the full release document before saving. The
plain `update(releaseId, options)` method hits the v1 consumer endpoint, whose
handler requires a **full body** and rejects partial payloads (a body without
`assistant` fails with "Bad assistant").
</Aside>

<Aside type="caution" title="Why require signing">
The per-visitor free-message quota is enforced by your **front-end proxy**, not
the API. With `requireSignedAnonymousChat: false`, anyone who knows the release
id can call the anonymous-chat endpoint directly and burn your wallet/quota,
bypassing that gate. For any release fronting a public page, set it `true` and
share the `LANDING_PAGE_HMAC_KEY` only with your proxy — see the
[Cloudflare Workers guide](/guides/cloudflare-workers/) for the signing details.
</Aside>

## MCP server config

A Release can be published as its own **MCP server** — a curated, tenant-scoped
endpoint your consumers connect their AI assistants to. Set the `mcpConfig`
object on the release to enable it and choose which tools, guardrails, and
billing apply:

```typescript
await divinci.releases.updateInWorkspace(workspaceId, "rel_abc123", {
  mcpConfig: {
    enabled: true,
    exposedTools: ["search_knowledge", "send_message"], // omit = all release tools
    allowAnonymousMcp: false,
    maxSpendPerTokenCents: 500,            // $5.00 cap per token
    mcpRateLimit: { requestsPerMinute: 60, requestsPerDay: 10000 },
  },
});
```

| Field | Default | Purpose |
| --- | --- | --- |
| `enabled` | `false` | **Master switch.** While `false`, the tenant MCP endpoint returns `404`. |
| `exposedTools` | *(all)* | Allowlist of tool names. Empty/unset exposes all of the release's tools. |
| `allowedScopes` | *(all)* | Subset of platform scopes the release's tokens may carry. |
| `allowAnonymousMcp` | `false` | Allow connections with **no** API key or OAuth token. Public/read-only products only. |
| `maxSpendPerTokenCents` | `1000` | Billing safety cap per token (1000 = $10.00). |
| `mcpRateLimit` | `{ 60/min, 10000/day }` | Per-minute and per-day request ceilings. |

Once enabled and published, consumers connect to
`https://mcp.divinci.app/{whitelabelId}/mcp`. See
**[Release as an MCP Server](/mcp/whitelabel-servers)** for the endpoint,
authentication options (API key, central OAuth, or your own federated IdP), and
billing modes.

## Lifecycle

```typescript
await divinci.releases.activate("rel_abc123"); // make it the live release
await divinci.releases.archive("rel_abc123");  // retire it
await divinci.releases.delete("rel_abc123");
```

## Method reference

| Method | Description |
|--------|-------------|
| `list(workspaceId, options?)` | Paginated releases in a workspace. |
| `get(releaseId)` | Fetch a release. |
| `create(options)` | Create a release (requires `workspaceId`). |
| `update(releaseId, options)` | Update a release. |
| `updateInWorkspace(workspaceId, releaseId, options)` | Update within an explicit workspace. |
| `activate(releaseId)` | Mark a release active. |
| `archive(releaseId)` | Archive a release. |
| `delete(releaseId)` | Delete a release. |

<Aside type="note">
  `clone()` and `getAnalytics()` exist on the client surface but are not yet
  available on the API and currently throw. Track the changelog for availability.
</Aside>
