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

# SDKs & languages

> Official typed SDKs for TypeScript and Python, a full CLI, the MCP server, and the raw REST contract you can call from any language.

Every Drin capability is reachable from one account and one key. Use the typed
SDK in your language, the CLI for scripts and CI, the MCP server for agents — or
talk to the REST API directly from anything that speaks HTTP.

<CardGroup cols={3}>
  <Card title="TypeScript / Node" icon="https://mintcdn.com/drin/yBW6bGNHvna53cF7/images/lang/typescript.svg?fit=max&auto=format&n=yBW6bGNHvna53cF7&q=85&s=1eb07f5f537d32ecf74206a8a172a5f6" href="#typescript" width="128" height="128" data-path="images/lang/typescript.svg">
    `@drin00/sdk` — typed client with retries, cursor pagination, and webhook verification.
  </Card>

  <Card title="Python" icon="https://mintcdn.com/drin/yBW6bGNHvna53cF7/images/lang/python.svg?fit=max&auto=format&n=yBW6bGNHvna53cF7&q=85&s=f7484c64547760eaca1c8c0180345168" href="#python" width="128" height="128" data-path="images/lang/python.svg">
    `pip install drin` — the official Python client, same surface as the TS SDK.
  </Card>

  <Card title="Go" icon="https://mintcdn.com/drin/yBW6bGNHvna53cF7/images/lang/go.svg?fit=max&auto=format&n=yBW6bGNHvna53cF7&q=85&s=d63537753d936b3f29b79d960b9cb707" href="#rest" width="128" height="128" data-path="images/lang/go.svg">
    Call the REST API with the standard `net/http` client — worked example below.
  </Card>

  <Card title="Ruby" icon="https://mintcdn.com/drin/yBW6bGNHvna53cF7/images/lang/ruby.svg?fit=max&auto=format&n=yBW6bGNHvna53cF7&q=85&s=aad233285f87ccff1a5c9ba97d408e1f" href="#rest" width="128" height="128" data-path="images/lang/ruby.svg">
    Use `Net::HTTP` or your HTTP client of choice against the REST contract.
  </Card>

  <Card title="PHP" icon="https://mintcdn.com/drin/yBW6bGNHvna53cF7/images/lang/php.svg?fit=max&auto=format&n=yBW6bGNHvna53cF7&q=85&s=70c89b1ff5053e5054a445231800c865" href="#rest" width="128" height="128" data-path="images/lang/php.svg">
    A few lines of `curl` or Guzzle — the same JSON body as everywhere else.
  </Card>

  <Card title="cURL / REST" icon="terminal" href="#rest">
    No dependency. The raw HTTP contract, callable from any stack.
  </Card>
</CardGroup>

## Install

<CodeGroup>
  ```bash TypeScript theme={null}
  npm install @drin00/sdk
  ```

  ```bash Python theme={null}
  pip install drin
  ```

  ```bash CLI theme={null}
  npx -y @drin00/cli send --to you@example.com --subject "Hi" --text "It works"
  ```
</CodeGroup>

<a id="typescript" />

## TypeScript / Node

The official client mirrors the wire contract one-to-one, with built-in
retries, async-iterator pagination, typed errors, and local webhook
verification.

```ts theme={null}
import { DrinClient } from "@drin00/sdk";

const drin = new DrinClient({ apiKey: process.env.DRIN_API_KEY! });

const { id } = await drin.emails.send({
  from: { email: "hello@acme.com", name: "Acme" },
  to: [{ email: "customer@example.com" }],
  subject: "Welcome aboard",
  html: "<h1>You're in</h1>",
});
```

<a id="python" />

## Python

Same surface, Pythonic names. Published to PyPI as `drin`.

```python theme={null}
import os
from drin import DrinClient

drin = DrinClient(api_key=os.environ["DRIN_API_KEY"])

msg = drin.emails.send(
    from_={"email": "hello@acme.com", "name": "Acme"},
    to=[{"email": "customer@example.com"}],
    subject="Welcome aboard",
    html="<h1>You're in</h1>",
)
print(msg["id"])
```

<a id="rest" />

## Any language (REST)

No SDK? Every endpoint is a plain JSON request over HTTPS. Here's the same send
in Go, Ruby, and PHP.

<CodeGroup>
  ```go Go theme={null}
  body, _ := json.Marshal(map[string]any{
      "from":    "Acme <hello@acme.com>",
      "to":      "customer@example.com",
      "subject": "Welcome aboard",
      "html":    "<h1>You're in</h1>",
  })
  req, _ := http.NewRequest("POST", "https://api.drin.run/v1/emails", bytes.NewReader(body))
  req.Header.Set("Authorization", "Bearer "+os.Getenv("DRIN_API_KEY"))
  req.Header.Set("Content-Type", "application/json")
  resp, _ := http.DefaultClient.Do(req)
  ```

  ```ruby Ruby theme={null}
  require "net/http"
  require "json"

  uri = URI("https://api.drin.run/v1/emails")
  req = Net::HTTP::Post.new(uri, {
    "Authorization" => "Bearer #{ENV['DRIN_API_KEY']}",
    "Content-Type"  => "application/json",
  })
  req.body = { from: "Acme <hello@acme.com>", to: "customer@example.com",
               subject: "Welcome aboard", html: "<h1>You're in</h1>" }.to_json
  Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
  ```

  ```php PHP theme={null}
  $ch = curl_init("https://api.drin.run/v1/emails");
  curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
      "Authorization: Bearer " . getenv("DRIN_API_KEY"),
      "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
      "from" => "Acme <hello@acme.com>",
      "to" => "customer@example.com",
      "subject" => "Welcome aboard",
      "html" => "<h1>You're in</h1>",
    ]),
  ]);
  $response = curl_exec($ch);
  ```
</CodeGroup>

<Note>
  Account-wide keys also take a `sender` (your project's external id) so requests
  are scoped correctly. Project-scoped keys don't need it.
</Note>
