> ## 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.

# Quickstart

> Send your first email in under a minute — get a key, copy a snippet, watch it land, then send from your own domain.

Send your first email in under a minute. You need two things — an API key and a from address — and Drin gives you a shared onboarding domain so you can send before touching DNS.

<Steps>
  <Step title="Get an API key">
    Create one in the dashboard under [API Keys](https://app.drin.run/api-keys). The secret is shown **once** — store it somewhere safe, for example as `DRIN_API_KEY`.

    <Note>
      **Already have one.** New accounts get a key automatically during onboarding, baked into the in-dashboard snippets. This page is the version you can copy anywhere.
    </Note>
  </Step>

  <Step title="Send your first email">
    Every snippet hits the same contract: `POST https://api.drin.run/v1/emails` with a `from`, a `to` array, and `subject` + `html` (or `text`).

    <CodeGroup>
      ```bash cURL theme={null}
      curl https://api.drin.run/v1/emails \
        -H "Authorization: Bearer $DRIN_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "from": { "email": "onboarding@yourdomain.com" },
          "to": [{ "email": "you@example.com" }],
          "subject": "Hello from Drin",
          "html": "<p>It works!</p>"
        }'
      ```

      ```typescript Node.js theme={null}
      // npm install @drin00/sdk
      import { DrinClient } from "@drin00/sdk";

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

      await drin.emails.send({
        from: { email: "onboarding@yourdomain.com" },
        to: [{ email: "you@example.com" }],
        subject: "Hello from Drin",
        html: "<p>It works!</p>",
      });
      ```

      ```python Python theme={null}
      import os, requests

      requests.post(
          "https://api.drin.run/v1/emails",
          headers={"Authorization": f"Bearer {os.environ['DRIN_API_KEY']}"},
          json={
              "from": {"email": "onboarding@yourdomain.com"},
              "to": [{"email": "you@example.com"}],
              "subject": "Hello from Drin",
              "html": "<p>It works!</p>",
          },
      )
      ```

      ```go Go theme={null}
      payload := []byte(`{
        "from": { "email": "onboarding@yourdomain.com" },
        "to": [{ "email": "you@example.com" }],
        "subject": "Hello from Drin",
        "html": "<p>It works!</p>"
      }`)
      req, _ := http.NewRequest("POST", "https://api.drin.run/v1/emails", bytes.NewReader(payload))
      req.Header.Set("Authorization", "Bearer "+os.Getenv("DRIN_API_KEY"))
      req.Header.Set("Content-Type", "application/json")
      http.DefaultClient.Do(req)
      ```

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

      uri = URI("https://api.drin.run/v1/emails")
      http = Net::HTTP.new(uri.host, uri.port); http.use_ssl = true
      req = Net::HTTP::Post.new(uri)
      req["Authorization"] = "Bearer #{ENV.fetch('DRIN_API_KEY')}"
      req["Content-Type"] = "application/json"
      req.body = {
        from: { email: "onboarding@yourdomain.com" },
        to: [{ email: "you@example.com" }],
        subject: "Hello from Drin", html: "<p>It works!</p>"
      }.to_json
      http.request(req)
      ```

      ```php PHP theme={null}
      <?php
      $ch = curl_init("https://api.drin.run/v1/emails");
      curl_setopt_array($ch, [
          CURLOPT_RETURNTRANSFER => true,
          CURLOPT_POST => true,
          CURLOPT_HTTPHEADER => [
              "Authorization: Bearer " . getenv("DRIN_API_KEY"),
              "Content-Type: application/json",
          ],
          CURLOPT_POSTFIELDS => json_encode([
              "from" => ["email" => "onboarding@yourdomain.com"],
              "to" => [["email" => "you@example.com"]],
              "subject" => "Hello from Drin",
              "html" => "<p>It works!</p>",
          ]),
      ]);
      echo curl_exec($ch);
      ```
    </CodeGroup>

    A `202 Accepted` with a message `id` means it's queued. Java, C#, Rust, Elixir, Kotlin, Swift and a no-code **SMTP** recipe are all in the dashboard's in-app quickstart too.
  </Step>

  <Step title="Watch it land">
    Every send shows up on [Email Activity](https://app.drin.run/activity) within seconds, with its full transit log — queued → sent → delivered → opened. Or fetch it over the API: `GET /v1/emails/{id}`.
  </Step>

  <Step title="Send from your own domain">
    The shared onboarding domain runs in **test mode** — it can only deliver to your own address. To email anyone from your own brand, [verify a domain](/domains) on the [Domains](https://app.drin.run/domains) page (DKIM + SPF + DMARC, guided). Then list your verified domains in code and use one as your `from`:

    ```typescript Node.js theme={null}
    const [d] = await drin.domains.listVerified();

    await drin.emails.send({
      from: { email: `hello@${d.domain}` },
      to: [{ email: "customer@example.com" }],
      subject: "Welcome aboard",
      html: "<p>Glad you're here.</p>",
    });
    ```
  </Step>
</Steps>

<Info>
  **Account-wide keys.** A key that isn't scoped to a single project must name the sending project per request with the `X-Drin-Product: <project-id>` header — or the SDK's `sender` option. Project-scoped keys don't need it. See [Authentication](/authentication).
</Info>

## Next steps

<CardGroup cols={2}>
  <Card title="Templates" icon="file-lines" href="/templates">
    Store reusable HTML with `{{merge}}` variables and send by `templateId`.
  </Card>

  <Card title="Receive email" icon="inbox" href="/receiving">
    Turn on receiving, create an inbox, and reply in-thread.
  </Card>

  <Card title="Webhooks" icon="bell" href="/webhooks">
    Get delivery, bounce, complaint, open and click events in real time.
  </Card>

  <Card title="Give an agent email" icon="plug" href="/agents/mcp">
    Wire the MCP server into Claude, Cursor, or your own agent.
  </Card>
</CardGroup>
