https://api.atys.pro/api/v1/contacts/,
respondem JSON e têm contrato estável: mudanças incompatíveis só entram numa versão nova.
Como funciona
Tudo o que a equipe faz na tela de Contatos da Atys também pode ser feito pela API: cadastrar e
encontrar clientes, ligá-los aos canais de atendimento (WhatsApp, Instagram, e-mail…), preencher
campos personalizados, etiquetar e agir em lote. O contato é a base dos outros módulos: o
id dele é o que o CRM
(contact_ids) e a Agenda
(contact_id) pedem.
Dois conceitos:
- Contato — a pessoa ou empresa: nome, e-mail, telefone, campos personalizados.
- Canal do contato — o contato num canal de atendimento da sua empresa (por exemplo, o WhatsApp da loja). É nele que ficam atendente, setor, etiquetas de atendimento e a conversa.
Autenticação e permissões
Use uma chave de API com as permissões de Contatos que a integração precisa:
| Permissão da chave | Libera |
|---|---|
| Consultar contatos | Todos os GET: contatos, canais, campos, etiquetas, status de importação e exportação. |
| Gerenciar contatos | Criar e editar contatos e canais, campos e etiquetas; ações em lote, importação e exportação. |
| Excluir contatos | Exclusão individual e em lote. Separada de propósito: só dê se a integração realmente exclui. |
A chave enxerga os mesmos contatos e canais que o usuário API enxerga na plataforma (setores e canais atribuídos a ele) e só faz o que as permissões dele permitem.
Campos personalizados (catálogo)
Os campos personalizados do contato cadastrados em Configurações, com a categoria, na ordem da tela.
Use o id como field_id ao gravar valores. O type define o formato
aceito: text, textarea, number, price (centavos),
percentage, date, email, url, phone,
cpf_cnpj, cep, checkbox (true/false),
select e checkbox_group (uma das options).
Exemplos de código — listar campos personalizados
curl "https://api.atys.pro/api/v1/contacts/custom-fields" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/custom-fields', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts/custom-fields',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/custom-fields');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts/custom-fields', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts/custom-fields", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": [
{
"id": 55,
"name": "CPF",
"type": "cpf_cnpj",
"type_label": "CPF/CNPJ",
"description": null,
"options": null,
"category": {
"id": 3,
"name": "Informações adicionais"
},
"position": 1
},
{
"id": 57,
"name": "Interesse",
"type": "select",
"type_label": "Lista",
"description": null,
"options": [
"Compra",
"Aluguel"
],
"category": {
"id": 3,
"name": "Informações adicionais"
},
"position": 2
}
]
}
Etiquetas de contato (catálogo)
Etiquetas de atendimento que a chave pode usar — a mesma regra da tela (etiquetas restritas a setor, canal ou colaborador só aparecem para quem tem acesso).
Exemplos de código — listar etiquetas de contato
curl "https://api.atys.pro/api/v1/contacts/tags" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/tags', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts/tags',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/tags');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts/tags', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts/tags", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": [
{
"id": 9,
"name": "VIP",
"description": null,
"color": "#f59e0b"
}
]
}
Canais da empresa
Os canais de atendimento (números de WhatsApp, Instagram, Telegram…) que o usuário API atende. É o
company_channel_id que os endpoints de criação pedem: canal fora desta lista responde
422 invalid_company_channel.
Exemplos de código — listar canais da empresa
curl "https://api.atys.pro/api/v1/contacts/company-channels" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/company-channels', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts/company-channels',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/company-channels');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts/company-channels', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts/company-channels", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": [
{
"id": 44,
"name": "WhatsApp da Loja",
"channel_type": {
"id": 3,
"name": "CHANNEL_TYPE_OFC_WHATSAPP",
"description": "Whatsapp oficial"
}
},
{
"id": 45,
"name": "Comercial",
"channel_type": {
"id": 2,
"name": "CHANNEL_TYPE_WHATSAPP",
"description": "Whatsapp"
}
}
]
}
Origens, setores e atendentes
Origens possíveis de um canal do contato (filtro origin_id da listagem), setores da empresa e
usuários ativos que podem ser atendentes (sector_id e user_id dos canais).
Contatos e canais criados pela API ficam com a origem CONTACT_ORIGIN_API.
Exemplos de código — listar origens
curl "https://api.atys.pro/api/v1/contacts/origins" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/origins', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts/origins',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/origins');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts/origins', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts/origins", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": [
{
"id": 2,
"name": "CONTACT_ORIGIN_MANUAL",
"description": "Manual"
},
{
"id": 4,
"name": "CONTACT_ORIGIN_API",
"description": "API"
}
]
}
Exemplos de código — listar setores
curl "https://api.atys.pro/api/v1/contacts/sectors" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/sectors', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts/sectors',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/sectors');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts/sectors', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts/sectors", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": [
{
"id": 6,
"name": "Comercial",
"description": null
}
]
}
Exemplos de código — listar atendentes
curl "https://api.atys.pro/api/v1/contacts/users" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/users', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts/users',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/users');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts/users', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts/users", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": [
{
"id": 81,
"name": "Ana Atendente",
"email": "ana@loja.com.br"
}
]
}
Criar contato
Faz o mesmo que o botão Novo contato da tela: cria o contato e, para cada item de
channels, o canal do contato naquele canal da empresa. O identificador é normalizado como
na plataforma — no WhatsApp o número é gravado só com dígitos, com DDI e DDD. Sem channels, cria só o contato
(útil para vincular a um negócio do CRM ou a um evento da Agenda).
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name | string (255) | Sim | Nome do contato. |
email | string (100) | Não | E-mail; único por empresa. |
description | string | Não | Observações sobre o contato. |
channels | array (até 20) | Não | Canais do contato. Cada item: company_channel_id e key (obrigatórios), name, user_id e sector_id (opcionais). |
channels[].company_channel_id | integer | Sim | Um dos canais da empresa. |
channels[].key | string (50) | Sim | Identificador no canal: telefone com DDI e DDD no WhatsApp, @usuario no Instagram, id ou @usuario no Telegram. |
channels[].name | string | Não | Nome do contato naquele canal; default o name. |
channels[].user_id / sector_id | integer | Não | Atendente e setor iniciais (catálogos). |
tag_ids | integer[] | Não | Etiquetas aplicadas a cada canal criado (etiquetas). Exige channels. |
custom_fields | array | Não | [{"field_id": 55, "value": "529.982.247-25"}]. Com canais, os valores vão para o primeiro canal criado (como na tela); sem canais, para o contato. |
Exemplos de código — criar contato
curl -X POST "https://api.atys.pro/api/v1/contacts" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"name": "Maria Cliente",
"email": "maria@cliente.com.br",
"channels": [
{
"company_channel_id": 44,
"key": "+55 (11) 98888-7777"
}
],
"tag_ids": [9],
"custom_fields": [
{
"field_id": 55,
"value": "529.982.247-25"
}
]
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"name": "Maria Cliente",
"email": "maria@cliente.com.br",
"channels": [
{
"company_channel_id": 44,
"key": "+55 (11) 98888-7777"
}
],
"tag_ids": [9],
"custom_fields": [
{
"field_id": 55,
"value": "529.982.247-25"
}
]
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.post(
'https://api.atys.pro/api/v1/contacts',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'name': 'Maria Cliente',
'email': 'maria@cliente.com.br',
'channels': [
{
'company_channel_id': 44,
'key': '+55 (11) 98888-7777'
}
],
'tag_ids': [9],
'custom_fields': [
{
'field_id': 55,
'value': '529.982.247-25'
}
]
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Maria Cliente',
'email' => 'maria@cliente.com.br',
'channels' => [
[
'company_channel_id' => 44,
'key' => '+55 (11) 98888-7777'
]
],
'tag_ids' => [9],
'custom_fields' => [
[
'field_id' => 55,
'value' => '529.982.247-25'
]
]
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('POST', 'v1/contacts', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'name' => 'Maria Cliente',
'email' => 'maria@cliente.com.br',
'channels' => [
[
'company_channel_id' => 44,
'key' => '+55 (11) 98888-7777'
]
],
'tag_ids' => [9],
'custom_fields' => [
[
'field_id' => 55,
'value' => '529.982.247-25'
]
]
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"name": "Maria Cliente",
"email": "maria@cliente.com.br",
"channels": [
{
"company_channel_id": 44,
"key": "+55 (11) 98888-7777"
}
],
"tag_ids": [9],
"custom_fields": [
{
"field_id": 55,
"value": "529.982.247-25"
}
]
}`)
req, _ := http.NewRequest("POST", "https://api.atys.pro/api/v1/contacts", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"id": 5120,
"name": "Maria Cliente",
"email": "maria@cliente.com.br",
"description": null,
"kind": "person",
"organization_id": null,
"is_group": false,
"channels": [
{
"id": 77120,
"contact_id": 5120,
"name": "Maria Cliente",
"key": "5511988887777",
"is_group": false,
"channel_type": {
"id": 3,
"name": "CHANNEL_TYPE_OFC_WHATSAPP",
"description": "Whatsapp oficial"
},
"company_channel": {
"id": 44,
"name": "WhatsApp da Loja"
},
"origin": {
"id": 4,
"name": "CONTACT_ORIGIN_API"
},
"sector": null,
"assigned_user": null,
"status_id": null,
"tags": [
{
"id": 9,
"name": "VIP",
"color": "#f59e0b"
}
],
"last_message_at": "2026-11-04T15:20:00-03:00",
"created_at": "2026-11-04T15:20:00-03:00",
"updated_at": "2026-11-04T15:20:00-03:00"
}
],
"created_at": "2026-11-04T15:20:00-03:00",
"updated_at": "2026-11-04T15:20:00-03:00"
},
"meta": {
"contact_was_existing": false,
"skipped_channels": []
}
}
Erros específicos (422): invalid_company_channel, invalid_user,
invalid_sector, invalid_tag, tags_require_channel,
unknown_custom_field (com field_ids) e invalid_custom_field_value
(valor fora do formato do tipo, com field_id). Número de WhatsApp que não existe também
responde 422. Nada é gravado quando há erro de validação.
Contato que já existe
A criação não duplica pessoas, com a mesma regra da tela: se algum identificador de channels
já pertence a um contato da empresa, ou se o email já está cadastrado, o contato existente é
reaproveitado (os dados dele não são sobrescritos) e só os canais novos são criados. Canal que já existe
naquele canal da empresa é pulado e aparece em meta.skipped_channels com o
existing_channel_id. Nesse caso a resposta é 200 OK com
meta.contact_was_existing = true — por isso é seguro repetir a chamada.
Campos personalizados só são gravados quando algum canal (ou o contato) é criado; para atualizar um
contato existente use campos do canal ou do contato.
Exemplos de código — criar contato que já existe
curl -X POST "https://api.atys.pro/api/v1/contacts" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"name": "Maria",
"channels": [
{
"company_channel_id": 44,
"key": "5511988887777"
}
]
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"name": "Maria",
"channels": [
{
"company_channel_id": 44,
"key": "5511988887777"
}
]
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.post(
'https://api.atys.pro/api/v1/contacts',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'name': 'Maria',
'channels': [
{
'company_channel_id': 44,
'key': '5511988887777'
}
]
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Maria',
'channels' => [
[
'company_channel_id' => 44,
'key' => '5511988887777'
]
]
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('POST', 'v1/contacts', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'name' => 'Maria',
'channels' => [
[
'company_channel_id' => 44,
'key' => '5511988887777'
]
]
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"name": "Maria",
"channels": [
{
"company_channel_id": 44,
"key": "5511988887777"
}
]
}`)
req, _ := http.NewRequest("POST", "https://api.atys.pro/api/v1/contacts", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"id": 5120,
"name": "Maria Cliente",
"email": "maria@cliente.com.br",
"description": null,
"kind": "person",
"organization_id": null,
"is_group": false,
"channels": [
{
"id": 77120,
"contact_id": 5120,
"name": "Maria Cliente",
"key": "5511988887777",
"is_group": false,
"channel_type": {
"id": 3,
"name": "CHANNEL_TYPE_OFC_WHATSAPP",
"description": "Whatsapp oficial"
},
"company_channel": {
"id": 44,
"name": "WhatsApp da Loja"
},
"origin": {
"id": 4,
"name": "CONTACT_ORIGIN_API"
},
"sector": null,
"assigned_user": null,
"status_id": null,
"tags": [
{
"id": 9,
"name": "VIP",
"color": "#f59e0b"
}
],
"last_message_at": "2026-11-04T15:20:00-03:00",
"created_at": "2026-11-04T15:20:00-03:00",
"updated_at": "2026-11-04T15:20:00-03:00"
}
],
"created_at": "2026-11-04T15:20:00-03:00",
"updated_at": "2026-11-04T15:20:00-03:00"
},
"meta": {
"contact_was_existing": true,
"skipped_channels": [
{
"key": "5511988887777",
"channel_type_id": 3,
"reason": "already_exists",
"existing_channel_id": 77120
}
]
}
}
Listar e buscar contatos
Os contatos que a chave enxerga, do mais recentemente alterado para o mais antigo. Vale a mesma
visibilidade das conversas na plataforma: um contato aparece quando algum canal dele está num setor,
atendente e canal da empresa ao alcance do usuário API — ou quando ainda não tem canal nenhum. Em cada
contato, channels traz só os canais visíveis.
| Parâmetro | Tipo | Descrição |
|---|---|---|
q | string | Busca por nome ou e-mail (palavra a palavra, sem diferenciar acento) e por telefone/identificador do canal — com e sem o 9º dígito. |
tag_ids[] | integer[] | Contatos com algum canal que tenha uma destas etiquetas. |
company_channel_id | integer | Contatos com canal neste canal da empresa. |
channel_type_id | integer | Contatos com canal deste tipo (WhatsApp, Instagram…). |
origin_id | integer | Contatos com canal desta origem. |
updated_since | data ISO 8601 | Contatos alterados a partir desta data (nome, e-mail, descrição) — para sincronização incremental. |
page / per_page | integer | Paginação; per_page de 1 a 100 (default 25). |
Exemplos de código — buscar contatos
curl "https://api.atys.pro/api/v1/contacts?q=maria&tag_ids%5B%5D=9&per_page=25" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts?q=maria&tag_ids%5B%5D=9&per_page=25', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts?q=maria&tag_ids%5B%5D=9&per_page=25',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts?q=maria&tag_ids%5B%5D=9&per_page=25');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'query' => [
'q' => 'maria',
'tag_ids[]' => 9,
'per_page' => 25
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts?q=maria&tag_ids%5B%5D=9&per_page=25", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": [
{
"id": 5120,
"name": "Maria Cliente",
"email": "maria@cliente.com.br",
"description": null,
"kind": "person",
"organization_id": null,
"is_group": false,
"channels": [
{
"id": 77120,
"contact_id": 5120,
"name": "Maria Cliente",
"key": "5511988887777",
"is_group": false,
"channel_type": {
"id": 3,
"name": "CHANNEL_TYPE_OFC_WHATSAPP",
"description": "Whatsapp oficial"
},
"company_channel": {
"id": 44,
"name": "WhatsApp da Loja"
},
"origin": {
"id": 4,
"name": "CONTACT_ORIGIN_API"
},
"sector": null,
"assigned_user": null,
"status_id": null,
"tags": [
{
"id": 9,
"name": "VIP",
"color": "#f59e0b"
}
],
"last_message_at": "2026-11-04T15:20:00-03:00",
"created_at": "2026-11-04T15:20:00-03:00",
"updated_at": "2026-11-04T15:20:00-03:00"
}
],
"created_at": "2026-11-04T15:20:00-03:00",
"updated_at": "2026-11-04T15:20:00-03:00"
}
],
"meta": {
"page": 1,
"per_page": 25,
"total": 1,
"last_page": 1
}
}
Localizar por telefone ou e-mail
Atalho para achar o id de um contato antes de criar um negócio no CRM ou um evento na Agenda.
Informe phone (com ou sem DDI, máscara e 9º dígito) e/ou email (sem diferenciar
maiúsculas). Devolve o primeiro contato visível que casar ou 404.
Exemplos de código — localizar contato
curl "https://api.atys.pro/api/v1/contacts/lookup?phone=11988887777" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/lookup?phone=11988887777', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts/lookup?phone=11988887777',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/lookup?phone=11988887777');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts/lookup', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'query' => [
'phone' => '11988887777'
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts/lookup?phone=11988887777", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"id": 5120,
"name": "Maria Cliente",
"email": "maria@cliente.com.br",
"description": null,
"kind": "person",
"organization_id": null,
"is_group": false,
"channels": [
{
"id": 77120,
"contact_id": 5120,
"name": "Maria Cliente",
"key": "5511988887777",
"is_group": false,
"channel_type": {
"id": 3,
"name": "CHANNEL_TYPE_OFC_WHATSAPP",
"description": "Whatsapp oficial"
},
"company_channel": {
"id": 44,
"name": "WhatsApp da Loja"
},
"origin": {
"id": 4,
"name": "CONTACT_ORIGIN_API"
},
"sector": null,
"assigned_user": null,
"status_id": null,
"tags": [
{
"id": 9,
"name": "VIP",
"color": "#f59e0b"
}
],
"last_message_at": "2026-11-04T15:20:00-03:00",
"created_at": "2026-11-04T15:20:00-03:00",
"updated_at": "2026-11-04T15:20:00-03:00"
}
],
"created_at": "2026-11-04T15:20:00-03:00",
"updated_at": "2026-11-04T15:20:00-03:00"
}
}
Ver um contato
O contato com os canais visíveis. Os dados seguem as permissões do usuário API na plataforma: sem ver nome completo só o primeiro nome, sem ver número o identificador mascarado e sem ver e-mail só o domínio.
Exemplos de código — detalhe do contato
curl "https://api.atys.pro/api/v1/contacts/5120" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/5120', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts/5120',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/5120');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts/5120', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts/5120", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"id": 5120,
"name": "Maria Cliente",
"email": "maria@cliente.com.br",
"description": null,
"kind": "person",
"organization_id": null,
"is_group": false,
"channels": [
{
"id": 77120,
"contact_id": 5120,
"name": "Maria Cliente",
"key": "5511988887777",
"is_group": false,
"channel_type": {
"id": 3,
"name": "CHANNEL_TYPE_OFC_WHATSAPP",
"description": "Whatsapp oficial"
},
"company_channel": {
"id": 44,
"name": "WhatsApp da Loja"
},
"origin": {
"id": 4,
"name": "CONTACT_ORIGIN_API"
},
"sector": null,
"assigned_user": null,
"status_id": null,
"tags": [
{
"id": 9,
"name": "VIP",
"color": "#f59e0b"
}
],
"last_message_at": "2026-11-04T15:20:00-03:00",
"created_at": "2026-11-04T15:20:00-03:00",
"updated_at": "2026-11-04T15:20:00-03:00"
}
],
"created_at": "2026-11-04T15:20:00-03:00",
"updated_at": "2026-11-04T15:20:00-03:00"
}
}
Editar contato
Envie só o que muda: name, email e description. E-mail já usado por
outro contato da empresa responde 422 email_taken. Canais, campos e etiquetas têm endpoints
próprios.
Exemplos de código — editar contato
curl -X PATCH "https://api.atys.pro/api/v1/contacts/5120" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"name": "Maria Silva",
"description": "Cliente desde 2024"
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts/5120', {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"name": "Maria Silva",
"description": "Cliente desde 2024"
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.patch(
'https://api.atys.pro/api/v1/contacts/5120',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'name': 'Maria Silva',
'description': 'Cliente desde 2024'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/5120');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Maria Silva',
'description' => 'Cliente desde 2024'
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('PATCH', 'v1/contacts/5120', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'name' => 'Maria Silva',
'description' => 'Cliente desde 2024'
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"name": "Maria Silva",
"description": "Cliente desde 2024"
}`)
req, _ := http.NewRequest("PATCH", "https://api.atys.pro/api/v1/contacts/5120", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"id": 5120,
"name": "Maria Silva",
"email": "maria@cliente.com.br",
"description": "Cliente desde 2024",
"kind": "person",
"organization_id": null,
"is_group": false,
"channels": [
{
"id": 77120,
"contact_id": 5120,
"name": "Maria Cliente",
"key": "5511988887777",
"is_group": false,
"channel_type": {
"id": 3,
"name": "CHANNEL_TYPE_OFC_WHATSAPP",
"description": "Whatsapp oficial"
},
"company_channel": {
"id": 44,
"name": "WhatsApp da Loja"
},
"origin": {
"id": 4,
"name": "CONTACT_ORIGIN_API"
},
"sector": null,
"assigned_user": null,
"status_id": null,
"tags": [
{
"id": 9,
"name": "VIP",
"color": "#f59e0b"
}
],
"last_message_at": "2026-11-04T15:20:00-03:00",
"created_at": "2026-11-04T15:20:00-03:00",
"updated_at": "2026-11-04T15:20:00-03:00"
}
],
"created_at": "2026-11-04T15:20:00-03:00",
"updated_at": "2026-11-04T15:20:00-03:00"
}
}
Canais do contato
Um contato pode estar em vários canais da empresa (o mesmo cliente no WhatsApp da loja e no Instagram). É no canal que ficam atendente, setor, etiquetas, origem e a conversa. A lista traz só os canais visíveis; também é possível ver um canal pelo id.
Exemplos de código — listar canais do contato
curl "https://api.atys.pro/api/v1/contacts/5120/channels" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/5120/channels', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts/5120/channels',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/5120/channels');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts/5120/channels', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts/5120/channels", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": [
{
"id": 77120,
"contact_id": 5120,
"name": "Maria Cliente",
"key": "5511988887777",
"is_group": false,
"channel_type": {
"id": 3,
"name": "CHANNEL_TYPE_OFC_WHATSAPP",
"description": "Whatsapp oficial"
},
"company_channel": {
"id": 44,
"name": "WhatsApp da Loja"
},
"origin": {
"id": 4,
"name": "CONTACT_ORIGIN_API"
},
"sector": null,
"assigned_user": null,
"status_id": null,
"tags": [
{
"id": 9,
"name": "VIP",
"color": "#f59e0b"
}
],
"last_message_at": "2026-11-04T15:20:00-03:00",
"created_at": "2026-11-04T15:20:00-03:00",
"updated_at": "2026-11-04T15:20:00-03:00"
},
{
"id": 77131,
"contact_id": 5120,
"name": "Maria (WhatsApp)",
"key": "552177776666",
"is_group": false,
"channel_type": {
"id": 2,
"name": "CHANNEL_TYPE_WHATSAPP",
"description": "Whatsapp"
},
"company_channel": {
"id": 45,
"name": "Comercial"
},
"origin": {
"id": 4,
"name": "CONTACT_ORIGIN_API"
},
"sector": {
"id": 6,
"name": "Comercial"
},
"assigned_user": {
"id": 81,
"name": "Ana Atendente"
},
"status_id": null,
"tags": [],
"last_message_at": "2026-11-04T15:20:00-03:00",
"created_at": "2026-11-04T15:20:00-03:00",
"updated_at": "2026-11-04T15:20:00-03:00"
}
]
}
Exemplos de código — ver canal do contato
curl "https://api.atys.pro/api/v1/contacts/5120/channels/77120" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/5120/channels/77120', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts/5120/channels/77120',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/5120/channels/77120');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts/5120/channels/77120', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts/5120/channels/77120", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"id": 77120,
"contact_id": 5120,
"name": "Maria Cliente",
"key": "5511988887777",
"is_group": false,
"channel_type": {
"id": 3,
"name": "CHANNEL_TYPE_OFC_WHATSAPP",
"description": "Whatsapp oficial"
},
"company_channel": {
"id": 44,
"name": "WhatsApp da Loja"
},
"origin": {
"id": 4,
"name": "CONTACT_ORIGIN_API"
},
"sector": null,
"assigned_user": null,
"status_id": null,
"tags": [
{
"id": 9,
"name": "VIP",
"color": "#f59e0b"
}
],
"last_message_at": "2026-11-04T15:20:00-03:00",
"created_at": "2026-11-04T15:20:00-03:00",
"updated_at": "2026-11-04T15:20:00-03:00"
}
}
Adicionar canal ao contato
Liga um contato existente a mais um canal da empresa, com a mesma normalização do identificador da
criação. Repetir o mesmo canal e identificador devolve o canal existente (200 OK). Se o
identificador já é de outro contato naquele canal, responde 422 channel_key_in_use;
se for de outro contato em outro canal da empresa, a plataforma mantém a pessoa unificada e o canal novo
fica naquele contato (confira o contact_id da resposta).
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
company_channel_id | integer | Sim | Um dos canais da empresa. |
key | string (50) | Sim | Identificador no canal. |
name | string | Não | Nome do contato no canal; default o nome do contato. |
user_id / sector_id | integer | Não | Atendente e setor iniciais. |
tag_ids | integer[] | Não | Etiquetas do canal. |
Exemplos de código — adicionar canal
curl -X POST "https://api.atys.pro/api/v1/contacts/5120/channels" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"company_channel_id": 45,
"key": "5521977776666",
"name": "Maria (WhatsApp)",
"user_id": 81,
"sector_id": 6
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts/5120/channels', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"company_channel_id": 45,
"key": "5521977776666",
"name": "Maria (WhatsApp)",
"user_id": 81,
"sector_id": 6
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.post(
'https://api.atys.pro/api/v1/contacts/5120/channels',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'company_channel_id': 45,
'key': '5521977776666',
'name': 'Maria (WhatsApp)',
'user_id': 81,
'sector_id': 6
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/5120/channels');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'company_channel_id' => 45,
'key' => '5521977776666',
'name' => 'Maria (WhatsApp)',
'user_id' => 81,
'sector_id' => 6
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('POST', 'v1/contacts/5120/channels', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'company_channel_id' => 45,
'key' => '5521977776666',
'name' => 'Maria (WhatsApp)',
'user_id' => 81,
'sector_id' => 6
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"company_channel_id": 45,
"key": "5521977776666",
"name": "Maria (WhatsApp)",
"user_id": 81,
"sector_id": 6
}`)
req, _ := http.NewRequest("POST", "https://api.atys.pro/api/v1/contacts/5120/channels", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"id": 77131,
"contact_id": 5120,
"name": "Maria (WhatsApp)",
"key": "552177776666",
"is_group": false,
"channel_type": {
"id": 2,
"name": "CHANNEL_TYPE_WHATSAPP",
"description": "Whatsapp"
},
"company_channel": {
"id": 45,
"name": "Comercial"
},
"origin": {
"id": 4,
"name": "CONTACT_ORIGIN_API"
},
"sector": {
"id": 6,
"name": "Comercial"
},
"assigned_user": {
"id": 81,
"name": "Ana Atendente"
},
"status_id": null,
"tags": [],
"last_message_at": "2026-11-04T15:20:00-03:00",
"created_at": "2026-11-04T15:20:00-03:00",
"updated_at": "2026-11-04T15:20:00-03:00"
}
}
Editar canal do contato
O que o modal Editar canal da tela edita — envie só o que muda: key,
name, company_channel_id (outro canal da empresa do mesmo tipo),
user_id e sector_id (null tira o atendente/setor). Novo número de
WhatsApp é verificado; identificador já usado naquele canal responde 422. Em grupos não é
possível mudar o identificador nem o canal da empresa (422 group_channel_locked).
Exemplos de código — editar canal do contato
curl -X PATCH "https://api.atys.pro/api/v1/contacts/5120/channels/77131" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"name": "Maria WhatsApp",
"user_id": null
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts/5120/channels/77131', {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"name": "Maria WhatsApp",
"user_id": null
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.patch(
'https://api.atys.pro/api/v1/contacts/5120/channels/77131',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'name': 'Maria WhatsApp',
'user_id': None
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/5120/channels/77131');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Maria WhatsApp',
'user_id' => null
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('PATCH', 'v1/contacts/5120/channels/77131', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'name' => 'Maria WhatsApp',
'user_id' => null
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"name": "Maria WhatsApp",
"user_id": null
}`)
req, _ := http.NewRequest("PATCH", "https://api.atys.pro/api/v1/contacts/5120/channels/77131", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"id": 77131,
"contact_id": 5120,
"name": "Maria WhatsApp",
"key": "552177776666",
"is_group": false,
"channel_type": {
"id": 2,
"name": "CHANNEL_TYPE_WHATSAPP",
"description": "Whatsapp"
},
"company_channel": {
"id": 45,
"name": "Comercial"
},
"origin": {
"id": 4,
"name": "CONTACT_ORIGIN_API"
},
"sector": {
"id": 6,
"name": "Comercial"
},
"assigned_user": null,
"status_id": null,
"tags": [],
"last_message_at": "2026-11-04T15:20:00-03:00",
"created_at": "2026-11-04T15:20:00-03:00",
"updated_at": "2026-11-04T15:20:00-03:00"
}
}
Campos personalizados do contato
A plataforma guarda campos personalizados em dois lugares: no contato (a ficha do contato
no CRM) e no canal do contato (a tela de Contatos e o painel lateral do atendimento — veja
campos do canal). Estes endpoints leem e gravam os do contato.
O PUT recebe fields: [{field_id, value}] e grava só os campos enviados; valor
vazio ou null apaga o campo. Os valores são validados e normalizados pelo tipo
(CPF/CNPJ e telefone só dígitos, data AAAA-MM-DD…); fora do formato responde
422 invalid_custom_field_value.
Exemplos de código — ver campos do contato
curl "https://api.atys.pro/api/v1/contacts/5120/custom-fields" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/5120/custom-fields', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts/5120/custom-fields',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/5120/custom-fields');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts/5120/custom-fields', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts/5120/custom-fields", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": [
{
"field_id": 56,
"name": "Loja",
"type": "text",
"value": "Loja Centro"
}
]
}
Exemplos de código — gravar campos do contato
curl -X PUT "https://api.atys.pro/api/v1/contacts/5120/custom-fields" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"fields": [
{
"field_id": 56,
"value": "Loja Centro"
},
{
"field_id": 57,
"value": null
}
]
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts/5120/custom-fields', {
method: 'PUT',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"fields": [
{
"field_id": 56,
"value": "Loja Centro"
},
{
"field_id": 57,
"value": null
}
]
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.put(
'https://api.atys.pro/api/v1/contacts/5120/custom-fields',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'fields': [
{
'field_id': 56,
'value': 'Loja Centro'
},
{
'field_id': 57,
'value': None
}
]
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/5120/custom-fields');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'fields' => [
[
'field_id' => 56,
'value' => 'Loja Centro'
],
[
'field_id' => 57,
'value' => null
]
]
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('PUT', 'v1/contacts/5120/custom-fields', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'fields' => [
[
'field_id' => 56,
'value' => 'Loja Centro'
],
[
'field_id' => 57,
'value' => null
]
]
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"fields": [
{
"field_id": 56,
"value": "Loja Centro"
},
{
"field_id": 57,
"value": null
}
]
}`)
req, _ := http.NewRequest("PUT", "https://api.atys.pro/api/v1/contacts/5120/custom-fields", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": [
{
"field_id": 56,
"name": "Loja",
"type": "text",
"value": "Loja Centro"
}
]
}
Campos personalizados do canal
Os campos que a equipe vê e edita na tela de Contatos e no atendimento, gravados pela mesma rotina da tela. Mesmo formato e mesmas regras dos campos do contato.
Exemplos de código — ver campos do canal
curl "https://api.atys.pro/api/v1/contacts/5120/channels/77120/custom-fields" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/5120/channels/77120/custom-fields', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts/5120/channels/77120/custom-fields',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/5120/channels/77120/custom-fields');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts/5120/channels/77120/custom-fields', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts/5120/channels/77120/custom-fields", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": [
{
"field_id": 55,
"name": "CPF",
"type": "cpf_cnpj",
"value": "52998224725"
}
]
}
Exemplos de código — gravar campos do canal
curl -X PUT "https://api.atys.pro/api/v1/contacts/5120/channels/77120/custom-fields" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"fields": [
{
"field_id": 55,
"value": "529.982.247-25"
}
]
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts/5120/channels/77120/custom-fields', {
method: 'PUT',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"fields": [
{
"field_id": 55,
"value": "529.982.247-25"
}
]
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.put(
'https://api.atys.pro/api/v1/contacts/5120/channels/77120/custom-fields',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'fields': [
{
'field_id': 55,
'value': '529.982.247-25'
}
]
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/5120/channels/77120/custom-fields');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'fields' => [
[
'field_id' => 55,
'value' => '529.982.247-25'
]
]
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('PUT', 'v1/contacts/5120/channels/77120/custom-fields', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'fields' => [
[
'field_id' => 55,
'value' => '529.982.247-25'
]
]
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"fields": [
{
"field_id": 55,
"value": "529.982.247-25"
}
]
}`)
req, _ := http.NewRequest("PUT", "https://api.atys.pro/api/v1/contacts/5120/channels/77120/custom-fields", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": [
{
"field_id": 55,
"name": "CPF",
"type": "cpf_cnpj",
"value": "52998224725"
}
]
}
Etiquetas do canal
As etiquetas de atendimento ficam no canal do contato. tag_ids substitui o
conjunto (envie [] para limpar); etiquetas que o usuário API não enxerga e já estavam no canal
são preservadas. As automações de etiqueta atribuída/removida disparam e, no WhatsApp Business,
a etiqueta correspondente é sincronizada — como na tela. Para etiquetar muitos contatos de uma vez, use as
ações em lote.
Exemplos de código — definir etiquetas do canal
curl -X PUT "https://api.atys.pro/api/v1/contacts/5120/channels/77120/tags" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"tag_ids": [9]
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts/5120/channels/77120/tags', {
method: 'PUT',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"tag_ids": [9]
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.put(
'https://api.atys.pro/api/v1/contacts/5120/channels/77120/tags',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'tag_ids': [9]
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/5120/channels/77120/tags');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'tag_ids' => [9]
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('PUT', 'v1/contacts/5120/channels/77120/tags', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'tag_ids' => [9]
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"tag_ids": [9]
}`)
req, _ := http.NewRequest("PUT", "https://api.atys.pro/api/v1/contacts/5120/channels/77120/tags", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"id": 77120,
"contact_id": 5120,
"name": "Maria Cliente",
"key": "5511988887777",
"is_group": false,
"channel_type": {
"id": 3,
"name": "CHANNEL_TYPE_OFC_WHATSAPP",
"description": "Whatsapp oficial"
},
"company_channel": {
"id": 44,
"name": "WhatsApp da Loja"
},
"origin": {
"id": 4,
"name": "CONTACT_ORIGIN_API"
},
"sector": null,
"assigned_user": null,
"status_id": null,
"tags": [
{
"id": 9,
"name": "VIP",
"color": "#f59e0b"
}
],
"last_message_at": "2026-11-04T15:20:00-03:00",
"created_at": "2026-11-04T15:20:00-03:00",
"updated_at": "2026-11-04T15:20:00-03:00"
}
}
Excluir canal do contato
O mesmo que Apagar contato numa linha da tela: apaga o canal, a conversa dele e os campos personalizados do canal. Se era o último canal, o contato também é excluído. Irreversível.
Exemplos de código — excluir canal do contato
curl -X DELETE "https://api.atys.pro/api/v1/contacts/5120/channels/77131" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/5120/channels/77131', {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
console.log(response.status); // 204 = excluído
import os
import requests
response = requests.delete(
'https://api.atys.pro/api/v1/contacts/5120/channels/77131',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
print(response.status_code) # 204 = excluído
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/5120/channels/77131');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('DELETE', 'v1/contacts/5120/channels/77131', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("DELETE", "https://api.atys.pro/api/v1/contacts/5120/channels/77131", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
Excluir contato
Exclui o contato com todos os canais e conversas. Se o contato tiver algum canal que o usuário API não
enxerga, nada é excluído e a resposta é 422 contact_has_hidden_channels — exclua os canais
visíveis individualmente. Irreversível.
Exemplos de código — excluir contato
curl -X DELETE "https://api.atys.pro/api/v1/contacts/5120" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/5120', {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
console.log(response.status); // 204 = excluído
import os
import requests
response = requests.delete(
'https://api.atys.pro/api/v1/contacts/5120',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
print(response.status_code) # 204 = excluído
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/5120');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('DELETE', 'v1/contacts/5120', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("DELETE", "https://api.atys.pro/api/v1/contacts/5120", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
Ações em lote: seleção, filtros e fila
São as ações do menu da tela de Contatos: atribuir atendente, transferir de canal, etiquetar,
encerrar conversas, excluir, importar e exportar. Elas agem sobre os canais do contato
(as linhas da tela): os ids aceitos são contact_channel_ids, até 500 por chamada.
Transferir, etiquetar e exportar aceitam duas formas de escolher os canais:
- Por ids (
contact_channel_ids) — a ação roda na hora e responde200com o resultado. Se algum id não for da sua empresa ou estiver fora da visibilidade do usuário API, nada é feito: a resposta é422 invalid_idscom a lista emdetails.invalid_ids. - Por filtros (
"scope": "filtered"+filters) — como "todos os filtrados" na tela. A ação entra na fila e responde202 Acceptedcom umoperation_id. Sem filtros, vale para todos os canais que o usuário API enxerga.
Filtro (filters.*) | Tipo | Descrição |
|---|---|---|
search | string | Busca por nome, e-mail ou telefone, como a busca da tela. |
company_channel_id | integer | Canal da empresa (o WhatsApp da loja, por exemplo). |
origin_id | integer | Origem do contato. |
user_id / without_user | integer / boolean | Atendente responsável, ou só os sem atendente. |
sector_id / without_sector | integer / boolean | Setor, ou só os sem setor. |
status_id | integer | Status da conversa. |
tag_ids + tag_match | array de integer + string | Etiquetas de contato; any (default) = qualquer uma, all = todas. |
created_from / created_to | date (AAAA-MM-DD) | Período de criação (os dois juntos). |
updated_from / updated_to | date (AAAA-MM-DD) | Período da última atualização (os dois juntos). |
Acompanhe uma operação em fila pelo operation_id. status vai de
processing a completed; result traz as contagens da ação
(transferidos, alterados, encerrados…). O status fica disponível por 24 horas e só para a chave do
usuário que iniciou a operação.
Exemplos de código — status de uma ação em lote
curl "https://api.atys.pro/api/v1/contacts/bulk/trf_6543a1b2c3d4e5.12345678" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/bulk/trf_6543a1b2c3d4e5.12345678', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts/bulk/trf_6543a1b2c3d4e5.12345678',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/bulk/trf_6543a1b2c3d4e5.12345678');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts/bulk/trf_6543a1b2c3d4e5.12345678', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts/bulk/trf_6543a1b2c3d4e5.12345678", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"operation_id": "trf_6543a1b2c3d4e5.12345678",
"operation": "transfer",
"status": "completed",
"total": 1240,
"result": {
"transferred": 1198,
"merged": 12,
"skipped": 30,
"failed": 0
},
"started_at": "2026-11-04T16:20:00-03:00",
"completed_at": "2026-11-04T16:23:41-03:00"
}
}
operation é transfer, tags_add, tags_remove ou
close. Ao terminar, a importação também dispara o webhook contact.import.completed.
Motivos de encerramento
Todo encerramento de conversa pede um motivo, e cada canal tem os seus (os três do sistema —
Resolvido, Parou de responder e Spam — e os criados pela equipe).
has_satisfaction_survey indica se, encerrando com esse motivo, a pesquisa de satisfação é
enviada pelo usuário API. Filtre por company_channel_id; a lista é paginada
(page, per_page até 100).
Exemplos de código — listar motivos de encerramento
curl "https://api.atys.pro/api/v1/contacts/close-reasons?company_channel_id=44" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/close-reasons?company_channel_id=44', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts/close-reasons?company_channel_id=44',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/close-reasons?company_channel_id=44');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts/close-reasons', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'query' => [
'company_channel_id' => 44
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts/close-reasons?company_channel_id=44", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": [
{
"id": 812,
"text": "Resolvido",
"slug": "resolved",
"is_system": true,
"company_channel_id": 44,
"has_satisfaction_survey": true
},
{
"id": 815,
"text": "Venda concluída",
"slug": null,
"is_system": false,
"company_channel_id": 44,
"has_satisfaction_survey": false
}
],
"meta": {
"page": 1,
"per_page": 25,
"total": 2,
"last_page": 1
}
}
Para criar um motivo novo num canal, como o "Novo motivo de encerramento" do modal: company_channel_id
e text (até 100 caracteres). O canal precisa ser um dos canais do usuário API
(invalid_company_channel).
Exemplos de código — criar motivo de encerramento
curl -X POST "https://api.atys.pro/api/v1/contacts/close-reasons" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"company_channel_id": 44,
"text": "Venda concluída"
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts/close-reasons', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"company_channel_id": 44,
"text": "Venda concluída"
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.post(
'https://api.atys.pro/api/v1/contacts/close-reasons',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'company_channel_id': 44,
'text': 'Venda concluída'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/close-reasons');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'company_channel_id' => 44,
'text' => 'Venda concluída'
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('POST', 'v1/contacts/close-reasons', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'company_channel_id' => 44,
'text' => 'Venda concluída'
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"company_channel_id": 44,
"text": "Venda concluída"
}`)
req, _ := http.NewRequest("POST", "https://api.atys.pro/api/v1/contacts/close-reasons", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"id": 815,
"text": "Venda concluída",
"slug": null,
"is_system": false,
"company_channel_id": 44,
"has_satisfaction_survey": false
}
}
Atribuir atendente em lote
Define o atendente (e, opcionalmente, o setor) de vários canais de uma vez — "Atribuir contato ao
atendente" na tela. O atendente precisa ser um usuário da empresa (invalid_user) e, com
sector_id, pertencer ao setor (user_not_in_sector). Grupos não têm atendente:
são ignorados e voltam em skipped_group_ids. Só por ids.
Exemplos de código — atribuir atendente
curl -X POST "https://api.atys.pro/api/v1/contacts/bulk/assign" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"contact_channel_ids": [9120, 9121, 9122],
"user_id": 77,
"sector_id": 12
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts/bulk/assign', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"contact_channel_ids": [9120, 9121, 9122],
"user_id": 77,
"sector_id": 12
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.post(
'https://api.atys.pro/api/v1/contacts/bulk/assign',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'contact_channel_ids': [9120, 9121, 9122],
'user_id': 77,
'sector_id': 12
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/bulk/assign');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'contact_channel_ids' => [9120, 9121, 9122],
'user_id' => 77,
'sector_id' => 12
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('POST', 'v1/contacts/bulk/assign', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'contact_channel_ids' => [9120, 9121, 9122],
'user_id' => 77,
'sector_id' => 12
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"contact_channel_ids": [9120, 9121, 9122],
"user_id": 77,
"sector_id": 12
}`)
req, _ := http.NewRequest("POST", "https://api.atys.pro/api/v1/contacts/bulk/assign", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"updated": 3,
"skipped_group_ids": [],
"user": {
"id": 77,
"name": "Carla Atendente"
},
"sector_id": 12
}
}
Transferir para outro canal
Move as conversas para outro canal da empresa do mesmo tipo (WhatsApp para WhatsApp, por exemplo) — "Transferir para outro canal" na tela. O canal de destino precisa estar entre os canais do usuário API, a menos que ele tenha a permissão de transferir para qualquer canal.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
target_company_channel_id | integer | Sim | Canal de destino. |
contact_channel_ids | array de integer | Sem scope | Até 500 canais do contato. |
scope + filters | string + objeto | Não | filtered para usar os filtros (em fila). |
transfer_messages | boolean | Não | Leva o histórico de mensagens junto. Default false. |
confirm_merge | boolean | Não | Se o contato já existe no destino, mescla as duas conversas. Sem isso, esses casos são ignorados. |
Grupos, tipos de canal incompatíveis, conversas que já estão no destino e conflitos sem mesclagem são
ignorados e aparecem nas contagens skipped_*.
Exemplos de código — transferir canais selecionados
curl -X POST "https://api.atys.pro/api/v1/contacts/bulk/transfer" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"contact_channel_ids": [9120, 9121, 9122],
"target_company_channel_id": 51,
"transfer_messages": true,
"confirm_merge": false
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts/bulk/transfer', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"contact_channel_ids": [9120, 9121, 9122],
"target_company_channel_id": 51,
"transfer_messages": true,
"confirm_merge": false
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.post(
'https://api.atys.pro/api/v1/contacts/bulk/transfer',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'contact_channel_ids': [9120, 9121, 9122],
'target_company_channel_id': 51,
'transfer_messages': True,
'confirm_merge': False
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/bulk/transfer');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'contact_channel_ids' => [9120, 9121, 9122],
'target_company_channel_id' => 51,
'transfer_messages' => true,
'confirm_merge' => false
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('POST', 'v1/contacts/bulk/transfer', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'contact_channel_ids' => [9120, 9121, 9122],
'target_company_channel_id' => 51,
'transfer_messages' => true,
'confirm_merge' => false
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"contact_channel_ids": [9120, 9121, 9122],
"target_company_channel_id": 51,
"transfer_messages": true,
"confirm_merge": false
}`)
req, _ := http.NewRequest("POST", "https://api.atys.pro/api/v1/contacts/bulk/transfer", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"transferred": 2,
"merged": 0,
"skipped_group": 0,
"skipped_incompatible_type": 0,
"skipped_same_channel": 0,
"skipped_needs_merge": 1
}
}
Exemplos de código — transferir pelos filtros
curl -X POST "https://api.atys.pro/api/v1/contacts/bulk/transfer" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"scope": "filtered",
"filters": {
"company_channel_id": 44,
"without_user": true
},
"target_company_channel_id": 51
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts/bulk/transfer', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"scope": "filtered",
"filters": {
"company_channel_id": 44,
"without_user": true
},
"target_company_channel_id": 51
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.post(
'https://api.atys.pro/api/v1/contacts/bulk/transfer',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'scope': 'filtered',
'filters': {
'company_channel_id': 44,
'without_user': True
},
'target_company_channel_id': 51
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/bulk/transfer');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'scope' => 'filtered',
'filters' => [
'company_channel_id' => 44,
'without_user' => true
],
'target_company_channel_id' => 51
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('POST', 'v1/contacts/bulk/transfer', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'scope' => 'filtered',
'filters' => [
'company_channel_id' => 44,
'without_user' => true
],
'target_company_channel_id' => 51
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"scope": "filtered",
"filters": {
"company_channel_id": 44,
"without_user": true
},
"target_company_channel_id": 51
}`)
req, _ := http.NewRequest("POST", "https://api.atys.pro/api/v1/contacts/bulk/transfer", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"operation_id": "trf_6543a1b2c3d4e5.12345678",
"status": "processing",
"total": 1240
}
}
Adicionar ou remover etiquetas
"Adicionar etiquetas" e "Remover etiquetas" da tela: action é add ou
remove e tag_ids são etiquetas de contato (até 50) que o usuário API pode usar
— senão 422 invalid_tag_ids e nada muda. Etiquetas sincronizadas com o WhatsApp não podem
ser alteradas em massa.
Por padrão, etiquetar em lote não dispara as automações das etiquetas (workflows e
conversões de anúncio). Para disparar, envie "run_automations": true.
changed conta os canais que mudaram; unchanged, os que já tinham (ou não tinham) as etiquetas.
Exemplos de código — etiquetar canais selecionados
curl -X POST "https://api.atys.pro/api/v1/contacts/bulk/tags" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"action": "add",
"tag_ids": [310, 311],
"contact_channel_ids": [9120, 9121, 9122]
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts/bulk/tags', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"action": "add",
"tag_ids": [310, 311],
"contact_channel_ids": [9120, 9121, 9122]
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.post(
'https://api.atys.pro/api/v1/contacts/bulk/tags',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'action': 'add',
'tag_ids': [310, 311],
'contact_channel_ids': [9120, 9121, 9122]
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/bulk/tags');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'action' => 'add',
'tag_ids' => [310, 311],
'contact_channel_ids' => [9120, 9121, 9122]
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('POST', 'v1/contacts/bulk/tags', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'action' => 'add',
'tag_ids' => [310, 311],
'contact_channel_ids' => [9120, 9121, 9122]
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"action": "add",
"tag_ids": [310, 311],
"contact_channel_ids": [9120, 9121, 9122]
}`)
req, _ := http.NewRequest("POST", "https://api.atys.pro/api/v1/contacts/bulk/tags", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"action": "add",
"changed": 3,
"unchanged": 0
}
}
Exemplos de código — remover etiqueta pelos filtros
curl -X POST "https://api.atys.pro/api/v1/contacts/bulk/tags" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"action": "remove",
"tag_ids": [310],
"scope": "filtered",
"filters": {
"company_channel_id": 44,
"tag_ids": [310],
"created_from": "2026-10-01",
"created_to": "2026-10-31"
}
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts/bulk/tags', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"action": "remove",
"tag_ids": [310],
"scope": "filtered",
"filters": {
"company_channel_id": 44,
"tag_ids": [310],
"created_from": "2026-10-01",
"created_to": "2026-10-31"
}
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.post(
'https://api.atys.pro/api/v1/contacts/bulk/tags',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'action': 'remove',
'tag_ids': [310],
'scope': 'filtered',
'filters': {
'company_channel_id': 44,
'tag_ids': [310],
'created_from': '2026-10-01',
'created_to': '2026-10-31'
}
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/bulk/tags');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'action' => 'remove',
'tag_ids' => [310],
'scope' => 'filtered',
'filters' => [
'company_channel_id' => 44,
'tag_ids' => [310],
'created_from' => '2026-10-01',
'created_to' => '2026-10-31'
]
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('POST', 'v1/contacts/bulk/tags', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'action' => 'remove',
'tag_ids' => [310],
'scope' => 'filtered',
'filters' => [
'company_channel_id' => 44,
'tag_ids' => [310],
'created_from' => '2026-10-01',
'created_to' => '2026-10-31'
]
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"action": "remove",
"tag_ids": [310],
"scope": "filtered",
"filters": {
"company_channel_id": 44,
"tag_ids": [310],
"created_from": "2026-10-01",
"created_to": "2026-10-31"
}
}`)
req, _ := http.NewRequest("POST", "https://api.atys.pro/api/v1/contacts/bulk/tags", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"operation_id": "tag_6543a1b2c3d4e7.55667788",
"status": "processing",
"total": 860
}
}
Encerrar conversas
Encerra conversas de um canal com um motivo do próprio canal
(invalid_close_reason se não for). Conversas já encerradas e grupos são ignorados.
- Com
contact_channel_ids— encerra só esses canais, na hora. Todos precisam ser docompany_channel_idinformado. - Sem ids — encerra todas as conversas abertas do canal, em fila, como o "Encerrar conversas" da tela. Só nesse modo dá para enviar a pesquisa de satisfação (
send_satisfaction_survey); com ids, o pedido voltasurvey_requires_whole_channel.
Antes de encerrar o canal inteiro, a prévia mostra se o motivo envia a pesquisa, por onde
(chat ou email) e para quantas conversas: só contatos que escreveram nas
últimas recent_hours horas recebem, até max_surveys por encerramento, enviadas aos poucos.
Exemplos de código — prévia do encerramento do canal
curl "https://api.atys.pro/api/v1/contacts/bulk/close/survey-preview?company_channel_id=44&close_reason_id=812" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/bulk/close/survey-preview?company_channel_id=44&close_reason_id=812', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts/bulk/close/survey-preview?company_channel_id=44&close_reason_id=812',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/bulk/close/survey-preview?company_channel_id=44&close_reason_id=812');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts/bulk/close/survey-preview', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'query' => [
'company_channel_id' => 44,
'close_reason_id' => 812
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts/bulk/close/survey-preview?company_channel_id=44&close_reason_id=812", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"survey_available": true,
"reason": null,
"via": "chat",
"open_conversations": 318,
"eligible": 42,
"recent_hours": 24,
"max_surveys": 200
}
}
Exemplos de código — encerrar canais selecionados
curl -X POST "https://api.atys.pro/api/v1/contacts/bulk/close" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"company_channel_id": 44,
"close_reason_id": 812,
"contact_channel_ids": [9120, 9121, 9122]
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts/bulk/close', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"company_channel_id": 44,
"close_reason_id": 812,
"contact_channel_ids": [9120, 9121, 9122]
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.post(
'https://api.atys.pro/api/v1/contacts/bulk/close',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'company_channel_id': 44,
'close_reason_id': 812,
'contact_channel_ids': [9120, 9121, 9122]
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/bulk/close');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'company_channel_id' => 44,
'close_reason_id' => 812,
'contact_channel_ids' => [9120, 9121, 9122]
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('POST', 'v1/contacts/bulk/close', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'company_channel_id' => 44,
'close_reason_id' => 812,
'contact_channel_ids' => [9120, 9121, 9122]
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"company_channel_id": 44,
"close_reason_id": 812,
"contact_channel_ids": [9120, 9121, 9122]
}`)
req, _ := http.NewRequest("POST", "https://api.atys.pro/api/v1/contacts/bulk/close", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"closed": 3,
"skipped_groups": 0
}
}
Exemplos de código — encerrar todas as conversas do canal
curl -X POST "https://api.atys.pro/api/v1/contacts/bulk/close" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"company_channel_id": 44,
"close_reason_id": 812,
"send_satisfaction_survey": true
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts/bulk/close', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"company_channel_id": 44,
"close_reason_id": 812,
"send_satisfaction_survey": true
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.post(
'https://api.atys.pro/api/v1/contacts/bulk/close',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'company_channel_id': 44,
'close_reason_id': 812,
'send_satisfaction_survey': True
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/bulk/close');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'company_channel_id' => 44,
'close_reason_id' => 812,
'send_satisfaction_survey' => true
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('POST', 'v1/contacts/bulk/close', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'company_channel_id' => 44,
'close_reason_id' => 812,
'send_satisfaction_survey' => true
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"company_channel_id": 44,
"close_reason_id": 812,
"send_satisfaction_survey": true
}`)
req, _ := http.NewRequest("POST", "https://api.atys.pro/api/v1/contacts/bulk/close", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"operation_id": "cls_6543a1b2c3d4e8.99887766",
"status": "processing",
"total": 318
}
}
Excluir em lote
Exclui os canais do contato e, quando o contato fica sem nenhum canal, o próprio contato — com as conversas e os campos personalizados do canal. Não tem volta. Só por ids (até 500): a exclusão por filtros ou da empresa inteira, que existe na tela, não é oferecida pela API. Exige a permissão Excluir contatos na chave.
Exemplos de código — excluir canais selecionados
curl -X POST "https://api.atys.pro/api/v1/contacts/bulk/delete" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"contact_channel_ids": [9120, 9121, 9122]
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts/bulk/delete', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"contact_channel_ids": [9120, 9121, 9122]
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.post(
'https://api.atys.pro/api/v1/contacts/bulk/delete',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'contact_channel_ids': [9120, 9121, 9122]
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/bulk/delete');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'contact_channel_ids' => [9120, 9121, 9122]
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('POST', 'v1/contacts/bulk/delete', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'contact_channel_ids' => [9120, 9121, 9122]
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"contact_channel_ids": [9120, 9121, 9122]
}`)
req, _ := http.NewRequest("POST", "https://api.atys.pro/api/v1/contacts/bulk/delete", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"deleted_channels": 3,
"deleted_contacts": 2,
"contact_channel_ids": [
9120,
9121,
9122
]
}
}
Importar contatos
A mesma importação do modal "Importar contatos", com as colunas da planilha em JSON: name
e phone obrigatórios, email opcional. Até 5.000 linhas por
chamada, num canal da empresa que seja um dos canais do usuário API (Minichat não aceita importação:
unsupported_channel).
- O telefone pode vir com espaços, traços, parênteses e
+; vale só os dígitos. Com"number_type": "national", números sem o 55 ganham o código do Brasil. - Telefone que já existe no canal é contado em
duplicatese não muda. Se o telefone ou o e-mail já pertencem a um contato da empresa, o canal novo é ligado a ele. - Num canal de WhatsApp com até 50 números, eles são conferidos no WhatsApp; os que não existem entram em
invalid.
Até 100 linhas a importação roda na hora e responde 200 com as contagens. Acima disso, entra
na fila e responde 202 Accepted com o import_id.
Exemplos de código — importar contatos
curl -X POST "https://api.atys.pro/api/v1/contacts/import" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"company_channel_id": 44,
"number_type": "national",
"contacts": [
{
"name": "Maria Cliente",
"phone": "(11) 98888-0001",
"email": "maria@cliente.com.br"
},
{
"name": "João Comprador",
"phone": "11988880002"
}
]
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts/import', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"company_channel_id": 44,
"number_type": "national",
"contacts": [
{
"name": "Maria Cliente",
"phone": "(11) 98888-0001",
"email": "maria@cliente.com.br"
},
{
"name": "João Comprador",
"phone": "11988880002"
}
]
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.post(
'https://api.atys.pro/api/v1/contacts/import',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'company_channel_id': 44,
'number_type': 'national',
'contacts': [
{
'name': 'Maria Cliente',
'phone': '(11) 98888-0001',
'email': 'maria@cliente.com.br'
},
{
'name': 'João Comprador',
'phone': '11988880002'
}
]
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/import');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'company_channel_id' => 44,
'number_type' => 'national',
'contacts' => [
[
'name' => 'Maria Cliente',
'phone' => '(11) 98888-0001',
'email' => 'maria@cliente.com.br'
],
[
'name' => 'João Comprador',
'phone' => '11988880002'
]
]
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('POST', 'v1/contacts/import', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'company_channel_id' => 44,
'number_type' => 'national',
'contacts' => [
[
'name' => 'Maria Cliente',
'phone' => '(11) 98888-0001',
'email' => 'maria@cliente.com.br'
],
[
'name' => 'João Comprador',
'phone' => '11988880002'
]
]
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"company_channel_id": 44,
"number_type": "national",
"contacts": [
{
"name": "Maria Cliente",
"phone": "(11) 98888-0001",
"email": "maria@cliente.com.br"
},
{
"name": "João Comprador",
"phone": "11988880002"
}
]
}`)
req, _ := http.NewRequest("POST", "https://api.atys.pro/api/v1/contacts/import", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"import_id": null,
"status": "completed",
"total": 2,
"imported": 1,
"duplicates": 1,
"invalid": 0,
"failed": 0
}
}
{
"data": {
"import_id": "import_6543a1b2c3d4f0.87654321",
"status": "processing",
"total": 3200
}
}
Acompanhe pelo import_id (24 horas, só a chave de quem importou). skipped soma
duplicados e inválidos; status termina em completed ou failed (com error).
O webhook contact.import.completed também avisa o fim.
Exemplos de código — status da importação
curl "https://api.atys.pro/api/v1/contacts/import/import_6543a1b2c3d4f0.87654321" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/import/import_6543a1b2c3d4f0.87654321', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts/import/import_6543a1b2c3d4f0.87654321',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/import/import_6543a1b2c3d4f0.87654321');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts/import/import_6543a1b2c3d4f0.87654321', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts/import/import_6543a1b2c3d4f0.87654321", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"import_id": "import_6543a1b2c3d4f0.87654321",
"status": "processing",
"total": 3200,
"processed": 1500,
"imported": 1421,
"skipped": 74,
"failed": 5,
"error": null,
"started_at": "2026-11-04T16:30:00-03:00"
}
}
Exportar contatos
Gera a mesma planilha .xlsx do "Exportar contatos" da tela, por ids ou pelos
filtros. fields escolhe as colunas, na ordem:
name, email, phone, channel, company_channel,
origin, attendant, sector, created_at,
updated_at e tags. Nome completo, telefone e e-mail seguem as permissões do
usuário API (sem elas, saem abreviados ou mascarados). Até 50.000 linhas por exportação
(export_too_large); sem nenhuma, nothing_to_export.
Até 1.000 linhas o arquivo fica pronto na hora (200, com o download_token);
acima disso, a exportação entra na fila (202 Accepted) e o token aparece no status.
Exemplos de código — exportar contatos
curl -X POST "https://api.atys.pro/api/v1/contacts/export" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"fields": ["name", "email", "phone", "attendant", "tags"],
"scope": "filtered",
"filters": {
"company_channel_id": 44,
"tag_ids": [310],
"created_from": "2026-10-01",
"created_to": "2026-10-31"
}
}'
const response = await fetch('https://api.atys.pro/api/v1/contacts/export', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"fields": ["name", "email", "phone", "attendant", "tags"],
"scope": "filtered",
"filters": {
"company_channel_id": 44,
"tag_ids": [310],
"created_from": "2026-10-01",
"created_to": "2026-10-31"
}
})
});
const { data, error } = await response.json();
import os
import requests
response = requests.post(
'https://api.atys.pro/api/v1/contacts/export',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
},
json={
'fields': ['name', 'email', 'phone', 'attendant', 'tags'],
'scope': 'filtered',
'filters': {
'company_channel_id': 44,
'tag_ids': [310],
'created_from': '2026-10-01',
'created_to': '2026-10-31'
}
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/export');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'fields' => ['name', 'email', 'phone', 'attendant', 'tags'],
'scope' => 'filtered',
'filters' => [
'company_channel_id' => 44,
'tag_ids' => [310],
'created_from' => '2026-10-01',
'created_to' => '2026-10-31'
]
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('POST', 'v1/contacts/export', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
'json' => [
'fields' => ['name', 'email', 'phone', 'attendant', 'tags'],
'scope' => 'filtered',
'filters' => [
'company_channel_id' => 44,
'tag_ids' => [310],
'created_from' => '2026-10-01',
'created_to' => '2026-10-31'
]
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
body := []byte(`{
"fields": ["name", "email", "phone", "attendant", "tags"],
"scope": "filtered",
"filters": {
"company_channel_id": 44,
"tag_ids": [310],
"created_from": "2026-10-01",
"created_to": "2026-10-31"
}
}`)
req, _ := http.NewRequest("POST", "https://api.atys.pro/api/v1/contacts/export", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"export_id": null,
"status": "completed",
"total": 312,
"download_token": "3f6c2a9e-8d41-4b7a-9c55-1e2f3a4b5c6d",
"expires_at": "2026-11-04T17:40:00-03:00"
}
}
{
"data": {
"export_id": "export_6543a1b2c3d4f9.11223344",
"status": "processing",
"total": 18450
}
}
Exemplos de código — status da exportação
curl "https://api.atys.pro/api/v1/contacts/export/export_6543a1b2c3d4f9.11223344" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/export/export_6543a1b2c3d4f9.11223344', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts/export/export_6543a1b2c3d4f9.11223344',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/export/export_6543a1b2c3d4f9.11223344');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts/export/export_6543a1b2c3d4f9.11223344', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts/export/export_6543a1b2c3d4f9.11223344", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
{
"data": {
"export_id": "export_6543a1b2c3d4f9.11223344",
"status": "completed",
"total": 18450,
"download_token": "3f6c2a9e-8d41-4b7a-9c55-1e2f3a4b5c6d",
"expires_at": "2026-11-04T17:52:00-03:00",
"started_at": "2026-11-04T16:50:00-03:00",
"completed_at": "2026-11-04T16:52:10-03:00",
"error": null
}
}
O download devolve o arquivo (Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet),
não JSON. O token vale 60 minutos e um único download, só com a chave de quem exportou;
depois disso, 404 — gere a exportação de novo.
Exemplos de código — baixar a planilha
curl "https://api.atys.pro/api/v1/contacts/export/3f6c2a9e-8d41-4b7a-9c55-1e2f3a4b5c6d/download" \
-H "Authorization: Bearer atys_live_SUA_CHAVE" \
-H "Accept: application/json"
const response = await fetch('https://api.atys.pro/api/v1/contacts/export/3f6c2a9e-8d41-4b7a-9c55-1e2f3a4b5c6d/download', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.ATYS_API_KEY}`,
'Accept': 'application/json'
}
});
const { data, error } = await response.json();
import os
import requests
response = requests.get(
'https://api.atys.pro/api/v1/contacts/export/3f6c2a9e-8d41-4b7a-9c55-1e2f3a4b5c6d/download',
headers={
'Authorization': f"Bearer {os.environ['ATYS_API_KEY']}",
'Accept': 'application/json'
}
)
result = response.json()
<?php
$ch = curl_init('https://api.atys.pro/api/v1/contacts/export/3f6c2a9e-8d41-4b7a-9c55-1e2f3a4b5c6d/download');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.getenv('ATYS_API_KEY'),
'Accept: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.atys.pro/api/']);
$response = $client->request('GET', 'v1/contacts/export/3f6c2a9e-8d41-4b7a-9c55-1e2f3a4b5c6d/download', [
'headers' => [
'Authorization' => 'Bearer '.getenv('ATYS_API_KEY'),
'Accept' => 'application/json',
],
]);
$result = json_decode($response->getBody(), true);
package main
import (
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.atys.pro/api/v1/contacts/export/3f6c2a9e-8d41-4b7a-9c55-1e2f3a4b5c6d/download", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ATYS_API_KEY"))
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
Erros
Todo erro da v1 tem o mesmo formato: {"error": {"code", "message", "details"}}. Use o
code no seu tratamento; a message é para leitura humana.
422 validation_failed — dados inválidos
Campo obrigatório ausente ou com formato errado. details traz os erros por campo.
{
"error": {
"code": "validation_failed",
"message": "The name field is required.",
"details": { "name": ["The name field is required."] }
}
}
422 invalid_ids / invalid_company_channel / invalid_user / invalid_sector / invalid_tag
Algum id não é da empresa ou não é visível para a chave. Nas ações em lote, details.invalid_ids lista quais — e nada é feito.
422 email_taken / channel_key_in_use / contact_has_hidden_channels / group_channel_locked
E-mail ou número já usado por outro contato, contato com canais que o usuário API não vê (não pode ser excluído inteiro), ou canal de grupo, que não é editável.
422 unknown_custom_field / invalid_custom_field_value / tags_require_channel
Campo personalizado inexistente ou valor no formato errado; etiquetas só existem no canal do contato.
422 invalid_whatsapp_number / whatsapp_validation_unavailable / unsupported_channel
O número não tem WhatsApp, não há WhatsApp conectado para validar, ou o canal não aceita importação.
422 user_not_in_sector / invalid_close_reason / survey_requires_whole_channel / export_too_large / nothing_to_export
Erros das ações em lote, da importação e da exportação — veja cada seção.
404 not_found
O contato, canal ou item não existe ou não é visível para a chave.
403 forbidden
A chave não tem a permissão de Contatos necessária, ou o usuário API não tem a permissão na plataforma.
401 unauthenticated
Chave ausente, inválida, revogada ou expirada.