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

# Authentication

> Bearer API keys, project scoping, the X-Drin-Product header, and idempotent requests.

Every request authenticates with an API key sent as a Bearer token. Account-wide keys name the sending project per request; project-scoped keys don't.

Send the key in the `Authorization` header:

```http Authorization theme={null}
Authorization: Bearer $DRIN_API_KEY
```

Create and revoke keys in the dashboard under [API Keys](https://app.drin.run/api-keys), or over the API (`POST /v1/api-keys`). The secret is shown **once** at creation — store it immediately; you can't read it back.

<Warning>
  **Keep keys server-side.** API keys are secrets. Use them only from server-side code, never in a browser, mobile app, or anything shipped to a user. If a key leaks, revoke it and mint a new one.
</Warning>

## Choosing the sending project

A Drin account can hold several **projects**, each with its own sender identity, domains, and keys. Two kinds of key decide how the project is chosen.

<CardGroup cols={2}>
  <Card title="Project-scoped key" icon="lock">
    Tied to one project. The project is implied — send with just the `Authorization` header, nothing else.
  </Card>

  <Card title="Account-wide key" icon="key">
    Spans every project. You must name the project per request so Drin knows who's sending.
  </Card>
</CardGroup>

Name the project on an account-wide key with the `X-Drin-Product` header:

```http Headers theme={null}
Authorization: Bearer $DRIN_API_KEY
X-Drin-Product: my-project        # the project's external id
```

<Note>
  **Header aliases.** `X-Drin-Product` is the canonical header. `X-Drin-Sender` is an accepted alias — it's what the SDK's `sender` option, the CLI's `--sender` flag, and the `DRIN_SENDER` env var send. All three name the same thing.
</Note>

In the SDK, pass `sender` once when you construct the client:

```typescript Node.js theme={null}
const drin = new DrinClient({
  apiKey: process.env.DRIN_API_KEY,
  sender: "my-project", // only needed for account-wide keys
});
```

## Request headers

| Header            | Required            | Purpose                                             |
| ----------------- | ------------------- | --------------------------------------------------- |
| `Authorization`   | Always              | `Bearer <api-key>`.                                 |
| `Content-Type`    | On `POST` / `PATCH` | `application/json`.                                 |
| `X-Drin-Product`  | Account-wide keys   | Names the sending project (alias: `X-Drin-Sender`). |
| `Idempotency-Key` | Optional            | Safe-retry a `POST` (see below).                    |

## Idempotency

Pass an `Idempotency-Key` on any `POST` to make it safe to retry — if the same key arrives twice, Drin returns the original result instead of sending again. Keys are honored for **24 hours**, per sending project.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.drin.run/v1/emails \
    -H "Authorization: Bearer $DRIN_API_KEY" \
    -H "Idempotency-Key: order-1234-receipt" \
    -H "Content-Type: application/json" \
    -d '{
      "from": { "email": "hi@acme.com" },
      "to": [{ "email": "a@b.com" }],
      "subject": "Receipt",
      "html": "<p>Thanks!</p>"
    }'
  ```

  ```typescript Node.js theme={null}
  await drin.emails.send(
    {
      from: { email: "hi@acme.com" },
      to: [{ email: "a@b.com" }],
      subject: "Receipt",
      html: "<p>Thanks!</p>",
    },
    { idempotencyKey: "order-1234-receipt" },
  );
  ```
</CodeGroup>

The SDK automatically retries transient failures (`429` and `5xx`) with exponential backoff — and it only retries a `POST` when you've supplied an `Idempotency-Key`, so a send is never duplicated. See [Errors](/errors) for the full retry behaviour.
