---
title: 'Tutorial: Publish a flow as a chat model'
path: tutorials/publish-as-chat-model
status: published
---

# Tutorial: Publish a flow as a chat model

Take any deployed Core and register it as a chat model that OpenAI-compatible clients can address by name.

## What this gets you

After publishing, your Core is reachable at:

```http
POST https://scaigrid.scailabs.ai/v1/chat/completions
model: scaicore/{your-tenant-slug}/{core-slug}
```

Any client that speaks the OpenAI chat completions protocol (the OpenAI SDK in any language, ScaiBot, custom integrations) can use it. Multi-turn conversations work via `entity_id` (entity-mode Cores) or stateless (default).

## Steps

### 1. Open your flow

Load the flow you want to publish — for this tutorial, the [Hello World](./hello-world) Echo Agent works fine. Anything with an `entry_api` trigger will do.

### 2. Toggle the flag

In the Flow properties panel (click empty canvas to deselect), check **Publish as chat model**.

A row appears underneath: **Visibility group IDs (comma-separated)**. Leave empty to make the chat model visible to your whole tenant. Restrict to specific ScaiKey group IDs (`group_abc, group_xyz`) if only a subset should be allowed to call it — members of any listed group will be able to reach it, others get a 403.

### 3. Save + Deploy

Click **Save**, then **Deploy**. The toast now reads:

```text
Deployed core_id=core_abc123 — published as scaicore/acme/echo-agent
```

The `published as` suffix appears because the deploy step ran a second call (`POST /v1/modules/scaicore/cores/{core_id}/publish`) after creating the Core.

### 4. Call it

```bash
TENANT="acme"
SLUG="echo-agent"

curl -X POST "https://scaigrid.scailabs.ai/v1/chat/completions" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "scaicore/'"$TENANT"'/'"$SLUG"'",
    "messages": [
      {"role": "user", "content": "hello"}
    ]
  }'
```

Using the OpenAI Python SDK:

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://scaigrid.scailabs.ai/v1",
    api_key=os.environ["SCAIGRID_TOKEN"],
)

resp = client.chat.completions.create(
    model=f"scaicore/{tenant}/{slug}",
    messages=[{"role": "user", "content": "hello"}],
)
print(resp.choices[0].message.content)
```

## How the request reaches your flow

ScaiGrid's chat completions endpoint resolves `scaicore/{tenant}/{slug}` to the published Core. It maps the chat-format request into a Core invocation:

- The latest user message text becomes `input.message`.
- The full message history is available to the Core as `input.messages` (for stateful conversation logic).
- The response from the Core (typically the `response` field on the output) is returned as the assistant message.

Your flow can opt into the full message history by looking at `messages` in its input scope, or stay stateless by only consuming `message`.

## Conversational state

By default, Cores are stateless — each chat request stands alone. For multi-turn memory:

1. Set the Core's instance mode to `entity` (currently set via `manifest.scaiflow_meta` — canvas doesn't expose it yet).
2. The client includes `entity_id` in the request (typically the conversation/user id).
3. The Core's state persists across requests with the same `entity_id`.

See [ScaiCore: Instance modes](/docs/scaicore) for the full mechanics.

## Toggling publish off

Setting `publish_as_model` back to `false` and re-deploying does NOT currently un-publish — that requires a manual call:

```bash
curl -X DELETE "https://scaigrid.scailabs.ai/v1/modules/scaicore/cores/${CORE_ID}/publish" \
  -H "Authorization: Bearer ${TOKEN}"
```

This will be addressed in a future release (the deploy step will detect the transition and call delete-publish). See [Troubleshooting: changing publish state](../troubleshooting/deploy-failures).

## Visibility scoping with groups

```jsonc
"publish_as_model": true,
"model_visibility_group_ids": ["group_acme_eng", "group_acme_ops"]
```

ScaiGrid forwards these to its model-visibility layer. A user calling `/v1/chat/completions` with `model: scaicore/acme/echo-agent` must be a member of at least one of the listed ScaiKey groups, or the call returns 403. Empty list = all tenant users.

## Next

- **[ScaiCore docs](/docs/scaicore)** — for instance modes, multi-turn state, and the underlying runtime details.
- **[ScaiBot docs](/docs/scaigrid/scaibot)** — for integrating your published Core into ScaiLabs' chat experience.
