Install
openclaw skills install brevo-apiBrevo API integration with managed OAuth. Email marketing, transactional emails, SMS, contacts, and CRM. Use this skill when users want to send emails, manage contacts, create campaigns, or work with Brevo lists and templates. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway). Requires network access and valid Maton API key.
openclaw skills install brevo-apiAccess the Brevo API with managed OAuth authentication. Send transactional emails, manage contacts and lists, create email campaigns, and work with templates.
# Get account info
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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
https://api.maton.ai/brevo/v3/{resource}
Maton proxies requests to api.brevo.com and automatically injects your OAuth token.
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"
Manage your Brevo OAuth connections at https://api.maton.ai.
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'brevo'}).encode()
req = urllib.request.Request('https://api.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
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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:
{
"connection": {
"connection_id": "{connection_id}",
"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.
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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
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://api.maton.ai/brevo/v3/account')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '{connection_id}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If you have multiple connections, always include this header to ensure requests go to the intended account.
GET /brevo/v3/account
Response:
{
"email": "user@example.com",
"firstName": "John",
"lastName": "Doe",
"companyName": "Acme Inc",
"relay": {
"enabled": true,
"data": {
"userName": "user@smtp-brevo.com",
"relay": "smtp-relay.brevo.com",
"port": 587
}
}
}
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:
{
"contacts": [
{
"id": 1,
"email": "contact@example.com",
"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 /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_idPOST /brevo/v3/contacts
Content-Type: application/json
{
"email": "newcontact@example.com",
"attributes": {
"FIRSTNAME": "Jane",
"LASTNAME": "Smith"
},
"listIds": [2],
"updateEnabled": false
}
Response:
{
"id": 2
}
Set updateEnabled: true to update the contact if it already exists.
PUT /brevo/v3/contacts/{identifier}
Content-Type: application/json
{
"attributes": {
"FIRSTNAME": "Updated",
"LASTNAME": "Name"
}
}
Returns 204 No Content on success.
DELETE /brevo/v3/contacts/{identifier}
Returns 204 No Content on success.
GET /brevo/v3/contacts/{identifier}/campaignStats
GET /brevo/v3/contacts/lists
Response:
{
"lists": [
{
"id": 2,
"name": "Newsletter Subscribers",
"folderId": 1,
"uniqueSubscribers": 150,
"totalBlacklisted": 2,
"totalSubscribers": 148
}
],
"count": 1
}
GET /brevo/v3/contacts/lists/{listId}
POST /brevo/v3/contacts/lists
Content-Type: application/json
{
"name": "New List",
"folderId": 1
}
Response:
{
"id": 3
}
PUT /brevo/v3/contacts/lists/{listId}
Content-Type: application/json
{
"name": "Updated List Name"
}
Returns 204 No Content on success.
DELETE /brevo/v3/contacts/lists/{listId}
Returns 204 No Content on success.
GET /brevo/v3/contacts/lists/{listId}/contacts
POST /brevo/v3/contacts/lists/{listId}/contacts/add
Content-Type: application/json
{
"emails": ["contact1@example.com", "contact2@example.com"]
}
POST /brevo/v3/contacts/lists/{listId}/contacts/remove
Content-Type: application/json
{
"emails": ["contact1@example.com"]
}
GET /brevo/v3/contacts/folders
Response:
{
"folders": [
{
"id": 1,
"name": "Marketing",
"uniqueSubscribers": 500,
"totalSubscribers": 480,
"totalBlacklisted": 20
}
],
"count": 1
}
GET /brevo/v3/contacts/folders/{folderId}
POST /brevo/v3/contacts/folders
Content-Type: application/json
{
"name": "New Folder"
}
Response:
{
"id": 4
}
PUT /brevo/v3/contacts/folders/{folderId}
Content-Type: application/json
{
"name": "Renamed Folder"
}
Returns 204 No Content on success.
DELETE /brevo/v3/contacts/folders/{folderId}
Deletes folder and all lists within it. Returns 204 No Content on success.
GET /brevo/v3/contacts/folders/{folderId}/lists
GET /brevo/v3/contacts/attributes
Response:
{
"attributes": [
{
"name": "FIRSTNAME",
"category": "normal",
"type": "text"
},
{
"name": "LASTNAME",
"category": "normal",
"type": "text"
}
]
}
POST /brevo/v3/contacts/attributes/{category}/{attributeName}
Content-Type: application/json
{
"type": "text"
}
Categories: normal, transactional, category, calculated, global
PUT /brevo/v3/contacts/attributes/{category}/{attributeName}
Content-Type: application/json
{
"value": "new value"
}
DELETE /brevo/v3/contacts/attributes/{category}/{attributeName}
POST /brevo/v3/smtp/email
Content-Type: application/json
{
"sender": {
"name": "John Doe",
"email": "john@example.com"
},
"to": [
{
"email": "recipient@example.com",
"name": "Jane Smith"
}
],
"subject": "Welcome!",
"htmlContent": "<html><body><h1>Hello!</h1><p>Welcome to our service.</p></body></html>"
}
Response:
{
"messageId": "<202602092329.12910305853@smtp-relay.mailin.fr>"
}
Optional Parameters:
cc - Carbon copy recipientsbcc - Blind carbon copy recipientsreplyTo - Reply-to addresstextContent - Plain text versiontemplateId - Use a template instead of htmlContentparams - Template parametersattachment - File attachmentsheaders - Custom headerstags - Email tags for trackingscheduledAt - Schedule for later (ISO 8601)GET /brevo/v3/smtp/emails
Query Parameters:
email - Filter by recipient emailtemplateId - Filter by templatemessageId - Filter by message IDstartDate - Start date (YYYY-MM-DD)endDate - End date (YYYY-MM-DD)limit - Results per pageoffset - Starting indexDELETE /brevo/v3/smtp/email/{identifier}
The identifier can be a messageId or batchId.
GET /brevo/v3/smtp/statistics/events
Query Parameters:
limit - Results per pageoffset - Starting indexstartDate - Start dateendDate - End dateemail - Filter by recipientevent - Filter by event type: delivered, opened, clicked, bounced, etc.GET /brevo/v3/smtp/templates
Response:
{
"count": 1,
"templates": [
{
"id": 1,
"name": "Welcome Email",
"subject": "Welcome {{params.name}}!",
"isActive": true,
"sender": {
"name": "Company",
"email": "noreply@company.com"
},
"htmlContent": "<html>...</html>",
"createdAt": "2026-02-09 23:29:38",
"modifiedAt": "2026-02-09 23:29:38"
}
]
}
GET /brevo/v3/smtp/templates/{templateId}
POST /brevo/v3/smtp/templates
Content-Type: application/json
{
"sender": {
"name": "Company",
"email": "noreply@company.com"
},
"templateName": "Welcome Email",
"subject": "Welcome {{params.name}}!",
"htmlContent": "<html><body><h1>Hello {{params.name}}!</h1></body></html>"
}
Response:
{
"id": 1
}
PUT /brevo/v3/smtp/templates/{templateId}
Content-Type: application/json
{
"templateName": "Updated Template Name",
"subject": "New Subject"
}
Returns 204 No Content on success.
DELETE /brevo/v3/smtp/templates/{templateId}
Returns 204 No Content on success.
POST /brevo/v3/smtp/templates/{templateId}/sendTest
Content-Type: application/json
{
"emailTo": ["test@example.com"]
}
GET /brevo/v3/emailCampaigns
Query Parameters:
type - Filter by type: classic, triggerstatus - Filter by status: draft, sent, archive, queued, suspended, in_processlimit - Results per pageoffset - Starting indexResponse:
{
"count": 1,
"campaigns": [
{
"id": 2,
"name": "Monthly Newsletter",
"subject": "Our March Update",
"type": "classic",
"status": "draft",
"sender": {
"name": "Company",
"email": "news@company.com"
},
"createdAt": "2026-02-09T23:29:39.000Z"
}
]
}
GET /brevo/v3/emailCampaigns/{campaignId}
POST /brevo/v3/emailCampaigns
Content-Type: application/json
{
"name": "March Newsletter",
"subject": "Our March Update",
"sender": {
"name": "Company",
"email": "news@company.com"
},
"htmlContent": "<html><body><h1>March News</h1></body></html>",
"recipients": {
"listIds": [2]
}
}
Response:
{
"id": 2
}
PUT /brevo/v3/emailCampaigns/{campaignId}
Content-Type: application/json
{
"name": "Updated Campaign Name",
"subject": "Updated Subject"
}
Returns 204 No Content on success.
DELETE /brevo/v3/emailCampaigns/{campaignId}
Returns 204 No Content on success.
POST /brevo/v3/emailCampaigns/{campaignId}/sendNow
POST /brevo/v3/emailCampaigns/{campaignId}/sendTest
Content-Type: application/json
{
"emailTo": ["test@example.com"]
}
PUT /brevo/v3/emailCampaigns/{campaignId}/status
Content-Type: application/json
{
"status": "suspended"
}
GET /brevo/v3/senders
Response:
{
"senders": [
{
"id": 1,
"name": "Company",
"email": "noreply@company.com",
"active": true,
"ips": []
}
]
}
GET /brevo/v3/senders/{senderId}
POST /brevo/v3/senders
Content-Type: application/json
{
"name": "Marketing",
"email": "marketing@company.com"
}
PUT /brevo/v3/senders/{senderId}
Content-Type: application/json
{
"name": "Updated Name"
}
DELETE /brevo/v3/senders/{senderId}
GET /brevo/v3/smtp/blockedContacts
DELETE /brevo/v3/smtp/blockedContacts/{email}
GET /brevo/v3/smtp/blockedDomains
POST /brevo/v3/smtp/blockedDomains
Content-Type: application/json
{
"domain": "spam-domain.com"
}
DELETE /brevo/v3/smtp/blockedDomains/{domain}
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:
{
"contacts": [...],
"count": 150
}
To get the next page, increment offset by limit:
offset=0&limit=50offset=50&limit=50offset=100&limit=50const response = await fetch(
'https://api.maton.ai/brevo/v3/contacts',
{
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
}
}
);
const data = await response.json();
console.log(data.contacts);
import os
import requests
response = requests.get(
'https://api.maton.ai/brevo/v3/contacts',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}
)
data = response.json()
print(data['contacts'])
import os
import requests
response = requests.post(
'https://api.maton.ai/brevo/v3/smtp/email',
headers={
'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}',
'Content-Type': 'application/json'
},
json={
'sender': {'name': 'John', 'email': 'john@example.com'},
'to': [{'email': 'recipient@example.com', 'name': 'Jane'}],
'subject': 'Hello!',
'htmlContent': '<html><body><h1>Hi Jane!</h1></body></html>'
}
)
result = response.json()
print(f"Sent! Message ID: {result['messageId']}")
import os
import requests
headers = {
'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}',
'Content-Type': 'application/json'
}
# Create contact
response = requests.post(
'https://api.maton.ai/brevo/v3/contacts',
headers=headers,
json={
'email': 'newuser@example.com',
'attributes': {'FIRSTNAME': 'New', 'LASTNAME': 'User'},
'listIds': [2]
}
)
contact = response.json()
print(f"Created contact ID: {contact['id']}")
/v3/ prefix in the path{{params.name}} syntaxjq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments| 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 limitx-sib-ratelimit-remaining - Remaining requestsx-sib-ratelimit-reset - Reset timeWhen you receive an "Invalid API key" error, ALWAYS follow these steps before concluding there is an issue:
MATON_API_KEY environment variable is set:echo $MATON_API_KEY
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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