# Send your first email

> Authenticate with an API key, send an email, follow what happens to it, and the conventions every request shares.

Source: https://docs.minket.com/start/
Updated: 2026-10-30

You need an account, an API key and a sending domain. Sign in to the console at `https://console.minket.com` with a code sent to your email address, or with Google; the first sign-in creates your account and your organization. Create the key in the console under Keys ([API keys](https://docs.minket.com/keys/)) and verify the domain ([Set up a sending domain](https://docs.minket.com/domains/)). A test key needs no domain: it can send from `minket.test`.

## Send

```sh tab=curl
curl https://api.minket.com/v1/emails \
  -H "Authorization: Bearer $MINKET_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: welcome-7f3a9c" \
  -d '{
    "from": "Acme <hello@mail.acme.example>",
    "to": ["jane@example.com"],
    "subject": "Welcome",
    "text": "Thanks for signing up."
  }'
```

```ts tab
import { Minket } from "@minket/sdk";

const minket = new Minket({ apiKey: process.env.MINKET_API_KEY ?? "" });

const email = await minket.emails.send(
  {
    from: "Acme <hello@mail.acme.example>",
    to: ["jane@example.com"],
    subject: "Welcome",
    text: "Thanks for signing up.",
  },
  { idempotencyKey: "welcome-7f3a9c" },
);
```

```json
{
  "id": "019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b",
  "object": "email",
  "status": "queued",
  "scheduled_at": null,
  "idempotent_replay": false
}
```

`from` must be on a verified domain of the key's project, or be a connected mailbox. The `Idempotency-Key` makes a retry safe: the same key and body answer with this result instead of sending again ([Idempotency](https://docs.minket.com/sending/#idempotency)).

## Try it without sending

A `minket_test_…` key takes the same request, stores the email and plays its events back, and delivers nothing. Send to `delivered@minket.test`, `bounced@minket.test`, `complained@minket.test` or `delayed@minket.test` to see each outcome ([Test mode](https://docs.minket.com/sending/#test-mode)).

## Follow it

`queued` means accepted. What happens next arrives as events: [by webhook](https://docs.minket.com/webhooks/) as it happens, from [the event feed](https://docs.minket.com/events/#the-feed), or for one email with `GET /v1/emails/{id}/events`.

## Conventions

### Authentication

Send the key as a bearer token on every request:

```http
Authorization: Bearer minket_live_…
```

| Problem | Response |
| --- | --- |
| No `Authorization` header, or not a `Bearer` token | `401 missing_api_key` |
| Malformed, revoked or unknown key | `403 invalid_api_key` |
| The key's scope does not cover the route | `401 restricted_api_key` |

### Requests and responses

Requests and responses are JSON with snake_case fields. Ids are UUIDs (version 7, so they sort by creation time). Timestamps are ISO 8601 with a timezone, such as `2026-10-24T18:00:00.000Z`. Every response carries an `x-request-id`, and every failure has one shape with a `retryable` flag ([Error codes and retries](https://docs.minket.com/errors/)).

### Pagination

Every list takes `limit` (1 to 100, default 20) and a cursor. Pages are newest first: `after=<id>` continues to older items, `before=<id>` returns newer ones.

```sh
curl "https://api.minket.com/v1/emails?limit=20&after=019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b" \
  -H "Authorization: Bearer $MINKET_API_KEY"
```

```json
{ "object": "list", "data": [], "has_more": false }
```

Pass the `id` of the last item as `after` while `has_more` is true. There are no page numbers and no totals.

### Environments

Each project has a `production` and a `preview` environment with their own keys, emails and webhook endpoints, so a preview deployment's mail stays apart from production's; domains, templates, contacts and suppressions belong to the project and are shared.

### Rate limits

Each key has a per-second request limit and each project a daily cap on live mail; [Where limits apply](https://docs.minket.com/limits/#where-limits-apply) has both.
