> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.getdial.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.getdial.ai/_mcp/server.

# CrewAI integration

> Give a CrewAI crew Dial tools for SMS, OTP, and voice calls — phone identity for multi-agent web tasks.

`dial-crewai` packages Dial's operations as [CrewAI](https://crewai.com) tools, so a
multi-agent crew can send messages, receive OTP codes, and place calls as part of its
work. It's the CrewAI sibling of the [LangChain integration](/documentation/sdks/langchain):
each tool is a `crewai.tools.BaseTool` subclass wrapping the [Python SDK](/documentation/sdks/python)
client — it adds nothing to the REST contract, it just shapes Dial's operations into
CrewAI tools.

## Install

```bash
pip install dial-crewai
```

This pulls in `dial-sdk` and `crewai`.

## Give the tools to a crew

Build one [`DialClient`](/documentation/sdks/python), pass it to `dial_tools`, and hand
the list to an agent. Every tool shares that one client — a single connection pool for
the whole crew:

```python
from crewai import Agent
from dial_sdk import DialClient, DialConfig
from dial_crewai import dial_tools

dial = DialClient(DialConfig(api_key="sk_live_..."))

phone_agent = Agent(
    role="Phone identity agent",
    goal="Send and receive SMS so the crew can complete phone-verified signups",
    backstory="You operate the crew's Dial phone number.",
    tools=dial_tools(dial),
)
```

`dial_tools` returns the full set, all sharing your client — the analog of LangChain's
`DialToolkit.get_tools()`. CrewAI agents take a plain `tools=[...]` list.

## Available tools

Each tool takes your shared `DialClient` (`client=`):

| Tool                      | Action                                                |
| ------------------------- | ----------------------------------------------------- |
| `ListNumbersTool`         | List your phone numbers                               |
| `PurchaseNumberTool`      | Provision a new number (billable)                     |
| `SetNumberPropertiesTool` | Update a number's nickname or inbound instruction     |
| `SendMessageTool`         | Send an SMS                                           |
| `ListMessagesTool`        | List recent messages                                  |
| `MakeCallTool`            | Place an AI voice call                                |
| `ListCallsTool`           | List recent calls                                     |
| `GetCallTool`             | Fetch one call by id                                  |
| `GetBillingTool`          | Credit balance, subscription, per-number mode         |
| `WaitForMessageTool`      | Block until the next inbound SMS arrives, or time out |

### Or pick individual tools

```python
from dial_crewai import SendMessageTool, WaitForMessageTool

tools = [
    SendMessageTool(client=dial),
    WaitForMessageTool(client=dial),
]
```

## Handling OTP

The reason a crew needs phone identity: receive a verification code and act on it.
`WaitForMessageTool` blocks until the next inbound SMS arrives, so an agent can trigger
a signup that texts a code to your Dial number, read the code, and enter it back into
the form.

```python
from crewai import Agent, Crew, Task
from dial_sdk import DialClient, DialConfig
from dial_crewai import SendMessageTool, WaitForMessageTool

dial = DialClient(DialConfig(api_key="sk_live_..."))

signup_agent = Agent(
    role="Signup agent",
    goal="Complete a phone-verified signup by reading the OTP sent to our Dial number",
    backstory="You wait for verification codes on the crew's Dial number and read them back.",
    tools=[SendMessageTool(client=dial), WaitForMessageTool(client=dial)],
)

wait_for_otp = Task(
    description="Wait up to 120 seconds for the inbound verification code on our Dial number, then report it.",
    expected_output="The verification code that was texted to our number.",
    agent=signup_agent,
)

Crew(agents=[signup_agent], tasks=[wait_for_otp]).kickoff()
```

A full runnable example is in [`examples/signup_crew.py`](https://github.com/GENWAY-AI/dial/blob/main/sdk-crewai/examples/signup_crew.py).

The `dial-sdk` client is async; these tools are sync-first (`_run`) with an async→sync
bridge, and also expose `_arun` for async crews. `SendMessageTool` is a write action and
**isn't idempotent** — a re-invoke after a failure can send a duplicate. `MakeCallTool`
accepts an `idempotency_key`. See [Retries and idempotency](/documentation/reference/errors#retries-and-idempotency).

## Receiving events beyond a single wait

`WaitForMessageTool` is a one-shot wait, backed by a **presence-based** stream (not
at-least-once — missed events replay only on reconnect within \~2 minutes). To react to
inbound SMS or completed calls durably, use the [Python SDK's](/documentation/sdks/python)
`new_events_connection()` directly, or register a [webhook](/documentation/platform/webhooks)
(signed and retried at-least-once), and feed events into your crew however suits your app.