curl --request POST \
--url https://api.nuvia.ai/v1/contacts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "João Silva",
"country_code": "55",
"phone_number": "11999998888",
"email": "joao.silva@exemplo.com.br",
"job_title": "Gerente de Vendas",
"city": "São Paulo",
"additional_attributes": {
"cpf": "12345678900"
}
}
'import requests
url = "https://api.nuvia.ai/v1/contacts"
payload = {
"name": "João Silva",
"country_code": "55",
"phone_number": "11999998888",
"email": "joao.silva@exemplo.com.br",
"job_title": "Gerente de Vendas",
"city": "São Paulo",
"additional_attributes": { "cpf": "12345678900" }
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'João Silva',
country_code: '55',
phone_number: '11999998888',
email: 'joao.silva@exemplo.com.br',
job_title: 'Gerente de Vendas',
city: 'São Paulo',
additional_attributes: {cpf: '12345678900'}
})
};
fetch('https://api.nuvia.ai/v1/contacts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.nuvia.ai/v1/contacts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'João Silva',
'country_code' => '55',
'phone_number' => '11999998888',
'email' => 'joao.silva@exemplo.com.br',
'job_title' => 'Gerente de Vendas',
'city' => 'São Paulo',
'additional_attributes' => [
'cpf' => '12345678900'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.nuvia.ai/v1/contacts"
payload := strings.NewReader("{\n \"name\": \"João Silva\",\n \"country_code\": \"55\",\n \"phone_number\": \"11999998888\",\n \"email\": \"joao.silva@exemplo.com.br\",\n \"job_title\": \"Gerente de Vendas\",\n \"city\": \"São Paulo\",\n \"additional_attributes\": {\n \"cpf\": \"12345678900\"\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))
}HttpResponse<String> response = Unirest.post("https://api.nuvia.ai/v1/contacts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"João Silva\",\n \"country_code\": \"55\",\n \"phone_number\": \"11999998888\",\n \"email\": \"joao.silva@exemplo.com.br\",\n \"job_title\": \"Gerente de Vendas\",\n \"city\": \"São Paulo\",\n \"additional_attributes\": {\n \"cpf\": \"12345678900\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.nuvia.ai/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 \"name\": \"João Silva\",\n \"country_code\": \"55\",\n \"phone_number\": \"11999998888\",\n \"email\": \"joao.silva@exemplo.com.br\",\n \"job_title\": \"Gerente de Vendas\",\n \"city\": \"São Paulo\",\n \"additional_attributes\": {\n \"cpf\": \"12345678900\"\n }\n}"
response = http.request(request)
puts response.read_body{
"name": "<string>",
"company": "<unknown>",
"_id": "<string>",
"id": "<string>",
"country_code": "<string>",
"email": "<string>",
"last_activity_at": "<unknown>",
"phone_number": "<string>",
"linkedin_identifier": "<string>",
"job_title": "<string>",
"seniority": "<string>",
"department": "<string>",
"country": "<string>",
"region": "<string>",
"city": "<string>",
"experience": "<string>",
"skills": "<string>",
"additional_attributes": {},
"business": "<string>",
"notes": [
{
"_id": "<string>",
"content": "<string>",
"user": "<string>",
"createdAt": "<unknown>",
"updatedAt": "<unknown>"
}
],
"marketing_opt_out": {},
"whatsapp_user_id": "<string>",
"whatsapp_username": "<string>",
"whatsapp_parent_user_id": "<string>",
"createdAt": "<unknown>",
"updatedAt": "<unknown>"
}Create or update contact
Creates a contact or, if the company already has a contact with the same phone, updates that contact (upsert).
- Identification: the contact is matched by phone only (
country_code+phone_number). Email and LinkedIn are not used to find a contact. - Response: 201 both when creating and when finding an existing contact. The response doesn’t say which of the two happened.
- Existing contact:
nameis preserved.email,linkedin_identifierandadditional_attributesare updated; inadditional_attributesthe merge is by slug, and slugs not sent stay as they are. - Profile fields:
job_title,seniority,department,country,region,cityandexperienceare saved on creation and, on an existing contact, updated when sent. - Phone: digits only, area code + number, without the country code (the country code goes in
country_code). An invalid phone returns 400. - additional_attributes:
{ "slug": value }, with a primitive value (string, number, boolean or null). A slug that doesn’t exist, or that belongs to a custom field whose context isn’t CONTACT, is silently ignored.
curl --request POST \
--url https://api.nuvia.ai/v1/contacts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "João Silva",
"country_code": "55",
"phone_number": "11999998888",
"email": "joao.silva@exemplo.com.br",
"job_title": "Gerente de Vendas",
"city": "São Paulo",
"additional_attributes": {
"cpf": "12345678900"
}
}
'import requests
url = "https://api.nuvia.ai/v1/contacts"
payload = {
"name": "João Silva",
"country_code": "55",
"phone_number": "11999998888",
"email": "joao.silva@exemplo.com.br",
"job_title": "Gerente de Vendas",
"city": "São Paulo",
"additional_attributes": { "cpf": "12345678900" }
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'João Silva',
country_code: '55',
phone_number: '11999998888',
email: 'joao.silva@exemplo.com.br',
job_title: 'Gerente de Vendas',
city: 'São Paulo',
additional_attributes: {cpf: '12345678900'}
})
};
fetch('https://api.nuvia.ai/v1/contacts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.nuvia.ai/v1/contacts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'João Silva',
'country_code' => '55',
'phone_number' => '11999998888',
'email' => 'joao.silva@exemplo.com.br',
'job_title' => 'Gerente de Vendas',
'city' => 'São Paulo',
'additional_attributes' => [
'cpf' => '12345678900'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.nuvia.ai/v1/contacts"
payload := strings.NewReader("{\n \"name\": \"João Silva\",\n \"country_code\": \"55\",\n \"phone_number\": \"11999998888\",\n \"email\": \"joao.silva@exemplo.com.br\",\n \"job_title\": \"Gerente de Vendas\",\n \"city\": \"São Paulo\",\n \"additional_attributes\": {\n \"cpf\": \"12345678900\"\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))
}HttpResponse<String> response = Unirest.post("https://api.nuvia.ai/v1/contacts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"João Silva\",\n \"country_code\": \"55\",\n \"phone_number\": \"11999998888\",\n \"email\": \"joao.silva@exemplo.com.br\",\n \"job_title\": \"Gerente de Vendas\",\n \"city\": \"São Paulo\",\n \"additional_attributes\": {\n \"cpf\": \"12345678900\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.nuvia.ai/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 \"name\": \"João Silva\",\n \"country_code\": \"55\",\n \"phone_number\": \"11999998888\",\n \"email\": \"joao.silva@exemplo.com.br\",\n \"job_title\": \"Gerente de Vendas\",\n \"city\": \"São Paulo\",\n \"additional_attributes\": {\n \"cpf\": \"12345678900\"\n }\n}"
response = http.request(request)
puts response.read_body{
"name": "<string>",
"company": "<unknown>",
"_id": "<string>",
"id": "<string>",
"country_code": "<string>",
"email": "<string>",
"last_activity_at": "<unknown>",
"phone_number": "<string>",
"linkedin_identifier": "<string>",
"job_title": "<string>",
"seniority": "<string>",
"department": "<string>",
"country": "<string>",
"region": "<string>",
"city": "<string>",
"experience": "<string>",
"skills": "<string>",
"additional_attributes": {},
"business": "<string>",
"notes": [
{
"_id": "<string>",
"content": "<string>",
"user": "<string>",
"createdAt": "<unknown>",
"updatedAt": "<unknown>"
}
],
"marketing_opt_out": {},
"whatsapp_user_id": "<string>",
"whatsapp_username": "<string>",
"whatsapp_parent_user_id": "<string>",
"createdAt": "<unknown>",
"updatedAt": "<unknown>"
}Authorizations
JWT authentication token
Headers
Target company identifier. Required only for global API keys (type=global). Ignored for company API keys and human users.
Body
The contact's full name. Required. If a contact with the same phone already exists, its name is preserved.
1Country calling code (1-3 digits). Examples: 55 (Brazil), 1 (USA), 351 (Portugal). Together with phone_number, it identifies the contact: if the company already has a contact with this phone, that contact is updated instead of a new one being created.
1 - 3^\d{1,3}$Phone number without formatting, digits only (area code + number, without the country code). Example: 11999998888. A phone that doesn't form a valid number with the country_code is rejected with 400.
1^\d+$The contact's email address. Optional. Not used to find the contact; on an existing contact, it replaces the current email.
The contact's LinkedIn identifier (profile name). Example: joao-silva-123. Not used to find the contact; on an existing contact, it replaces the current value.
The contact's job title. Example: Sales Manager. Saved on creation; on an existing contact, updated when sent.
The contact's seniority level. Example: Senior, Junior, Mid-level. Saved on creation; on an existing contact, updated when sent.
The contact's department. Example: Sales, IT, Marketing. Saved on creation; on an existing contact, updated when sent.
The contact's country. Example: Brazil. Saved on creation; on an existing contact, updated when sent.
The contact's state or region. Example: São Paulo. Saved on creation; on an existing contact, updated when sent.
The contact's city. Example: São Paulo. Saved on creation; on an existing contact, updated when sent.
The contact's professional experience. Saved on creation; on an existing contact, updated when sent.
The contact's custom fields. Format: { "field_slug": value }. Slugs must match custom fields registered with the CONTACT context. Invalid or nonexistent slugs are silently ignored. Values must be primitives (string, number, boolean or null). On an existing contact, only the slugs sent are updated (merge); the others stay as they are. Example: { "cpf": "12345678900", "data_nascimento": "1990-01-15", "valor_total": 1500.50 }
Show child attributes
Show child attributes
Response
Contact created, or existing contact with the same phone updated
Contact name
Contact's company
Contact ID
Contact ID (same value as _id)
Country code of the number. Absent, along with the phone, when the contact only has a WhatsApp identifier.
Contact's email
Date of the contact's last activity
The contact's phone number. Absent when the contact only has a WhatsApp identifier.
LinkedIn profile identifier
Contact's job title
Contact's seniority level
Contact's department
Contact's country
Contact's state or region
Contact's city
Contact's professional experience
Contact's skills
Values of the contact's custom fields, by slug. When saved via additional_attributes, the value comes in the envelope { id, value, type, updatedAt }. When saved via custom_fields (PUT), it comes raw: the option_id (select), the list of option_id (multi_select) or the text (text).
Show child attributes
Show child attributes
ID of the Business the contact belongs to
Notes recorded on the contact
Show child attributes
Show child attributes
Marketing opt-out per communication channel (whatsapp, email, linkedin). Absent or empty when the contact hasn't opted out of any channel.
Show child attributes
Show child attributes
The contact's WhatsApp identifier (BSUID), assigned by Meta. Opaque string.
The contact's WhatsApp @username, as Meta sends it
The contact's WhatsApp parent_user_id, as Meta sends it
Contact creation date
Contact update date