Python
import requests
url = 'https://api.minimo.it/public/v1/contacts'
headers = {'Authorization': 'Bearer {{BEARER_TOKEN}}', 'Content-Type': 'application/json'}
data = {
'email': '{{email}}',
'customFields': {
'{{customKey1}}': '{{customValue1}}',
'{{customKey2}}': '{{customValue2}}',
'{{customKey3}}': '{{customValue3}}'
},
'marketingConsent': {
'email': True,
'whatsapp': False
}
}
response = requests.post(url, json=data, headers=headers)
print(response.json())fetch('https://api.minimo.it/public/v1/contacts', {
method: 'POST',
headers: {
'Authorization': 'Bearer {{BEARER_TOKEN}}',
'Content-Type': 'application/json'
},
body: JSON.stringify({
'email': '{{email}}',
'customFields': {
'{{customKey1}}': '{{customValue1}}',
'{{customKey2}}': '{{customValue2}}',
'{{customKey3}}': '{{customValue3}}'
},
'marketingConsent': {
'email': true,
'whatsapp': false
}
})
}).then(response => response.json()).then(data => console.log(data));curl --request POST \
--url https://api.minimo.it/public/v1/contacts \
--header 'Authorization: Bearer {{BEARER_TOKEN}}' \
--header 'Content-Type: application/json' \
--data '{
"email": "{{email}}",
"phone": "{{phone}}",
"customFields": {
"{{customKey1}}": "{{customValue1}}",
"{{customKey2}}": "{{customValue2}}",
"{{customKey3}}": "{{customValue3}}"
},
"marketingConsent": {
"email": true,
"whatsapp": false
}
}'<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.minimo.it/public/v1/contacts',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS =>'{
"email": "{{email}}",
"customFields": {
"{{customKey1}}": "{{customValue1}}",
"{{customKey2}}": "{{customValue2}}",
"{{customKey3}}": "{{customValue3}}"
}
}',
CURLOPT_HTTPHEADER => array(
'Authorization: Bearer {{BEARER_TOKEN}}',
'Content-Type: application/json'
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;import java.net.HttpURLConnection;
import java.net.URL;
import java.io.OutputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
public class Main {
public static void main(String[] args) {
try {
URL url = new URL('https://api.minimo.it/public/v1/contacts');
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod('POST');
conn.setRequestProperty('Authorization', 'Bearer {{BEARER_TOKEN}}');
conn.setRequestProperty('Content-Type', 'application/json');
conn.setDoOutput(true);
String jsonInputString = '{"email": "{{email}}", "customFields": {"{{customKey1}}": "{{customValue1}}", "{{customKey2}}": "{{customValue2}}", "{{customKey3}}": "{{customValue3}}"}}';
try(OutputStream os = conn.getOutputStream()) {
byte[] input = jsonInputString.getBytes('utf-8');
os.write(input, 0, input.length);
}
try(BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), 'utf-8'))) {
StringBuilder response = new StringBuilder();
String responseLine = null;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
System.out.println(response.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Subscribe New Contact</title>
</head>
<body>
<h1>Subscribe New Contact</h1>
<form action="https://api.minimo.it/public/v1/contacts" method="POST">
<label for="email">Email:</label>
<input type="email" id="email" name="email" required><br><br>
<label for="phone">Phone:</label>
<input type="text" id="phone" name="phone"><br><br>
<label for="customField1">Custom Field 1:</label>
<input type="text" id="customField1" name="customFields.customField1"><br><br>
<label for="customField2">Custom Field 2:</label>
<input type="text" id="customField2" name="customFields.customField2"><br><br>
<label for="customField3">Custom Field 3:</label>
<input type="text" id="customField3" name="customFields.customField3"><br><br>
<button type="submit">Subscribe</button>
</form>
</body>
</html>package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.minimo.it/public/v1/contacts"
payload := strings.NewReader("{\n \"email\": \"email@minimo.it\",\n \"customFields\": {\n \"customKey1\": \"customValue1\",\n \"customKey2\": \"customValue2\",\n \"customKey3\": \"customValue3\"\n },\n \"marketingConsent\": {\n \"email\": true,\n \"whatsapp\": false\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}require 'uri'
require 'net/http'
url = URI("https://api.minimo.it/public/v1/contacts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"email@minimo.it\",\n \"customFields\": {\n \"customKey1\": \"customValue1\",\n \"customKey2\": \"customValue2\",\n \"customKey3\": \"customValue3\"\n },\n \"marketingConsent\": {\n \"email\": true,\n \"whatsapp\": false\n }\n}"
response = http.request(request)
puts response.read_body{
"data": [
{
"id": 123,
"email": "<string>",
"phone": "<string>",
"status": "<string>",
"source": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"custom_fields": {},
"company": 123,
"deleted": true,
"external_id": "<string>",
"external_connection_id": 123
}
]
}{
"error": "<string>"
}Contacts
Create or Update Contact
Insert a new contact or update an existing one based on email or phone
POST
/
public
/
v1
/
contacts
Python
import requests
url = 'https://api.minimo.it/public/v1/contacts'
headers = {'Authorization': 'Bearer {{BEARER_TOKEN}}', 'Content-Type': 'application/json'}
data = {
'email': '{{email}}',
'customFields': {
'{{customKey1}}': '{{customValue1}}',
'{{customKey2}}': '{{customValue2}}',
'{{customKey3}}': '{{customValue3}}'
},
'marketingConsent': {
'email': True,
'whatsapp': False
}
}
response = requests.post(url, json=data, headers=headers)
print(response.json())fetch('https://api.minimo.it/public/v1/contacts', {
method: 'POST',
headers: {
'Authorization': 'Bearer {{BEARER_TOKEN}}',
'Content-Type': 'application/json'
},
body: JSON.stringify({
'email': '{{email}}',
'customFields': {
'{{customKey1}}': '{{customValue1}}',
'{{customKey2}}': '{{customValue2}}',
'{{customKey3}}': '{{customValue3}}'
},
'marketingConsent': {
'email': true,
'whatsapp': false
}
})
}).then(response => response.json()).then(data => console.log(data));curl --request POST \
--url https://api.minimo.it/public/v1/contacts \
--header 'Authorization: Bearer {{BEARER_TOKEN}}' \
--header 'Content-Type: application/json' \
--data '{
"email": "{{email}}",
"phone": "{{phone}}",
"customFields": {
"{{customKey1}}": "{{customValue1}}",
"{{customKey2}}": "{{customValue2}}",
"{{customKey3}}": "{{customValue3}}"
},
"marketingConsent": {
"email": true,
"whatsapp": false
}
}'<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.minimo.it/public/v1/contacts',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS =>'{
"email": "{{email}}",
"customFields": {
"{{customKey1}}": "{{customValue1}}",
"{{customKey2}}": "{{customValue2}}",
"{{customKey3}}": "{{customValue3}}"
}
}',
CURLOPT_HTTPHEADER => array(
'Authorization: Bearer {{BEARER_TOKEN}}',
'Content-Type: application/json'
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;import java.net.HttpURLConnection;
import java.net.URL;
import java.io.OutputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
public class Main {
public static void main(String[] args) {
try {
URL url = new URL('https://api.minimo.it/public/v1/contacts');
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod('POST');
conn.setRequestProperty('Authorization', 'Bearer {{BEARER_TOKEN}}');
conn.setRequestProperty('Content-Type', 'application/json');
conn.setDoOutput(true);
String jsonInputString = '{"email": "{{email}}", "customFields": {"{{customKey1}}": "{{customValue1}}", "{{customKey2}}": "{{customValue2}}", "{{customKey3}}": "{{customValue3}}"}}';
try(OutputStream os = conn.getOutputStream()) {
byte[] input = jsonInputString.getBytes('utf-8');
os.write(input, 0, input.length);
}
try(BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), 'utf-8'))) {
StringBuilder response = new StringBuilder();
String responseLine = null;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
System.out.println(response.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Subscribe New Contact</title>
</head>
<body>
<h1>Subscribe New Contact</h1>
<form action="https://api.minimo.it/public/v1/contacts" method="POST">
<label for="email">Email:</label>
<input type="email" id="email" name="email" required><br><br>
<label for="phone">Phone:</label>
<input type="text" id="phone" name="phone"><br><br>
<label for="customField1">Custom Field 1:</label>
<input type="text" id="customField1" name="customFields.customField1"><br><br>
<label for="customField2">Custom Field 2:</label>
<input type="text" id="customField2" name="customFields.customField2"><br><br>
<label for="customField3">Custom Field 3:</label>
<input type="text" id="customField3" name="customFields.customField3"><br><br>
<button type="submit">Subscribe</button>
</form>
</body>
</html>package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.minimo.it/public/v1/contacts"
payload := strings.NewReader("{\n \"email\": \"email@minimo.it\",\n \"customFields\": {\n \"customKey1\": \"customValue1\",\n \"customKey2\": \"customValue2\",\n \"customKey3\": \"customValue3\"\n },\n \"marketingConsent\": {\n \"email\": true,\n \"whatsapp\": false\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}require 'uri'
require 'net/http'
url = URI("https://api.minimo.it/public/v1/contacts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"email@minimo.it\",\n \"customFields\": {\n \"customKey1\": \"customValue1\",\n \"customKey2\": \"customValue2\",\n \"customKey3\": \"customValue3\"\n },\n \"marketingConsent\": {\n \"email\": true,\n \"whatsapp\": false\n }\n}"
response = http.request(request)
puts response.read_body{
"data": [
{
"id": 123,
"email": "<string>",
"phone": "<string>",
"status": "<string>",
"source": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"custom_fields": {},
"company": 123,
"deleted": true,
"external_id": "<string>",
"external_connection_id": 123
}
]
}{
"error": "<string>"
}This endpoint performs an upsert: it first tries to match by email, then falls back to phone if no email match is
found. If no match is found, a new contact is created.
How Matching Works
The endpoint matches contacts in this order:1
Match by email
If
email is provided, searches for an existing contact with the same email in your company.2
Match by phone
If no email match is found and
phone is provided, searches by phone number.3
Create new
If no match is found, creates a new contact.
Need to update a contact’s email? Prefer Update by ID for a
deterministic update. Upsert only avoids creating a new record if a phone number is also provided and matches an
existing contact.
Custom Fields
Store additional data using thecustomFields object. Fields are flexible key-value pairs:
{
"email": "customer@example.com",
"customFields": {
"company": "Acme Corp",
"plan": "enterprise",
"signup_date": "2025-11-13"
}
}
Marketing Consent
Control channel-specific marketing consent when creating or updating a contact:{
"email": "customer@example.com",
"marketingConsent": {
"email": true,
"whatsapp": false
}
}
| Field | Type | Description |
|---|---|---|
email | boolean | Email marketing opt-in (true) or opt-out (false) |
whatsapp | boolean | WhatsApp marketing opt-in (true) or opt-out (false) |
Both fields are optional. Omitting a channel leaves its current consent status unchanged. Setting
true opts the
contact in, false opts them out.Phone Numbers
For WhatsApp messaging, include a phone number in E.164 format:{
"email": "customer@example.com",
"phone": "+393391234567"
}
- Include country code (e.g.,
+39for Italy,+1for US) - No spaces, dashes, or parentheses
- Example:
+12025551234(US),+393391234567(Italy)
Invalid phone numbers will cause WhatsApp message sending to fail.
Common Errors
| Error | Cause | Solution |
|---|---|---|
validation_error | Invalid email format | Check email address format |
invalid_phone_number | Phone not in E.164 format | Add country code, remove spaces |
rate_limit_exceeded | Too many requests | Implement rate limiting, use batching |
unauthorized | Invalid API key | Verify API key in Authorization header |
Related Endpoints
- Get Contact by Email: Look up a contact by email
- Update Contact by ID: Update a specific contact by ID
- Delete Contact: Soft-delete a contact
- Custom Fields: Manage custom field definitions
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Body
application/jsonmultipart/form-dataapplication/x-www-form-urlencoded
- object
- object[]
Email address of the contact
Phone number of the contact
Contact status
Contact source tag
Contact source type classification
Available options:
database, minimo, third_party Custom fields specific to the contact
Channel-specific marketing consent preferences
Show child attributes
Show child attributes
Response
Successful response
Show child attributes
Show child attributes
⌘I