# API quickstart: your first API call

Source: https://help.meisa.io/articles/api-quickstart

The Meisa API lives at `https://api.meisa.io/api/v1` and every call sends your key in the `X-API-Key` header. Create a key, call `/ping/` to check it works, then add a contact and send your first email.

## Step 1: Create an API key

1. Go to **Settings**, then **API keys and webhooks**.
2. Click **Create API key**, name it, and pick **Everything** under **Permissions** for this test. Use narrower permissions in production.
3. Click **Create key** and **Copy key**.

Full details: [Create and manage API keys](/articles/create-an-api-key).

Store the key in an environment variable so it stays out of your code:

```bash
export MEISA_API_KEY="meisa_live_your_key"
```

## Step 2: Check the key

```bash
curl "https://api.meisa.io/api/v1/ping/" \
  -H "X-API-Key: $MEISA_API_KEY"
```

You get back `"status": "ok"`, the key's permissions and the product it belongs to. If you see `401`, the key is wrong or revoked.

## Step 3: Add a contact

```bash
curl -X POST "https://api.meisa.io/api/v1/contacts/upsert/" \
  -H "X-API-Key: $MEISA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "email": "you@yourcompany.com", "first_name": "Sam", "source": "api" }'
```

Upsert creates the contact or updates it if it already exists. Use your own address for this test.

## Step 4: Send a transactional email

First create a transactional email in Meisa with the key `welcome` (see [Send transactional emails with triggers](/articles/send-transactional-email)). Then:

```bash
curl -X POST "https://api.meisa.io/api/v1/emails/trigger/" \
  -H "X-API-Key: $MEISA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "trigger_key": "welcome", "to_email": "you@yourcompany.com", "variables": { "first_name": "Sam" } }'
```

A `200` with `"status": "sent"` means it's on its way. Check your inbox.

## The same calls in JavaScript

```js
const MEISA = 'https://api.meisa.io/api/v1';
const headers = { 'X-API-Key': process.env.MEISA_API_KEY, 'Content-Type': 'application/json' };

await fetch(`${MEISA}/contacts/upsert/`, {
  method: 'POST', headers,
  body: JSON.stringify({ email: 'you@yourcompany.com', first_name: 'Sam', source: 'api' }),
});

const res = await fetch(`${MEISA}/emails/trigger/`, {
  method: 'POST', headers,
  body: JSON.stringify({ trigger_key: 'welcome', to_email: 'you@yourcompany.com', variables: { first_name: 'Sam' } }),
});
console.log(res.status, await res.json());
```

Call the API from your server only. Never ship your key in browser or mobile code.

## Main endpoints

| What | Endpoint | Permission |
|---|---|---|
| Check your key | `GET /ping/` | any |
| Create or update a contact | `POST /contacts/upsert/` | `contacts:write` |
| Get unsubscribed addresses | `GET /contacts/api/unsubscribed-emails/` | `contacts:read` |
| Track an event | `POST /events/track/` | `events:track` |
| List sequences | `GET /sequences/api/list/` | `sequences:read` |
| Enroll in a sequence | `POST /sequences/enroll/` | `sequences:enroll` |
| Send a transactional email | `POST /emails/trigger/` | `emails:trigger` |
| Send a direct email | `POST /emails/send/` | `emails:trigger` |

The full reference is in the developer docs, linked from **API keys and webhooks**. That page also links a single spec file you can hand to your AI coding assistant.

## Errors and limits

| Code | Meaning | What to do |
|---|---|---|
| `400` | Missing or invalid field | Read the `error` message in the response. |
| `401` | Missing, wrong or revoked key | Check the `X-API-Key` header. |
| `403` | Key lacks the permission | Create a key with the right permission. |
| `404` | Contact or item not found | Upsert the contact first, or check the slug. |
| `422` | Can't send (unsubscribed, no sender) | Check the contact and your sender setup. |
| `429` | Rate limit hit | Wait and retry with backoff. |

Each key has its own per-minute and per-hour limit, shown in the `/ping/` response. Retry `429` and `5xx` errors with exponential backoff, and don't retry other `4xx` errors without fixing the request.

## Related articles

- [Create and manage API keys](/articles/create-an-api-key)
- [Send events and contacts from your app](/articles/send-events-from-your-app)
- [Send transactional emails with triggers](/articles/send-transactional-email)
- [Get webhooks for email events](/articles/set-up-webhooks)
