Summary and recommendation
Mailjet's REST API (v3) exposes a contact management layer, not a platform user management layer. Admin sub-users (Manager, Designer roles) cannot be provisioned or deprovisioned via the API - that is UI-only. The API operates on marketing contacts: create, update, list, delete, and list-subscription operations against the /contact and /listrecipient resources.
Authentication is HTTP Basic Auth using an API Key and Secret Key pair; the Full Access key scope is required for write operations. SCIM 2.0 is not available on any documented plan, so identity graph synchronization must be built on top of the REST contact endpoints directly.
API quick reference
| Has user API | Yes |
| Auth method | HTTP Basic Auth (API Key as username, Secret Key as password) |
| Base URL | Official docs |
| SCIM available | No |
| SCIM plan required | Not available on any documented plan |
Authentication
Auth method: HTTP Basic Auth (API Key as username, Secret Key as password)
Setup steps
- Log in to Mailjet account and navigate to Account Settings > API Keys.
- Copy the API Key (public) and Secret Key (private).
- Encode credentials as Base64 string: base64(API_KEY:SECRET_KEY).
- Pass as Authorization header: 'Authorization: Basic
'. - For Send API v3.1, use the same credentials against https://api.mailjet.com/v3.1/send.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| Full Access | Read and write access to all API resources including contacts, lists, and account settings. | Creating, updating, and deleting contacts and contact lists |
| Read Only | Read-only access to all API resources. | Listing and retrieving contacts and contact data |
| Send | Restricted key allowing only email sending via Send API. | Transactional email sending only; not sufficient for user management |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| ID | integer | Unique Mailjet-assigned contact identifier. | auto-generated | immutable | Used as path parameter for single-contact operations. |
| string | Contact's email address. Must be unique per account. | required | not updatable via standard endpoint; use /contact/{id}/changeaddress | Primary identifier for contacts. | |
| Name | string | Display name of the contact. | optional | optional | |
| IsExcludedFromCampaigns | boolean | Whether the contact is excluded from marketing campaigns (unsubscribed globally). | optional, defaults to false | optional | Setting to true is equivalent to a global unsubscribe. |
| CreatedAt | datetime (ISO 8601) | Timestamp when the contact was created. | auto-generated | immutable | |
| LastActivityAt | datetime (ISO 8601) | Timestamp of the contact's last activity. | auto-generated | auto-updated | |
| DeliveredCount | integer | Number of messages delivered to this contact. | auto-generated | read-only | |
| UnsubscribedAt | datetime (ISO 8601) | Timestamp when the contact unsubscribed. | null | read-only | |
| UnsubscribedBy | string | Source that triggered the unsubscribe. | null | read-only | |
| ContactListID | integer | ID of the contact list the contact belongs to (used in list-contact operations). | required for list subscription | optional | Managed via /listrecipient endpoint. |
| IsUnsubscribed | boolean | Whether the contact is unsubscribed from a specific list. | optional | optional | List-level unsubscribe, distinct from IsExcludedFromCampaigns. |
| Properties (custom) | object (key-value) | Custom contact properties defined via /contactmetadata. Values set via /contactdata. | optional | optional | Property names must be pre-defined via the contactmetadata resource before use. |
Core endpoints
List contacts
- Method: GET
- URL:
https://api.mailjet.com/v3/REST/contact - Watch out for: Default page size is 10; set Limit up to 1000 for bulk retrieval. Total reflects full count across pages.
Request example
GET /v3/REST/contact?Limit=10&Offset=0
Authorization: Basic <base64>
Response example
{
"Count": 2,
"Data": [
{"ID": 1, "Email": "a@example.com", "Name": "Alice", "IsExcludedFromCampaigns": false}
],
"Total": 2
}
Get single contact
- Method: GET
- URL:
https://api.mailjet.com/v3/REST/contact/{contact_ID|email} - Watch out for: Can look up by numeric ID or email address. Email must be URL-encoded if it contains special characters.
Request example
GET /v3/REST/contact/abc@example.com
Authorization: Basic <base64>
Response example
{
"Count": 1,
"Data": [
{"ID": 42, "Email": "abc@example.com", "Name": "Bob", "IsExcludedFromCampaigns": false}
],
"Total": 1
}
Create contact
- Method: POST
- URL:
https://api.mailjet.com/v3/REST/contact - Watch out for: Creating a contact does not subscribe them to any list. A separate POST to /listrecipient is required to add them to a contact list.
Request example
POST /v3/REST/contact
Content-Type: application/json
{"Email": "new@example.com", "Name": "New User", "IsExcludedFromCampaigns": false}
Response example
{
"Count": 1,
"Data": [
{"ID": 99, "Email": "new@example.com", "Name": "New User", "IsExcludedFromCampaigns": false}
],
"Total": 1
}
Update contact
- Method: PUT
- URL:
https://api.mailjet.com/v3/REST/contact/{contact_ID} - Watch out for: Mailjet uses PUT (full replacement of editable fields), not PATCH. Email address cannot be changed via this endpoint.
Request example
PUT /v3/REST/contact/99
Content-Type: application/json
{"Name": "Updated Name", "IsExcludedFromCampaigns": true}
Response example
{
"Count": 1,
"Data": [
{"ID": 99, "Email": "new@example.com", "Name": "Updated Name", "IsExcludedFromCampaigns": true}
],
"Total": 1
}
Delete contact
- Method: DELETE
- URL:
https://api.mailjet.com/v3/REST/contact/{contact_ID} - Watch out for: Deletion is subject to GDPR data deletion rules. Mailjet may retain anonymized statistical data. Deleted contacts cannot be recovered.
Request example
DELETE /v3/REST/contact/99
Authorization: Basic <base64>
Response example
HTTP 204 No Content
Bulk upsert contacts (async)
- Method: POST
- URL:
https://api.mailjet.com/v3/REST/contact/managemanycontacts - Watch out for: Returns a JobID immediately; operation is asynchronous. Poll GET /contact/managemanycontacts/{JobID} to check completion status.
Request example
POST /v3/REST/contact/managemanycontacts
Content-Type: application/json
{"Contacts": [{"Email": "a@x.com", "Name": "A"}, {"Email": "b@x.com"}], "ContactsLists": [{"ListID": 5, "Action": "addnoforce"}]}
Response example
{
"Count": 1,
"Data": [{"JobID": 12345}],
"Total": 1
}
Add/remove contact from list
- Method: POST
- URL:
https://api.mailjet.com/v3/REST/listrecipient - Watch out for: To remove a contact from a list, use DELETE /listrecipient/{ID} or set IsUnsubscribed=true to unsubscribe without removing.
Request example
POST /v3/REST/listrecipient
Content-Type: application/json
{"ContactID": 99, "ListID": 5, "IsUnsubscribed": false}
Response example
{
"Count": 1,
"Data": [{"ID": 201, "ContactID": 99, "ListID": 5, "IsUnsubscribed": false}],
"Total": 1
}
Get/set contact custom properties
- Method: PUT
- URL:
https://api.mailjet.com/v3/REST/contactdata/{contact_ID} - Watch out for: Custom property names must be pre-created via POST /contactmetadata before values can be set. Property names are case-sensitive.
Request example
PUT /v3/REST/contactdata/99
Content-Type: application/json
{"Data": [{"Name": "department", "Value": "Engineering"}]}
Response example
{
"Count": 1,
"Data": [{"ContactID": 99, "Data": [{"Name": "department", "Value": "Engineering"}]}],
"Total": 1
}
Rate limits, pagination, and events
- Rate limits: Mailjet enforces rate limits per API key. Limits vary by plan and endpoint type. The REST API (contact management) has separate limits from the Send API.
- Rate-limit headers: Yes
- Retry-After header: No
- Rate-limit notes: Exact rate limit header names are not publicly documented in detail. Bulk contact import via /contact/managemanycontacts is asynchronous and subject to separate job-based limits. Exceeding limits returns HTTP 429.
- Pagination method: offset
- Default page size: 10
- Max page size: 1000
- Pagination pointer: Limit and Offset query parameters (e.g., ?Limit=100&Offset=0)
| Plan | Limit | Concurrent |
|---|---|---|
| Free | 200 requests/minute on REST API | 0 |
| Essential | 300 requests/minute on REST API | 0 |
| Premium | 300 requests/minute on REST API | 0 |
| Custom/Enterprise | Negotiated higher limits | 0 |
- Webhooks available: Yes
- Webhook notes: Mailjet provides Event API (webhooks) for email delivery events. These are not user-management webhooks but can be used to track contact-level events such as unsubscribes, bounces, and spam complaints to keep external user records in sync.
- Alternative event strategy: Poll GET /contact or GET /eventcallbackurl for contact state changes. Use /contact/managemanycontacts job polling for bulk operation status.
- Webhook events: sent, open, click, bounce, spam, unsub, blocked, error
SCIM API status
- SCIM available: No
- SCIM version: Not documented
- Plan required: Not available on any documented plan
- Endpoint: Not documented
Limitations:
- Mailjet does not document a SCIM 2.0 endpoint in any official developer or help center documentation.
- SAML SSO is available on Premium 100k+ and Custom plans but SCIM provisioning is not offered.
- User provisioning must be handled via the REST Contact API or manual processes.
Common scenarios
Three core provisioning patterns are supported by the API. For single-contact provisioning: POST /v3/REST/contact to create the record, then POST /v3/REST/listrecipient to subscribe - contact creation alone does not add the contact to any list.
For bulk operations: POST /v3/REST/contact/managemanycontacts with a Contacts array and ContactsLists array.
this returns a JobID immediately and must be polled via GET /v3/REST/contact/managemanycontacts/{JobID} until Status is 'Completed' or 'Error' - the operation is fully asynchronous and large imports can take several minutes.
For deprovisioning: use PUT /v3/REST/contact/{ID} with IsExcludedFromCampaigns=true for global suppression, DELETE /v3/REST/listrecipient/{ID} for list-scoped removal, or DELETE /v3/REST/contact/{ID} for GDPR erasure - note that Mailjet may retain anonymized aggregate statistics after deletion.
Provision a new contact and subscribe to a list
- POST /v3/REST/contact with Email and Name to create the contact record.
- Note the returned contact ID from the response Data[0].ID.
- POST /v3/REST/listrecipient with ContactID and ListID to subscribe the contact to the target list.
- Optionally PUT /v3/REST/contactdata/{ID} to set custom property values.
Watch out for: Creating a contact does not automatically subscribe them to any list. Both steps are required. If the contact already exists, POST /contact returns the existing record without error (idempotent by email).
Bulk import and subscribe contacts
- POST /v3/REST/contact/managemanycontacts with a Contacts array and ContactsLists array specifying ListID and Action ('addnoforce' or 'addforce').
- Capture the JobID from the response.
- Poll GET /v3/REST/contact/managemanycontacts/{JobID} until Status is 'Completed' or 'Error'.
- On error, inspect the Error field in the job response for per-contact failure details.
Watch out for: The job is asynchronous; do not assume completion immediately after POST. Large imports may take several minutes. 'addnoforce' skips already-subscribed contacts; 'addforce' re-subscribes them.
Unsubscribe / deprovision a contact
- To globally suppress: PUT /v3/REST/contact/{ID} with IsExcludedFromCampaigns=true.
- To remove from a specific list: GET /v3/REST/listrecipient?ContactID={ID}&ListID={ListID} to find the listrecipient ID, then DELETE /v3/REST/listrecipient/{listrecipient_ID}.
- For GDPR erasure: DELETE /v3/REST/contact/{ID} to remove the contact record.
Watch out for: IsExcludedFromCampaigns=true suppresses all campaigns but does not remove the contact from lists. Full deletion via DELETE removes the contact but Mailjet may retain anonymized aggregate statistics.
Why building this yourself is a trap
Several non-obvious behaviors create integration risk. The API uses PUT semantics, not PATCH - omitting optional fields on update may reset them to defaults. Email addresses are immutable after contact creation via the standard PUT endpoint; a separate undocumented endpoint exists but is not reliably supported.
Custom contact properties must be pre-defined via POST /contactmetadata before any values can be written via /contactdata - skipping this step returns a 400 error with no clear remediation hint. Rate limit responses return HTTP 429 with no documented Retry-After header, so callers must implement exponential backoff independently.
Finally, the identity graph implication: because Mailjet contacts are marketing records rather than identity objects, any external identity graph integration must treat the Mailjet contact ID as a secondary identifier and reconcile on email address, which is unique per account but cannot be updated post-creation through the standard API surface.
Automate Mailjet workflows without one-off scripts
Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.