Quick Start
The Meisa API allows you to sync contacts, manage tags, and enroll users in sequences programmatically.
For integrations, use an API key (write-only syncing). For the Meisa dashboard, use JWT (Bearer token).
Base URL
https://api.meisa.io/api/v1Authentication
Include your API key in the X-API-Key header with every request.
Need a higher-level overview? Read the Integration Contract.
# API keys are intended for write-only syncing from your backend.
curl -X POST "https://api.meisa.io/api/v1/contacts/upsert/" \
-H "X-API-Key: meisa_live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","external_id":"usr_123","custom_fields":{"plan":"free"}}'Endpoints
Create a new contact or update an existing one by email or external_id. This is the recommended endpoint for syncing user data from your application.
Request Body
{
"email": "[email protected]",
"external_id": "usr_12345", // Your internal user ID
"first_name": "John",
"last_name": "Doe",
"custom_fields": {
"plan": "pro",
"mrr": 99.00,
"signup_date": "2024-01-15"
},
"tag_names": ["paying-customer", "pro-plan"]
}Response
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"first_name": "John",
"last_name": "Doe",
"external_id": "usr_12345",
"custom_fields": {
"plan": "pro",
"mrr": 99.00,
"signup_date": "2024-01-15"
},
"tags": [
{"id": "...", "name": "paying-customer"},
{"id": "...", "name": "pro-plan"}
],
"status": "active",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}Example: Python
import requests
MEISA_API_KEY = "meisa_live_your_api_key"
MEISA_BASE_URL = "https://api.meisa.io/api/v1"
def sync_user_to_meisa(user):
"""Sync a user to Meisa when they sign up or update their account."""
response = requests.post(
f"{MEISA_BASE_URL}/contacts/upsert/",
headers={
"X-API-Key": MEISA_API_KEY,
"Content-Type": "application/json"
},
json={
"email": user.email,
"external_id": str(user.id),
"first_name": user.first_name,
"last_name": user.last_name,
"custom_fields": {
"plan": user.subscription.plan,
"mrr": user.subscription.mrr,
"signup_date": user.created_at.isoformat()
}
}
)
return response.json()Example: JavaScript/Node.js
const axios = require('axios');
const meisa = axios.create({
baseURL: 'https://api.meisa.io/api/v1',
headers: {
'X-API-Key': process.env.MEISA_API_KEY,
'Content-Type': 'application/json'
}
});
async function syncUserToMeisa(user) {
const response = await meisa.post('/contacts/upsert/', {
email: user.email,
external_id: user.id,
first_name: user.firstName,
last_name: user.lastName,
custom_fields: {
plan: user.subscription?.plan,
mrr: user.subscription?.mrr,
signup_date: user.createdAt
}
});
return response.data;
}How Automations Work
The Simple Integration Pattern
Your integration only needs to sync data. Meisa's automation engine handles everything else-adding/removing tags, enrolling in sequences, and triggering actions based on field changes.
1. Your app sends: POST /contacts/upsert/ with current user state
2. Meisa detects which fields changed (e.g., plan: free → pro)
3. Automations you've configured in Meisa run automatically
Configure automations in Meisa to react to field changes. For example:
| When | Then |
|---|---|
plan changes to "pro" | Add tag "pro-plan", Remove tag "free-plan" |
plan changes from "free" to any paid | Add tag "paying-customer", Enroll in "welcome-pro" sequence |
plan changes to "churned" | Add tag "churned", Enroll in "win-back" sequence |
When to Sync Contacts
Best Practice: Sync on Key Events
Sync your contact data whenever user state changes that you care about. Meisa automations will detect field changes and react accordingly.
- ✓User Registration - Sync new user with initial plan, source, signup date
- ✓Plan Changes - Upgrade, downgrade, or cancellation events
- ✓User Login - Captures any profile updates since last sync
- ✓Profile Updates - When user updates important fields (optional)
- ✓Milestones - First purchase, usage thresholds, feature adoption (optional)
Remember: You don't need to manage tags or sequences in your code. Just sync the current user state, and Meisa automations handle the rest based on field changes.
Integration Examples
1. User Signup
When a user signs up, sync their initial state to Meisa.
// After user signup - just send the data
meisa.post('/contacts/upsert/', {
email: user.email,
external_id: user.id,
first_name: user.firstName,
custom_fields: {
plan: 'free',
signup_source: 'website',
signup_date: new Date().toISOString()
}
})
// Meisa automation handles: Add "free-plan" tag, Enroll in welcome sequence2. Plan Upgrade
When a user upgrades, just update the plan field. Automations handle the rest.
// After successful payment - just update the plan
meisa.post('/contacts/upsert/', {
email: user.email,
custom_fields: {
plan: 'pro',
billing_cycle: 'annual',
mrr: 99,
upgraded_at: new Date().toISOString()
}
})
// Meisa detects: plan changed from "free" to "pro"
// Automation runs: Remove "free-plan", Add "pro-plan", Add "paying-customer"3. User Churns
When a user cancels, update their status. Automations trigger win-back campaigns.
// After cancellation - just update the state
meisa.post('/contacts/upsert/', {
email: user.email,
custom_fields: {
plan: 'churned',
previous_plan: 'pro',
churned_at: new Date().toISOString(),
churn_reason: reason
}
})
// Meisa detects: plan changed to "churned"
// Automation runs: Remove "paying-customer", Add "churned", Enroll in win-back sequence4. Complete Integration Example
A minimal integration class that syncs user events to Meisa.
class MeisaIntegration {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = 'https://api.meisa.io/api/v1';
}
async syncUser(user, extraFields = {}) {
return fetch(`${this.baseUrl}/contacts/upsert/`, {
method: 'POST',
headers: {
'X-API-Key': this.apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: user.email,
external_id: user.id,
first_name: user.firstName,
last_name: user.lastName,
custom_fields: {
plan: user.plan,
billing_cycle: user.billingCycle,
mrr: user.mrr,
...extraFields
}
})
});
}
}
// Usage
const meisa = new MeisaIntegration(process.env.MEISA_API_KEY);
// On signup
await meisa.syncUser(user, { signup_date: new Date().toISOString() });
// On plan change
await meisa.syncUser(user, { upgraded_at: new Date().toISOString() });
// On churn
await meisa.syncUser({ ...user, plan: 'churned' }, {
churned_at: new Date().toISOString(),
churn_reason: 'too_expensive'
});Rate Limits
| Limit | Value |
|---|---|
| Per Minute | 60 requests |
| Per Hour | 1,000 requests |
Rate limits can be customized per API key. Contact support for higher limits.
Error Handling
The API returns standard HTTP status codes. Errors include a message explaining what went wrong.
| Status | Description |
|---|---|
200 | Success |
201 | Created |
400 | Bad Request - Invalid parameters |
401 | Unauthorized - Invalid or missing API key |
403 | Forbidden - Insufficient permissions (check scopes) |
404 | Not Found - Resource doesn't exist |
429 | Too Many Requests - Rate limit exceeded |
Need Help?
If you have questions or need assistance with your integration, we're here to help.
Contact Support