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

# Card Types

> All fourteen cards, every field, and the rules that block a publish

A flow is a graph of **cards**. Each card has **exits** — the edges out of it.
What an exit means depends on the card: a button on a *Buttons* card, a branch
on a *Condition*, simply "where next" on everything else.

Open the canvas at [Chat agents](https://callkaro.ai/dashboard/agents) → your
agent → **Flow**. Drag a card from the palette on the left.

<Info>
  Only **Buttons**, **Ask**, **AI assistant**, **Handoff** and **End** wait for the
  visitor. Every other card runs and moves straight on, so a chain of them
  executes in one turn and the visitor only ever sees the next card that waits.
</Info>

***

## At A Glance

| Card                | Waits? | Says something? | Exits                  |
| ------------------- | ------ | --------------- | ---------------------- |
| Start               | —      | no              | 1                      |
| Buttons             | yes    | yes             | up to 30 (the buttons) |
| Ask a question      | yes    | yes             | 1                      |
| Send message        | no     | yes             | 1                      |
| Send image or file  | no     | yes             | 1                      |
| Set a value         | no     | no              | 1                      |
| Condition           | no     | no              | up to 30 (branches)    |
| API call            | no     | no              | 2 — ok, error          |
| Run code            | no     | no              | 2 — ok, error          |
| AI assistant        | yes    | yes             | 1                      |
| Update contact      | no     | no              | 1                      |
| Handoff to a person | yes    | yes             | 1, optional            |
| Jump                | no     | no              | 1                      |
| End                 | yes    | yes             | none                   |

***

## Start

The anchor. Exactly one per flow, it renders nothing, and it takes one exit to
whatever card opens the conversation.

<Warning>
  Nothing may point **back** at Start — it renders nothing, so landing there looks
  like a stall. Publishing is blocked if anything does.
</Warning>

***

## Buttons

A message and a set of buttons. Waits for a tap.

| Field            | Notes                                                                                                                                               |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| Message          | Up to 4,000 characters. Supports [rich text](/widget/chat/appearance#rich-text-in-messages-and-buttons). Optional — buttons can stand on their own. |
| Buttons          | Up to 30. Each has a label (80 characters, rich text allowed) and a destination.                                                                    |
| Fallback message | Shown when the visitor types instead of tapping.                                                                                                    |

A button can also carry a **deep link** instead of, or as well as, a
destination — `myapp://orders/123`. A link-only button opens the link and leaves
the conversation where it is.

<Warning>
  `javascript:`, `data:`, `vbscript:`, `file:` and `blob:` links are refused at
  save time. A link with no scheme at all is treated as `https://`.
</Warning>

**Blocks publish:** a button with no text, or a button connected to nothing and
carrying no deep link.

***

## Ask A Question

A message, then a typed answer stored in a variable.

| Field           | Notes                                                                                           |
| --------------- | ----------------------------------------------------------------------------------------------- |
| Input type      | `text`, `number`, `email`, `phone`, `url`, `date`, `rating`                                     |
| Save answer in  | A variable name — letters, digits and underscores, not starting with a digit                    |
| Retry message   | Shown when the answer does not match the input type. The card asks again rather than advancing. |
| Rating out of   | 5 or 10, for `rating`                                                                           |
| Default country | Two-letter code, for `phone`                                                                    |

The input type changes the control the visitor sees — a number pad, a country
picker, a star row — and is validated server-side before the answer is stored.

<Tip>
  A `phone` ask card does something extra: once a phone number is collected, the
  whole conversation is **re-keyed onto that phone number** in your CRM, merging
  it with the same person's calls and WhatsApp. Ask for a phone number early if
  you want that merge.
</Tip>

**Blocks publish:** no message, or no variable name.

<Note>
  An ask card cannot take a file. Attachments are only possible on an AI card and
  during a human handoff.
</Note>

***

## Send Message

Says one thing and moves on. No buttons, no waiting.

Use several in a row for a paced multi-line reply — each arrives as its own
bubble, with a natural gap between them.

**Blocks publish:** no message.

***

## Send Image Or File

An image, a video or a file, with a line of text above it.

| Field   | Notes                                |
| ------- | ------------------------------------ |
| Kind    | `image`, `video` or `file`           |
| Link    | A public URL, up to 2,000 characters |
| Message | The line shown above it              |

The URL's own extension wins over the Kind setting when it is unambiguous, so a
card saved with a video link but Kind left on image still plays.

**Blocks publish:** no link, or no message.

***

## Set A Value

Writes variables without asking the visitor. Up to 20 name/value pairs, and the
values are templated — so `{{first_name}} {{last_name}}` is a valid value.

Useful for carrying a branch's meaning forward: set `topic = "billing"` on the
billing path and every later card can read it.

**Blocks publish:** sets nothing, or an unusable variable name.

***

## Condition

Branches on a variable. The **first** exit whose condition matches wins. The
last exit with no condition at all is the fallback.

| Operator    | Meaning                   |
| ----------- | ------------------------- |
| `eq`        | equals                    |
| `ne`        | does not equal            |
| `contains`  | contains                  |
| `gt`        | is more than              |
| `lt`        | is less than              |
| `empty`     | is empty or was never set |
| `not_empty` | has any value             |

**Warns (does not block):** no fallback exit, so the conversation stops when
nothing matches.

***

## API Call

An HTTP request to your system, with the response mapped into variables.

| Field   | Notes                                                 |
| ------- | ----------------------------------------------------- |
| Method  | GET, POST, PUT, PATCH, DELETE                         |
| URL     | Templated — `https://api.you.com/orders/{{order_id}}` |
| Headers | Up to 20 name/value pairs, templated                  |
| Body    | Templated, up to 4,000 characters                     |
| Map     | `variable` ← `json.path.in.response` — up to 20       |

Two exits: **ok** and **error**. A non-2xx response, a timeout or an unreachable
host takes the error exit, so a flaky API is a branch you handle rather than the
end of the conversation.

**Timeout:** 8 seconds.

**Blocks publish:** no URL, or an unusable variable name in the mapping.

***

## Run Code

Your own Python, in the middle of a flow.

```python theme={null}
async def run(variables):
    # variables is a dict of strings — everything collected so far.
    # Return a dict and it is merged back into the conversation.
    return {"tier": "gold" if variables.get("spend", "0") > "10000" else "standard"}
```

Two exits: **ok** and **error**. Raising takes the error exit.

**Timeout:** 8 seconds.

**Blocks publish:** no code, or code that does not define `async def run(`.

<Note>
  This is different from an AI card's functions, which are LangChain tools the
  model chooses to call. A Run code card always runs, at a fixed point in the
  flow. See [The AI card](/widget/chat/ai-card#functions).
</Note>

***

## AI Assistant

Hands the conversation to a model for as long as it takes. This is the biggest
card in the product and has [its own page](/widget/chat/ai-card).

| Field               | Notes                                                 |
| ------------------- | ----------------------------------------------------- |
| Opening message     | What the visitor reads as the card begins             |
| Instructions        | The system prompt for this card                       |
| Model               | Defaults to `callkaro/arjuna-2.5` on a new card       |
| Save last answer in | Optional variable                                     |
| Functions           | Up to 10 — your Python, or send-image / send-document |

The card ends when the assistant decides the conversation is over, then takes
its single exit. There is no turn cap and no "Done" button.

**Blocks publish:** no instructions.

***

## Update Contact

Writes onto the **contact record** in your CRM rather than into the conversation.
Up to 20 name/value pairs, templated.

Use it to persist what the flow learned — `plan`, `city`, `last_issue` — so it is
there next time the same person comes back.

**Blocks publish:** sets nothing, or an unusable variable name.

***

## Handoff To A Person

A person owns the conversation from here. The bot stops answering.

| Field   | Notes                                                                |
| ------- | -------------------------------------------------------------------- |
| Message | What the visitor reads while they wait                               |
| Note    | Context for the teammate who picks it up. The visitor never sees it. |

Its single exit is **optional** and is **not** walked when the card runs — it is
where the visitor lands when the teammate *ends* the chat. Leave it empty and
the conversation simply closes.

What happens when nobody is available is configured account-wide — see
[Raise a ticket](/widget/chat/ticket-fallback).

**Blocks publish:** no message, or handing back to a step that no longer exists.

***

## Jump

Nothing but an exit. It exists to keep a long edge off the canvas — a jump near
the card and a jump near the destination reads better than one line across the
whole board.

***

## End

The last thing the visitor reads. No exits.

The conversation is closed, and the panel offers **Start a new conversation**.

**Blocks publish:** no closing message.

***

## What Blocks A Publish

The builder refuses to publish while any of these is true. Warnings do not
block.

<AccordionGroup>
  <Accordion title="Errors — publishing is blocked">
    * The flow has no steps, or no Start step
    * The Start step is not connected, points at itself, or points at a step that no longer exists
    * Anything points **back** at the Start step
    * A card that speaks has no message (except Buttons, whose buttons can stand alone)
    * An Ask card with no variable
    * An API card with no URL
    * A media card with no link
    * A Run code card with no code, or code missing `async def run(`
    * An AI card with no instructions
    * A Set or Update contact card that sets nothing
    * An unusable variable name anywhere
    * Two exits on one card sharing an id
    * A button with no text
    * Anything connected to nothing, or to a step that no longer exists
    * A card with no exits at all
  </Accordion>

  <Accordion title="Warnings — publishing still works">
    * A Condition card with no fallback exit
    * Cards that cannot be reached from the Start step (a branch you are still building)
  </Accordion>
</AccordionGroup>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="The AI card" icon="sparkles" href="/widget/chat/ai-card">
    The one card with a page of its own
  </Card>

  <Card title="Variables and identity" icon="brackets-curly" href="/widget/chat/variables">
    What `{{...}}` can reach
  </Card>
</CardGroup>
