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

# Official libraries & CLI

> Typed Dairo clients for nine languages, plus a CLI for the terminal and a hosted MCP server for AI agents.

Dairo ships typed, idiomatic clients for nine languages, plus a CLI for your
terminal and an MCP server for AI agents. They all line up: the methods,
fields, and IDs you learn in one carry over to the rest.

<CardGroup cols={3}>
  <Card title="JavaScript / TypeScript" icon="brands js" href="#javascript-typescript" />

  <Card title="Python" icon="brands python" href="#python" />

  <Card title="Go" icon="brands golang" href="#go" />

  <Card title="Ruby" icon="gem" href="#ruby" />

  <Card title="PHP" icon="brands php" href="#php" />

  <Card title="Java" icon="brands java" href="#java" />

  <Card title="Rust" icon="brands rust" href="#rust" />

  <Card title=".NET" icon="brands microsoft" href="#net" />

  <Card title="Elixir" icon="droplet" href="#elixir" />
</CardGroup>

Every client lives at
[`github.com/dairo-app`](https://github.com/dairo-app).

## Choose an interface

<CardGroup cols={3}>
  <Card title="SDKs" icon="cubes">
    Typed, idiomatic clients for your application code. Start here when you're
    writing a service or agent in a supported language.
  </Card>

  <Card title="CLI" icon="terminal" href="/agent-first/cli">
    Scripting, CI, and quick manual operations from any shell.
  </Card>

  <Card title="MCP server" icon="plug" href="/agent-first/mcp-server">
    Native tool access for Claude, Cursor, and other MCP-capable agents.
  </Card>
</CardGroup>

## JavaScript / TypeScript

```bash theme={null}
npm install dairo
```

```ts theme={null}
import { Dairo } from "dairo";

const dairo = new Dairo({ apiKey: process.env.DAIRO_API_KEY! });

const result = await dairo.messages.send({
  inboxId: "inbox_123",
  to: "ada@example.com",
  subject: "Hello from Dairo",
  text: "Hi Ada",
});
console.log(result.id, result.status, result.channel);
```

The JavaScript SDK takes `apiKey` explicitly so browser and edge runtimes stay
predictable; pass a custom `baseUrl` or `fetch` for testing. Repository:
[`dairo-app/dairo-js`](https://github.com/dairo-app/dairo-js).

## Python

```bash theme={null}
pip install dairo
```

```python theme={null}
from dairo import Dairo

dairo = Dairo()  # reads DAIRO_API_KEY, or pass api_key="dairo_live_…"

message = dairo.messages.send(
    inbox_id="inbox_123",
    to="customer@example.com",
    subject="Welcome",
    text="Thanks for trying Dairo.",
)
print(message.id, message.status)
```

Requires Python 3.10 or later. Repository:
[`dairo-app/dairo-python`](https://github.com/dairo-app/dairo-python).

## Go

```bash theme={null}
go get github.com/dairo-app/dairo-go
```

```go theme={null}
package main

import (
    "context"
    "fmt"
    "log"
    "os"

    dairo "github.com/dairo-app/dairo-go"
)

func main() {
    client := dairo.NewClient(os.Getenv("DAIRO_API_KEY"))
    out, err := client.Send(context.Background(), dairo.SendMessageRequest{
        InboxID: "inbox_123",
        To:      []string{"person@example.com"},
        Subject: "Welcome",
        Text:    "Hello from Dairo",
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(out.ID, out.Status)
}
```

Repository: [`dairo-app/dairo-go`](https://github.com/dairo-app/dairo-go).

## Ruby

```bash theme={null}
gem install dairo --pre
```

```ruby theme={null}
require "dairo"

client = Dairo::Client.new(api_key: ENV.fetch("DAIRO_API_KEY"))

response = client.send({
  inboxId: "inbox_123",
  to: ["person@example.com"],
  subject: "Hello from Dairo",
  text: "Plain text body",
  html: "<p>HTML body</p>",
})

puts response.fetch("status") # "sent"
```

Repository: [`dairo-app/dairo-ruby`](https://github.com/dairo-app/dairo-ruby).

## PHP

```bash theme={null}
composer require dairo/dairo
```

```php theme={null}
<?php

use Dairo\DairoClient;

$dairo = new DairoClient(getenv('DAIRO_API_KEY'));

$outbound = $dairo->send([
    'inboxId' => 'inbox_123',
    'to' => ['person@example.com'],
    'subject' => 'Welcome',
    'text' => 'Hello from Dairo',
    'html' => '<p>Hello from Dairo</p>',
]);

echo $outbound['status']; // sent
```

Repository: [`dairo-app/dairo-php`](https://github.com/dairo-app/dairo-php).

## Java

```xml theme={null}
<dependency>
  <groupId>app.dairo</groupId>
  <artifactId>dairo-java</artifactId>
  <version>0.1.2</version>
</dependency>
```

```java theme={null}
import app.dairo.DairoClient;

DairoClient client = new DairoClient(System.getenv("DAIRO_API_KEY"));

String response = client.send(
    "{\"inboxId\":\"inbox_123\",\"to\":[\"person@example.com\"]," +
    "\"subject\":\"Hello\",\"text\":\"Plain body\",\"html\":\"<p>HTML body</p>\"}",
    "idem-123"); // optional idempotency key
```

The Java SDK returns raw JSON strings so your code stays forward-compatible,
and ships typed shapes such as `SendWarning` and `OutboundEventData` that
document the contract. Repository:
[`dairo-app/dairo-java`](https://github.com/dairo-app/dairo-java).

## Rust

```toml theme={null}
[dependencies]
dairo = { git = "https://github.com/dairo-app/dairo-rust" }
```

```rust theme={null}
use dairo::{Client, RequestOptions, SendMessageRequest};

#[tokio::main]
async fn main() -> Result<(), dairo::DairoError> {
    let client = Client::new("dairo_live_xxx")?;
    let sent = client.send(
        &SendMessageRequest {
            inbox_id: "inbox_123".to_string(),
            to: vec!["person@example.com".to_string()],
            subject: Some("Welcome".to_string()),
            text: Some("Hello from Dairo".to_string()),
            // channel defaults to the inbox's channel ("email"); set "a2a" to target Dairo inboxes.
            ..Default::default()
        },
        RequestOptions::default(),
    ).await?;
    println!("message {} is {}", sent.id, sent.status);
    Ok(())
}
```

The library is separate from the CLI, which ships as its own binary.
Repository: [`dairo-app/dairo-rust`](https://github.com/dairo-app/dairo-rust).

## .NET

```bash theme={null}
dotnet add package Dairo
```

```csharp theme={null}
using Dairo;

var client = new DairoClient(new DairoOptions { ApiKey = "dairo_live_xxx" });

var sent = await client.SendAsync(new SendMessageRequest(
    InboxId: "inbox_123",
    To: ["person@example.com"],
    Subject: "Hello",
    Text: "Plain text body",
    Html: "<p>HTML body</p>"));

Console.WriteLine($"{sent.Id} {sent.Status}");
```

Repository: [`dairo-app/dairo-dotnet`](https://github.com/dairo-app/dairo-dotnet).

## Elixir

```elixir theme={null}
def deps do
  [
    {:dairo, "~> 0.1"}
  ]
end
```

```elixir theme={null}
client = Dairo.Client.new(System.fetch_env!("DAIRO_API_KEY"))

{:ok, response} =
  Dairo.Client.send(client, %{
    inboxId: "inbox_123",
    to: ["person@example.com"],
    subject: "Hello",
    text: "Plain-text body",
    html: "<p>HTML body</p>"
  })

response["status"]
```

Requires Elixir 1.18+ on Erlang/OTP 27+. Repository:
[`dairo-app/dairo-elixir`](https://github.com/dairo-app/dairo-elixir).

## The same in every language

Whatever client you pick, these things work identically — learn them once.

* **One authentication model.** Every client takes the same
  `dairo_live_…` API key; most examples read it from `DAIRO_API_KEY`.
* **One resource model.** Each area of the API maps to a namespace —
  `messages` (send, list, get, cancel, events), `threads`, `inboxes`,
  `domains`, `webhooks`, `apiKeys`, `templates`, `audiences`, `letters`, and
  more — snake\_case in Python, PascalCase methods in .NET.
* **Stable IDs.** `messageId`, `inboxId`, `threadId`, and event IDs are
  identical across every SDK, the REST API, the CLI, and webhook payloads.

Cross-cutting behaviors — the response envelope, idempotency, scopes, send
warnings, and one-time secrets — are the same everywhere and documented once
in [Foundations](/concepts/the-envelope).
