API quickstart: your first API call
Create an API key, call /ping/ to check it, then add a contact and send a transactional email in three calls.
On this page
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
- Go to Settings, then API keys and webhooks.
- Click Create API key, name it, and pick Everything under Permissions for this test. Use narrower permissions in production.
- Click Create key and Copy key.
Full details: Create and manage API keys.
Store the key in an environment variable so it stays out of your code:
export MEISA_API_KEY="meisa_live_your_key"
Step 2: Check the key
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
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": "[email protected]", "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). Then:
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": "[email protected]", "variables": { "first_name": "Sam" } }'
A 200 with "status": "sent" means it’s on its way. Check your inbox.
The same calls in JavaScript
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: '[email protected]', first_name: 'Sam', source: 'api' }),
});
const res = await fetch(`${MEISA}/emails/trigger/`, {
method: 'POST', headers,
body: JSON.stringify({ trigger_key: 'welcome', to_email: '[email protected]', 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
Did this answer your question?
Thanks for letting us know.Thanks, we will improve this article. Want a hand right now?
Still need help?
Chat with us and get an answer in a few minutes.