Brevo
Access the Brevo API with managed OAuth authentication. Send transactional emails, manage contacts and lists, create email campaigns, and work with templates.
Quick Start
# Get account info
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/brevo/v3/account')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Base URL
https://gateway.maton.ai/brevo/v3/{resource}
The gateway proxies requests to api.brevo.com and automatically injects your OAuth token.
Authentication
All requests require the Maton API key in the Authorization header:
Authorization: Bearer $MATON_API_KEY
Environment Variable: Set your API key as MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"
Getting Your API Key
- Sign in or create an account at maton.ai
- Go to maton.ai/settings
- Copy your API key
Connection Management
Manage your Brevo OAuth connections at https://ctrl.maton.ai.
List Connections
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections?app=brevo&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Create Connection
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'brevo'}).encode()
req = urllib.request.Request('https://ctrl.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Get Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
json
{
"connection": {
"connection_id": "b04dd695-d056-433b-baf9-0fb4eb3bde9e",
"status": "ACTIVE",
"creation_time": "2026-02-09T19:51:00.932629Z",
"last_updated_time": "2026-02-09T19:51:30.123456Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "brevo",
"metadata": {}
}
}
Open the returned url in a browser to complete OAuth authorization.
Delete Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Specifying Connection
If you have multiple Brevo connections, specify which one to use with the Maton-Connection header:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/brevo/v3/account')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', 'b04dd695-d056-433b-baf9-0fb4eb3bde9e')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If omitted, the gateway uses the default (oldest) active connection.
API Reference
Account
Get Account Info
GET /brevo/v3/account
Response:
json
{
"email": "[email protected]",
"firstName": "John",
"lastName": "Doe",
"companyName": "Acme Inc",
"relay": {
"enabled": true,
"data": {
"userName": "[email protected]",
"relay": "smtp-relay.brevo.com",
"port": 587
}
}
}
Contacts
List Contacts
GET /brevo/v3/contacts
Query Parameters:
- limit - Number of results per page (default: 50, max: 500)
- offset - Index of first result (0-based)
- modifiedSince - Filter by modification date (ISO 8601)
Response:
json
{
"contacts": [
{
"id": 1,
"email": "[email protected]",
"emailBlacklisted": false,
"smsBlacklisted": false,
"createdAt": "2026-02-09T20:33:59.705+01:00",
"modifiedAt": "2026-02-09T20:35:19.529+01:00",
"listIds": [2],
"attributes": {
"FIRSTNAME": "John",
"LASTNAME": "Doe"
}
}
],
"count": 1
}
Get Contact
GET /brevo/v3/contacts/{identifier}
The identifier can be email address, phone number, or contact ID.
Query Parameters:
- identifierType - Type of identifier: email_id, phone_id, contact_id, ext_id
Create Contact
POST /brevo/v3/contacts
Content-Type: application/json
{
"email": "[email protected]",
"attributes": {
"FIRSTNAME": "Jane",
"LASTNAME": "Smith"
},
"listIds": [2],
"updateEnabled": false
}
Response:
json
{
"id": 2
}
Set updateEnabled: true to update the contact if it already exists.
Update Contact
PUT /brevo/v3/contacts/{identifier}
Content-Type: application/json
{
"attributes": {
"FIRSTNAME": "Updated",
"LASTNAME": "Name"
}
}
Returns 204 No Content on success.
Delete Contact
DELETE /brevo/v3/contacts/{identifier}
Returns 204 No Content on success.
Get Contact Campaign Stats
GET /brevo/v3/contacts/{identifier}/campaignStats
Lists
List All Lists
GET /brevo/v3/contacts/lists
Response:
json
{
"lists": [
{
"id": 2,
"name": "Newsletter Subscribers",
"folderId": 1,
"uniqueSubscribers": 150,
"totalBlacklisted": 2,
"totalSubscribers": 148
}
],
"count": 1
}
Get List
GET /brevo/v3/contacts/lists/{listId}
Create List
POST /brevo/v3/contacts/lists
Content-Type: application/json
{
"name": "New List",
"folderId": 1
}
Response:
json
{
"id": 3
}
Update List
PUT /brevo/v3/contacts/lists/{listId}
Content-Type: application/json
{
"name": "Updated List Name"
}
Returns 204 No Content on success.
Delete List
DELETE /brevo/v3/contacts/lists/{listId}
Returns 204 No Content on success.
Get Contacts in List
GET /brevo/v3/contacts/lists/{listId}/contacts
Add Contacts to List
POST /brevo/v3/contacts/lists/{listId}/contacts/add
Content-Type: application/json
{
"emails": ["[email protected]", "[email protected]"]
}
Remove Contacts from List
POST /brevo/v3/contacts/lists/{listId}/contacts/remove
Content-Type: application/json
{
"emails": ["[email protected]"]
}
Folders
List Folders
GET /brevo/v3/contacts/folders
Response:
json
{
"folders": [
{
"id": 1,
"name": "Marketing",
"uniqueSubscribers": 500,
"totalSubscribers": 480,
"totalBlacklisted": 20
}
],
"count": 1
}
Get Folder
GET /brevo/v3/contacts/folders/{folderId}
Create Folder
POST /brevo/v3/contacts/folders
Content-Type: application/json
{
"name": "New Folder"
}
Response:
json
{
"id": 4
}
Update Folder
PUT /brevo/v3/contacts/folders/{folderId}
Content-Type: application/json
{
"name": "Renamed Folder"
}
Returns 204 No Content on success.
Delete Folder
DELETE /brevo/v3/contacts/folders/{folderId}
Deletes folder and all lists within it. Returns 204 No Content on success.
Get Lists in Folder
GET /brevo/v3/contacts/folders/{folderId}/lists
Attributes
List Attributes
GET /brevo/v3/contacts/attributes
Response:
json
{
"attributes": [
{
"name": "FIRSTNAME",
"category": "normal",
"type": "text"
},
{
"name": "LASTNAME",
"category": "normal",
"type": "text"
}
]
}
Create Attribute
POST /brevo/v3/contacts/attributes/{category}/{attributeName}
Content-Type: application/json
{
"type": "text"
}
Categories: normal, transactional, category, calculated, global
Update Attribute
PUT /brevo/v3/contacts/attributes/{category}/{attributeName}
Content-Type: application/json
{
"value": "new value"
}
Delete Attribute
DELETE /brevo/v3/contacts/attributes/{category}/{attributeName}
Transactional Emails
Send Email
POST /brevo/v3/smtp/email
Content-Type: application/json
{
"sender": {
"name": "John Doe",
"email": "[email protected]"
},
"to": [
{
"email": "[email protected]",
"name": "Jane Smith"
}
],
"subject": "Welcome!",
"htmlContent": "<html><body><h1>Hello!</h1><p>Welcome to our service.</p></body></html>"
}
Response:
json
{
"messageId": "<[email protected]>"
}
Optional Parameters:
- cc - Carbon copy recipients
- bcc - Blind carbon copy recipients
- replyTo - Reply-to address
- textContent - Plain text version
- templateId - Use a template instead of htmlContent
- params - Template parameters
- attachment - File attachments
- headers - Custom headers
- tags - Email tags for tracking
- scheduledAt - Schedule for later (ISO 8601)
Get Transactional Emails
GET /brevo/v3/smtp/emails
Query Parameters:
- email - Filter by recipient email
- templateId - Filter by template
- messageId - Filter by message ID
- startDate - Start date (YYYY-MM-DD)
- endDate - End date (YYYY-MM-DD)
- limit - Results per page
- offset - Starting index
Delete Scheduled Email
DELETE /brevo/v3/smtp/email/{identifier}
The identifier can be a messageId or batchId.
Get Email Statistics
GET /brevo/v3/smtp/statistics/events
Query Parameters:
- limit - Results per page
- offset - Starting index
- startDate - Start date
- endDate - End date
- email - Filter by recipient
- event - Filter by event type: delivered, opened, clicked, bounced, etc.
Email Templates
List Templates
GET /brevo/v3/smtp/templates
Response:
json
{
"count": 1,
"templates": [
{
"id": 1,
"name": "Welcome Email",
"subject": "Welcome {{params.name}}!",
"isActive": true,
"sender": {
"name": "Company",
"email": "[email protected]"
},
"htmlContent": "<html>...</html>",
"createdAt": "2026-02-09 23:29:38",
"modifiedAt": "2026-02-09 23:29:38"
}
]
}
Get Template
GET /brevo/v3/smtp/templates/{templateId}
Create Template
POST /brevo/v3/smtp/templates
Content-Type: application/json
{
"sender": {
"name": "Company",
"email": "[email protected]"
},
"templateName": "Welcome Email",
"subject": "Welcome {{params.name}}!",
"htmlContent": "<html><body><h1>Hello {{params.name}}!</h1></body></html>"
}
Response:
json
{
"id": 1
}
Update Template
PUT /brevo/v3/smtp/templates/{templateId}
Content-Type: application/json
{
"templateName": "Updated Template Name",
"subject": "New Subject"
}
Returns 204 No Content on success.
Delete Template
DELETE /brevo/v3/smtp/templates/{templateId}
Returns 204 No Content on success.
Send Test Email
POST /brevo/v3/smtp/templates/{templateId}/sendTest
Content-Type: application/json
{
"emailTo": ["[email protected]"]
}
Email Campaigns
List Campaigns
GET /brevo/v3/emailCampaigns
Query Parameters:
- type - Filter by type: classic, trigger
- status - Filter by status: draft, sent, archive, queued, suspended, in_process
- limit - Results per page
- offset - Starting index
Response:
json
{
"count": 1,
"campaigns": [
{
"id": 2,
"name": "Monthly Newsletter",
"subject": "Our March Update",
"type": "classic",
"status": "draft",
"sender": {
"name": "Company",
"email": "[email protected]"
},
"createdAt": "2026-02-09T23:29:39.000Z"
}
]
}
Get Campaign
GET /brevo/v3/emailCampaigns/{campaignId}
Create Campaign
POST /brevo/v3/emailCampaigns
Content-Type: application/json
{
"name": "March Newsletter",
"subject": "Our March Update",
"sender": {
"name": "Company",
"email": "[email protected]"
},
"htmlContent": "<html><body><h1>March News</h1></body></html>",
"recipients": {
"listIds": [2]
}
}
Response:
json
{
"id": 2
}
Update Campaign
PUT /brevo/v3/emailCampaigns/{campaignId}
Content-Type: application/json
{
"name": "Updated Campaign Name",
"subject": "Updated Subject"
}
Returns 204 No Content on success.
Delete Campaign
DELETE /brevo/v3/emailCampaigns/{campaignId}
Returns 204 No Content on success.
Send Campaign Now
POST /brevo/v3/emailCampaigns/{campaignId}/sendNow
Send Test Email
POST /brevo/v3/emailCampaigns/{campaignId}/sendTest
Content-Type: application/json
{
"emailTo": ["[email protected]"]
}
Update Campaign Status
PUT /brevo/v3/emailCampaigns/{campaignId}/status
Content-Type: application/json
{
"status": "suspended"
}
Senders
List Senders
GET /brevo/v3/senders
Response:
json
{
"senders": [
{
"id": 1,
"name": "Company",
"email": "[email protected]",
"active": true,
"ips": []
}
]
}
Get Sender
GET /brevo/v3/senders/{senderId}
Create Sender
POST /brevo/v3/senders
Content-Type: application/json
{
"name": "Marketing",
"email": "[email protected]"
}
Update Sender
PUT /brevo/v3/senders/{senderId}
Content-Type: application/json
{
"name": "Updated Name"
}
Delete Sender
DELETE /brevo/v3/senders/{senderId}
Blocked Contacts
List Blocked Contacts
GET /brevo/v3/smtp/blockedContacts
Unblock Contact
DELETE /brevo/v3/smtp/blockedContacts/{email}
Blocked Domains
List Blocked Domains
GET /brevo/v3/smtp/blockedDomains
Add Blocked Domain
POST /brevo/v3/smtp/blockedDomains
Content-Type: application/json
{
"domain": "spam-domain.com"
}
Remove Blocked Domain
DELETE /brevo/v3/smtp/blockedDomains/{domain}
Pagination
Brevo uses offset-based pagination:
GET /brevo/v3/contacts?limit=50&offset=0
Parameters:
- limit - Number of results per page (varies by endpoint, typically max 500)
- offset - Starting index (0-based)
Response includes count:
json
{
"contacts": [...],
"count": 150
}
To get the next page, increment offset by limit:
- Page 1: offset=0&limit=50
- Page 2: offset=50&limit=50
- Page 3: offset=100&limit=50
Code Examples
JavaScript
const response = await fetch(
'https://gateway.maton.ai/brevo/v3/contacts',
{
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
}
}
);
const data = await response.json();
console.log(data.contacts);
Python
import os
import requests
response = requests.get(
'https://gateway.maton.ai/brevo/v3/contacts',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}
)
data = response.json()
print(data['contacts'])
Python (Send Email)
import os
import requests
response = requests.post(
'https://gateway.maton.ai/brevo/v3/smtp/email',
headers={
'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}',
'Content-Type': 'application/json'
},
json={
'sender': {'name': 'John', 'email': '[email protected]'},
'to': [{'email': '[email protected]', 'name': 'Jane'}],
'subject': 'Hello!',
'htmlContent': '<html><body><h1>Hi Jane!</h1></body></html>'
}
)
result = response.json()
print(f"Sent! Message ID: {result['messageId']}")
Python (Create Contact and Add to List)
import os
import requests
headers = {
'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}',
'Content-Type': 'application/json'
}
# Create contact
response = requests.post(
'https://gateway.maton.ai/brevo/v3/contacts',
headers=headers,
json={
'email': '[email protected]',
'attributes': {'FIRSTNAME': 'New', 'LASTNAME': 'User'},
'listIds': [2]
}
)
contact = response.json()
print(f"Created contact ID: {contact['id']}")
Notes
- All endpoints require the
/v3/prefix in the path - Attribute names must be in UPPERCASE
- Contact identifiers can be email, phone, or ID
- Sender email addresses must be verified in Brevo
- Template parameters use
{{params.name}}syntax - PUT and DELETE operations return 204 No Content on success
- Rate limits: 300 calls/minute on free plans, higher on paid plans
- IMPORTANT: When piping curl output to
jqor other commands, environment variables like$MATON_API_KEYmay not expand correctly in some shell environments
Error Handling
| Status | Meaning |
|---|---|
| 400 | Missing Brevo connection or bad request |
| 401 | Invalid or missing Maton API key |
| 404 | Resource not found |
| 429 | Rate limited |
| 4xx/5xx | Passthrough error from Brevo API |
Rate limit headers in response:
- x-sib-ratelimit-limit - Request limit
- x-sib-ratelimit-remaining - Remaining requests
- x-sib-ratelimit-reset - Reset time
Troubleshooting: Invalid API Key
When you receive an "Invalid API key" error, ALWAYS follow these steps before concluding there is an issue:
- Check that the
MATON_API_KEYenvironment variable is set:
echo $MATON_API_KEY
- Verify the API key is valid by listing connections:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF