Create or update a contact
POST
/v1/contactsThe contact of the number to (with the country code): created when the account has none (201), else changed (200). name replaces the name (null clears it); fields — values of custom fields by key (GET /v1/fields), only the keys sent change, null removes one. Campaigns fill the variables {{key}} from these values. Refusals: invalid_number, validation_failed (fields.{key} in — no such field, field_value — not a value of its type). Keys with the contacts permission.
API key permission: contacts · Limit: 50 requests a second per key
Request body
| Field | Type | Required | Description |
|---|---|---|---|
to | string | yes | The number with the country code (E.164, «+5511912345678»), as for a message. Up to 32 characters. |
name | string, nullable | Up to 120 characters; null or blank clears it; left out keeps it. Up to 120 characters. | |
fields | object | Values of custom fields by key (GET /fields): only the keys sent change; null removes a value. Keys map to string, nullable values. |
Request
bash
curl -X POST "https://api.ace-send.com/v1/contacts" \
-H "Authorization: Bearer ak_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+5521998120937",
"name": "Ana Souza",
"fields": {
"promo_code": "VIP25",
"amount": "150",
"vip_since": "2026-09-14"
}
}'js
const response = await fetch('https://api.ace-send.com/v1/contacts', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.ACE_SEND_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"to": "+5521998120937",
"name": "Ana Souza",
"fields": {
"promo_code": "VIP25",
"amount": "150",
"vip_since": "2026-09-14"
}
}),
})
const result = await response.json()
if (!response.ok) {
// application/problem+json: result.code is stable, result.title is for people
throw new Error(`${result.code}: ${result.title}`)
}
console.log(result)python
import os
import requests
headers = {
"Authorization": f"Bearer {os.environ['ACE_SEND_API_KEY']}",
}
response = requests.post(
"https://api.ace-send.com/v1/contacts",
headers=headers,
json={
"to": "+5521998120937",
"name": "Ana Souza",
"fields": {
"promo_code": "VIP25",
"amount": "150",
"vip_since": "2026-09-14",
},
},
timeout=10,
)
result = response.json()
if not response.ok:
# application/problem+json: result["code"] is stable, result["title"] is for people
raise RuntimeError(f"{result['code']}: {result['title']}")
print(result)php
<?php
// composer require guzzlehttp/guzzle
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.ace-send.com/v1/']);
$response = $client->request('POST', 'contacts', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ACE_SEND_API_KEY'),
],
'json' => [
'to' => '+5521998120937',
'name' => 'Ana Souza',
'fields' => [
'promo_code' => 'VIP25',
'amount' => '150',
'vip_since' => '2026-09-14',
],
],
'http_errors' => false,
]);
$result = json_decode((string) $response->getBody(), true);
if ($response->getStatusCode() >= 400) {
// application/problem+json: $result['code'] is stable, $result['title'] is for people
throw new RuntimeException($result['code'].': '.$result['title']);
}
print_r($result);Response
200 OK
| Field | Type | Description |
|---|---|---|
id | string | The contact, cnt_…. |
to | string | E.164, as messages are sent to it. |
name | string, nullable | The name of the contact. |
fields | object | Values of custom fields by key, as kept: text, a number or an amount as «1234.5», a date as «2026-10-30». Keys map to string values. |
created_at | string | When the contact was created. |
updated_at | string | When the contact last changed. |
json
{
"id": "cnt_CgdWeHQNoWb5T3WhuKxnx",
"to": "+5521998120937",
"name": "Ana Souza",
"fields": {
"promo_code": "VIP25",
"amount": "150",
"vip_since": "2026-09-14"
},
"created_at": "2026-10-30T08:15:00+00:00",
"updated_at": "2026-11-02T12:00:00+00:00"
}201 Created
| Field | Type | Description |
|---|---|---|
id | string | The contact, cnt_…. |
to | string | E.164, as messages are sent to it. |
name | string, nullable | The name of the contact. |
fields | object | Values of custom fields by key, as kept: text, a number or an amount as «1234.5», a date as «2026-10-30». Keys map to string values. |
created_at | string | When the contact was created. |
updated_at | string | When the contact last changed. |
json
{
"id": "cnt_CgdWeHQNoWb5T3WhuKxnx",
"to": "+5521998120937",
"name": "Ana Souza",
"fields": {
"promo_code": "VIP25",
"amount": "150",
"vip_since": "2026-09-14"
},
"created_at": "2026-11-02T12:00:00+00:00",
"updated_at": "2026-11-02T12:00:00+00:00"
}Errors
Errors are application/problem+json with a stable code (all codes). Besides the errors of every request (an invalid key, a missing permission, too many requests), this endpoint answers:
| Status | Code | Meaning |
|---|---|---|
| 422 | invalid_number | The number is not a valid phone number. Send it in E.164: +, the country code and the number, without spaces or dashes. |
| 422 | validation_failed | Some fields are wrong: errors lists each field with its code and a message. |
json
{
"type": "about:blank",
"status": 422,
"code": "invalid_number",
"title": "The recipient's number is not valid"
}