API kontaktów
Kontakt to pojedyncza osoba, do której wysyłasz wiadomości — obejmuje jej imię, numer telefonu, adres e-mail, kanał komunikacji, tagi, pola niestandardowe oraz listy i kampanie, do których należy. API kontaktów umożliwia tworzenie kontaktów, wyszukiwanie ich, aktualizowanie, tagowanie, importowanie masowe oraz usuwanie, a wszystko to bez konieczności korzystania z panelu nawigacyjnego.
Wszystkie ścieżki na tej stronie są względne względem bazowego adresu URL:
https://api.youraiconnector.com/v1
Zatem /contacts oznacza https://api.youraiconnector.com/v1/contacts.
Dopiero zaczynasz pracę z API? Najpierw przeczytaj Dostęp do API — zawiera informacje o tym, jak wygenerować klucz API, trzy sposoby uwierzytelniania, limity zapytań oraz format błędów. Wszystkie informacje na tej stronie zakładają, że masz już działający klucz API.
Informacje o identyfikatorach kontaktów
Każdy kontakt ma unikalny identyfikator (ID). Identyfikator, który otrzymujesz podczas tworzenia kontaktu (w data.contactId), jest tym samym identyfikatorem, którego używasz wszędzie indziej — do pobierania, aktualizowania, tagowania, wysyłania wiadomości lub usuwania tego kontaktu. Zapisz go raz i używaj ponownie.
Nie musisz tworzyć kontaktu, aby uzyskać jego identyfikator. Możesz go również wyszukać według numeru telefonu lub adresu e-mail (zobacz Pobierz kontakt) lub przeglądać wszystkie swoje kontakty (zobacz Lista kontaktów). Każda z tych metod zwraca ten sam identyfikator.
Tworzenie kontaktu
POST /contacts
Dodaje nowy kontakt do Twojego konta. Wymagany jest numer telefonu z kodem kraju — sam adres e-mail nie wystarczy. Wszystkie pozostałe pola są opcjonalne.
Możesz opcjonalnie dodać nowy kontakt bezpośrednio do jednej lub wielu list za pomocą listId (pojedyncza lista) lub listIds (tablica). Jeśli wysłane zostaną oba, listIds ma pierwszeństwo.
Każde pole, które wyślesz, a które nie jest jednym ze standardowych pól tworzenia wymienionych w poniższej tabeli pól Tworzenie kontaktu (phoneNumber, firstName, lastName, email, channel, is_bot_active, is_private, lead_profile, listId, listIds, custom_fields), jest automatycznie zapisywane jako pole niestandardowe — dzięki czemu płaska struktura danych z narzędzi takich jak Make czy Zapier działa bez konieczności zagnieżdżania. Możesz również przekazać jawny obiekt custom_fields.
| Pole | Wymagane | Opis |
|---|---|---|
phoneNumber |
Tak | Numer telefonu kontaktu wraz z kodem kraju (np. +15551234567). |
firstName |
Nie | Imię. |
lastName |
Nie | Nazwisko. |
email |
Nie | Adres e-mail. |
channel |
Nie | Kanał komunikacji. Jedna z wartości: whatsapp, sms, whatsapp_web. Domyślnie whatsapp. |
is_bot_active |
Nie | Czy asystent AI ma odpowiadać temu kontaktowi. Domyślnie true. |
is_private |
Nie | Oznacz kontakt jako prywatny. Gdy true, asystent AI jest dla niego wyłączony. Domyślnie false. |
lead_profile |
Nie | Notatki tekstowe dotyczące leada. |
listId |
Nie | Pojedynczy identyfikator listy, do której ma zostać dodany kontakt. |
listIds |
Nie | Tablica identyfikatorów list, do których ma zostać dodany kontakt (ma pierwszeństwo przed listId). |
custom_fields |
Nie | Obiekt zawierający własne pola klucz/wartość. Możesz je również przekazać jako klucze najwyższego poziomu. Pola z określonym typem (data, liczba, lista rozwijana, pole wyboru) są walidowane — zobacz Typy pól niestandardowych. |
Powszechne alternatywne nazwy są akceptowane, gdy brakuje nazwy standardowej: phone lub phone_number dla numeru, first_name oraz last_name dla nazwy, a także full_name lub name, która jest podzielona na imię i nazwisko. Błąd Missing required fields: phoneNumber oznacza zatem, że nie dotarła żadna wartość telefonu, a nie że klucz został zapisany inaczej.
cURL
curl -X POST "https://api.youraiconnector.com/v1/contacts?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phoneNumber": "+15551234567",
"firstName": "Jane",
"lastName": "Smith",
"email": "jane@example.com",
"is_bot_active": true,
"listIds": ["list123", "list456"]
}'
JavaScript
const res = await fetch("https://api.youraiconnector.com/v1/contacts", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
phoneNumber: "+15551234567",
firstName: "Jane",
lastName: "Smith",
email: "jane@example.com",
is_bot_active: true,
listIds: ["list123", "list456"],
}),
});
const data = await res.json();
console.log(data.data.contactId);
Python
import requests
res = requests.post(
"https://api.youraiconnector.com/v1/contacts",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"phoneNumber": "+15551234567",
"firstName": "Jane",
"lastName": "Smith",
"email": "jane@example.com",
"is_bot_active": True,
"listIds": ["list123", "list456"],
},
)
print(res.json()["data"]["contactId"])
Odpowiedź
{
"success": true,
"data": {
"message": "Successfully created new contact",
"contactId": "contact_abc123",
"listsAdded": ["list123", "list456"]
}
}
Identyfikator nowego kontaktu znajduje się w data.contactId. Listy, do których został dodany, są zwracane w data.listsAdded.
Duplikaty nie są tworzone. Jeśli kontakt o tym samym numerze telefonu już istnieje w kanale, w którym go tworzysz, wywołanie tworzenia nie utworzy drugiego. Odpowiedź zwraca status HTTP
200orazerror_codeo wartości409w treści, więc należy rozgałęziać logikę na podstawieerror_code, a nie statusu HTTP. Identyfikator istniejącego kontaktu jest zwracany jakocontactId(orazdata.contactId), dzięki czemu możesz od razu kontynuować pracę z nim:{ "success": false, "error_code": 409, "error": "A contact with this phone number already exists for the current user.", "contactId": "abc123", "data": { "contactId": "abc123" } }To samo dotyczy zduplikowanego adresu e-mail w kanale Email. Jeśli Twoje stanowisko w zespole widzi tylko własne kontakty, a istniejący kontakt do nich nie należy, identyfikator jest pomijany; w takim przypadku wyszukaj go za pomocą Pobierz kontakt według numeru telefonu lub adresu e-mail.
Równoważne zapisy numerów WhatsApp są traktowane jako ten sam numer. Niektóre kraje mają dwa poprawne sposoby zapisu tego samego numeru telefonu komórkowego, a WhatsApp może zgłaszać dowolny z nich: Meksyk (
+52…i starszy+521…), Brazylia (z dziewiątą cyfrą lub bez) oraz Argentyna (z9po+54lub bez). Sprawdzanie duplikatów podczas tworzenia iGET /contacts?phoneNumber=działa dla obu zapisów, więc otrzymasz istniejący kontakt niezależnie od tego, w jakiej formie go wyślesz.phone_numberzapisany w kontakcie nigdy nie jest nadpisywany.
Pobierz kontakt według numeru telefonu lub adresu e-mail
GET /contacts?phoneNumber=... lub GET /contacts?email=...
Wyszukuje pojedynczy kontakt i zwraca pełny, wzbogacony obiekt kontaktu — w tym jego listy, tagi i kampanie rozwiązane do par { id, name }, a także ostatnią wymienioną wiadomość.
Przekaż albo phoneNumber (w formacie międzynarodowym), albo email. Jeśli nie przekażesz żadnego z nich, ten sam punkt końcowy przełączy się w tryb Listowania kontaktów.
cURL
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts?phoneNumber=%2B15551234567&apiKey=YOUR_API_KEY"
JavaScript
const phone = encodeURIComponent("+15551234567");
const res = await fetch(`https://api.youraiconnector.com/v1/contacts?phoneNumber=${phone}`, {
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.contact);
Python
import requests
res = requests.get(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts",
headers={"X-API-Key": "YOUR_API_KEY"},
params={"phoneNumber": "+15551234567"},
)
print(res.json()["contact"])
Odpowiedź
{
"success": true,
"contactId": "contact_abc123",
"contact": {
"id": "contact_abc123",
"firstName": "Jane",
"lastName": "Smith",
"email": "jane@example.com",
"phoneNumber": "+15551234567",
"channel": "whatsapp",
"isBotActive": true,
"isPrivate": false,
"doNotDisturb": false,
"lead_profile": null,
"avatarUrl": "https://example.com/photo.jpg",
"customFields": {},
"lists": [{ "id": "list123", "name": "VIP customers" }],
"tags": [{ "id": "tagHotLead", "name": "Hot lead" }],
"campaigns": [{ "id": "campaign789", "name": "Spring promo" }],
"currentCampaign": { "id": "campaign789", "name": "Spring promo" },
"lastMessage": {
"direction": "inbound",
"body": "Sounds good, thanks!",
"status": "received",
"timestamp": "2026-06-09T10:21:00.000Z"
},
"adAttribution": {
"visit_at": "2026-06-09T10:14:02.000Z",
"matched_at": "2026-06-09T10:14:20.000Z",
"match_method": "reference",
"reference": "4827",
"fbc": "fb.1.1749463000000.IwAR...",
"fbp": "fb.1.1749000000000.123456789",
"fbclid": "IwAR...",
"gclid": null,
"gbraid": null,
"wbraid": null,
"utm_source": "facebook",
"utm_medium": "paid",
"utm_campaign": "spring-promo",
"utm_content": null,
"utm_term": null,
"landing_url": "https://yoursite.com/quiz?utm_source=facebook&utm_campaign=spring-promo",
"referrer": "https://l.facebook.com/",
"client_ip": "203.0.113.7",
"user_agent": "Mozilla/5.0 (iPhone; ...)",
"site_key": "YOUR_SITE_KEY",
"visit_id": "visit_abc123"
}
}
}
adAttribution to kliknięcie w reklamę, które doprowadziło do Twojej witryny, dopasowane do tego kontaktu przez skrypt kliknięcia w reklamę, lub null, gdy go nie ma. Pola zostały wyjaśnione w Atrybucja kliknięć w reklamy dla WhatsApp.
Identyfikator kontaktu jest zwracany zarówno na najwyższym poziomie (contactId), jak i wewnątrz obiektu (contact.id). Jeśli nic nie pasuje, otrzymasz 404 z { "success": false, "message": "Contact not found" }.
avatarUrlto zdjęcie profilowe kontaktu, pobrane z WhatsApp lub Meta, gdy użytkownik wyśle do Ciebie wiadomość. Jest ono tylko do odczytu: nie możesz go ustawić i jest ononulldla kontaktów, które nie mają zdjęcia lub kontaktują się przez kanał, który go nie udostępnia. Traktuj ten link jako tymczasowy, zamiast go przechowywać, ponieważ niektóre z tych linków do zdjęć wygasają i są odświeżane automatycznie. (W poniższym punkcie końcowym listy ta sama wartość nazywa sięavatar_url.)
Numery telefonów w adresach URL. Znak
+w ciągu zapytania musi być zakodowany jako%2B, w przeciwnym razie zostanie odczytany jako spacja. Powyższe przykłady robią to za Ciebie.
Kontakty z ukrytym numerem
Kontakt w WhatsApp Web może pojawić się bez numeru telefonu, gdy WhatsApp ukrywa go przed powiązanym urządzeniem (zobacz Kontakty z ukrytym numerem). Każdy odczytany kontakt zawiera dwa dodatkowe pola w tym celu: number_hidden (numberHidden w kształcie pojedynczego kontaktu) oraz whatsapp_lid (whatsappLid).
- W przypadku kontaktu z ukrytym numerem,
phone_numberma wartośćnull,number_hiddenma wartośćtrue, awhatsapp_lidprzechowuje wewnętrzny identyfikator WhatsApp w formacie153055538446346@lid. Nie jest to numer, pod który można zadzwonić. Dopasuj kontakt na podstawie tego identyfikatora w swoim systemie i przekaż go jakophoneNumberdo tego wyszukiwania, aby ponownie znaleźć kontakt. - W przypadku każdego innego kontaktu,
number_hiddenma wartośćfalse, awhatsapp_lidto identyfikator zgłoszony przez WhatsApp dla tego kontaktu lubnull.
Wychodzące webhooki i ładunki automatyzacji zawierające kontakt przenoszą te same trzy pola, a eksport kontaktów do pliku CSV zawiera kolumny „Number Hidden” (Ukryty numer) oraz „WhatsApp LID” obok „Phone Number” (Numer telefonu). Gdy WhatsApp udostępni rzeczywisty numer, kontakt zaktualizuje się automatycznie, a pole phone_number zostanie wypełnione.
Pobierz kontakt według identyfikatora
GET /contacts/{contactId}
Jeśli masz już identyfikator kontaktu, pobierz go bezpośrednio. Struktura odpowiedzi jest identyczna jak w przypadku wyszukiwania powyżej.
cURL
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123?apiKey=YOUR_API_KEY"
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123", {
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.contact);
Python
import requests
res = requests.get(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123",
headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["contact"])
Identyfikator kontaktu, który nie istnieje na Twoim koncie, zwraca 404.
Pobierz statystyki kontaktu
GET /contacts/{contactId}/stats
Zwraca zagregowane statystyki wiadomości dla jednego kontaktu: sumy, odpowiedzi AI kontra ludzkie, zużyte kredyty oraz znaczniki czasu pierwszej i ostatniej wiadomości.
cURL
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/stats?apiKey=YOUR_API_KEY"
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/stats", {
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.totalMessages, data.creditsUsed);
Python
import requests
res = requests.get(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/stats",
headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
print(data["totalMessages"], data["creditsUsed"])
Odpowiedź
{
"success": true,
"totalMessages": 48,
"sent": 21,
"received": 27,
"aiReplies": 18,
"humanReplies": 3,
"creditsUsed": 34,
"botMessageCount": 18,
"firstMessageAt": "2026-05-01T09:00:00.000Z",
"lastMessageAt": "2026-06-09T10:21:00.000Z"
}
botMessageCount to ten sam licznik wiadomości AI, który zeruje przycisk „reset” w aplikacji dla danego kontaktu. creditsUsed to bieżąca suma kredytów dla tego kontaktu, a nie tylko liczby z tej odpowiedzi. Identyfikator kontaktu, który nie istnieje na Twoim koncie, zwraca 404.
Pobierz koszt kontaktu
GET /contacts/{contactId}/cost
Zwraca liczbę kredytów zużytych przez kontakt, z podziałem na konwersacje (sesje czatu), rodzaje działań oraz średni koszt konwersacji obsługiwanej wyłącznie przez AI w porównaniu z taką, w którą zaangażował się Twój zespół. Użyj tego, aby pokazać klientowi, ile kosztuje konwersacja.
cURL
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/cost?apiKey=YOUR_API_KEY"
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/cost", {
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.total_credits, data.by_session.length);
Python
import requests
res = requests.get(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/cost",
headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
print(data["total_credits"], data["summary"]["contained"]["avg_credits"])
Odpowiedź
{
"success": true,
"contact_id": "contact_abc123",
"total_credits": 23,
"refunded_credits": 0,
"unattributed_credits": 0,
"by_session": [
{
"session_id": "hpWKxuTSXIKlylze5Ulx",
"start": "2026-09-21T00:00:00.000Z",
"end": "2026-09-24T17:41:52.052Z",
"status": "ChatSessionClosed",
"tag": null,
"credits": 7,
"actions": 7,
"outcome": "handed_off"
},
{
"session_id": "5kKnkI3DD4THIh2YVjey",
"start": "2026-09-09T00:00:00.000Z",
"end": "2026-09-11T22:42:34.056Z",
"status": "ChatSessionClosed",
"tag": null,
"credits": 13,
"actions": 14,
"outcome": "contained"
}
],
"by_type": [
{ "type": "AI Chat Conclude Evaluation", "credits": 8, "actions": 8 },
{ "type": "AI Tool Use", "credits": 4, "actions": 4 },
{ "type": "AI Message", "credits": 2, "actions": 2 }
],
"summary": {
"contained": { "sessions": 1, "credits": 13, "avg_credits": 13 },
"handed_off": { "sessions": 1, "credits": 7, "avg_credits": 7 },
"no_ai": { "sessions": 0, "credits": 0, "avg_credits": 0 }
},
"sessions_truncated": false
}
Uwagi terenowe:
total_creditszlicza każdą opłatę przypisaną do kontaktu (odpowiedzi AI, wywołania narzędzi, tagi, działania następcze, podsumowania, kroki oceny w tle, opłaty za szablony i dostarczenie) po odjęciu zwrotów kosztów dla tego kontaktu. Przyznane kredyty, doładowania i odnowienia nigdy nie są przypisywane do kontaktu. Testy w środowisku Playground są pomijane.- Opłata trafia do konwersacji, w której oknie czasowym się mieści. Okno trwa od rozpoczęcia konwersacji do jej zamknięcia lub rozpoczęcia kolejnej, dzięki czemu żadna opłata nie jest liczona dwukrotnie. Opłaty spoza każdego okna znajdują się w
unattributed_credits(oraz w sumie całkowitej). outcome:containedoznacza, że AI odpowiedziało i nikt z Twojego zespołu nie musiał interweniować;handed_offoznacza, że ktoś wysłał wiadomość po odpowiedzi AI lub AI zaalarmowało człowieka;no_aioznacza, że AI nigdy nie odpowiedziało w danej konwersacji.- Wyświetlanych jest do 200 konwersacji, od najnowszej;
sessions_truncatedtotrue, gdy jest ich więcej. - W przypadku klienta agencji przekaż
sub_account_id; liczby zostaną podane w kredytach tego klienta. creditsUsedw Pobierz statystyki kontaktu to bieżący licznik utrzymywany dla kontaktu. Ten punkt końcowy przelicza wartość na podstawie rejestru kredytów, więc obie wartości mogą się nieco różnić, głównie w przypadku starszych kontaktów.- Członkowie zespołu potrzebują uprawnień do rozliczeń. Kontakt, którego nie ma na Twoim koncie, zwróci
404;503oznacza, że obliczenie wartości nie jest obecnie możliwe, spróbuj ponownie za chwilę.
Listowanie kontaktów
GET /contacts
Wywołaj GET /contacts bez phoneNumber ani email, aby przeglądać wszystkie kontakty, zaczynając od najnowszych. Każda strona zwraca skrócone podsumowania kontaktów (listy, tagi i kampanie są zwracane jako tablice identyfikatorów, a nie pełne obiekty) oraz next_cursor.
| Parametr zapytania | Opis |
|---|---|
limit |
Rozmiar strony. Domyślnie 50, maksymalnie 100. |
cursor |
Wartość next_cursor z poprzedniej strony. Pomiń ją na pierwszej stronie. |
listId |
Opcjonalne. Zwróć tylko kontakty należące do tej listy. |
Aby przejść przez wszystkie strony: wykonaj pierwsze wywołanie bez kursora, a następnie przekazuj zwrócony next_cursor jako cursor. Zatrzymaj się, gdy next_cursor będzie null — oznacza to, że nie ma więcej wyników.
cURL
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts?limit=50&apiKey=YOUR_API_KEY"
# next page:
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts?limit=50&cursor=contact_abc123&apiKey=YOUR_API_KEY"
JavaScript
async function listAllContacts() {
const all = [];
let cursor = null;
do {
const url = new URL("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts");
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, { headers: { "X-API-Key": "YOUR_API_KEY" } });
const data = await res.json();
all.push(...data.contacts);
cursor = data.next_cursor;
} while (cursor);
return all;
}
Python
import requests
def list_all_contacts():
all_contacts = []
cursor = None
while True:
params = {"limit": 100}
if cursor:
params["cursor"] = cursor
res = requests.get(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts",
headers={"X-API-Key": "YOUR_API_KEY"},
params=params,
)
data = res.json()
all_contacts.extend(data["contacts"])
cursor = data["next_cursor"]
if not cursor:
break
return all_contacts
Odpowiedź
{
"success": true,
"contacts": [
{
"id": "contact_abc123",
"first_name": "Jane",
"last_name": "Smith",
"email": "jane@example.com",
"phone_number": "+15551234567",
"number_hidden": false,
"whatsapp_lid": null,
"channel": "whatsapp",
"is_bot_active": true,
"is_private": false,
"do_not_disturb": false,
"avatar_url": "https://example.com/photo.jpg",
"custom_fields": {},
"created_at": "2026-06-01T09:00:00.000Z",
"list_ids": ["list123"],
"tag_ids": ["tagHotLead"],
"campaign_ids": ["campaign789"],
"current_campaign_id": "campaign789",
"ad_attribution": null
}
],
"next_cursor": "contact_abc123"
}
Każdy kontakt zawiera ad_attribution: kliknięcie w reklamę, które doprowadziło do Twojej witryny, dopasowane do kontaktu, z tymi samymi polami co adAttribution w Pobierz kontakt według ID, lub null, gdy go nie ma.
Uwaga: Filtrowanie według listId, który nie istnieje na Twoim koncie, zwraca 404. Nieprawidłowy cursor zwraca 400.
Liczba kontaktów
GET /contacts/count
Zwraca liczbę kontaktów pasujących do filtra oraz podział na kanały, bez konieczności stronicowania. Jest to właściwe wywołanie dla każdego pytania typu „ile” — kafelka na pulpicie nawigacyjnym, automatyzacji lub zapytania do Champa. Wszystkie filtry są opcjonalne, a łączenie kilku z nich zawęża wynik (kontakt musi spełniać każdy z nich).
| Parametr zapytania | Opis |
|---|---|
agentId |
Tylko kontakty przypisane do tego agenta AI. Przekaż none dla kontaktów bez przypisanego agenta (są one obsługiwane przez domyślnego agenta kanału). |
channel |
Tylko kontakty na tym kanale, np. whatsapp, messenger, instagram, sms, email, chat_widget. |
tag |
Tylko kontakty z tą etykietą, według nazwy etykiety (wielkość liter nie ma znaczenia). Nazwa etykiety, której nie posiadasz, zwróci 404. |
listId |
Tylko kontakty na tej liście. |
botActive |
true lub false — tylko kontakty, których asystent AI jest włączony lub wyłączony. |
status |
Tylko kontakty o tym statusie, np. Lead. |
rules |
Obiekt reguł JSON zakodowany w formacie URL, używający tego samego kształtu co lista inteligentna (zobacz Kształt smart_rules poniżej). Nie można łączyć z innymi filtrami. |
Jeśli nie wyślesz żadnego filtra, otrzymasz całkowitą liczbę kontaktów na swoim koncie.
cURL
# everything
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/count?apiKey=YOUR_API_KEY"
# only the contacts one agent handles on Messenger
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/count?agentId=agent_xyz789&channel=messenger&apiKey=YOUR_API_KEY"
JavaScript
const url = new URL("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/count");
url.searchParams.set("agentId", "agent_xyz789");
url.searchParams.set("channel", "messenger");
const res = await fetch(url, { headers: { "X-API-Key": "YOUR_API_KEY" } });
const data = await res.json();
console.log(data.total);
Python
import requests
res = requests.get(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/count",
headers={"X-API-Key": "YOUR_API_KEY"},
params={"agentId": "agent_xyz789", "channel": "messenger"},
)
data = res.json()
print(data["total"])
Odpowiedź
{
"success": true,
"total": 3423,
"by_channel": { "messenger": 2744, "instagram": 667, "none": 12 },
"filters": { "agentId": "agent_xyz789" }
}
by_channel dzieli tę samą sumę według kanałów; kontakty, które nie znajdują się na żadnym kanale, są liczone w none. filters zwraca zastosowane filtry, dzięki czemu możesz sprawdzić, czy wywołanie zadziałało zgodnie z oczekiwaniami.
Uwaga: Wysłanie rules wraz z jakimkolwiek innym filtrem lub wartością rules, która nie jest poprawnym kodem JSON, zwróci 400. Nazwa etykiety lub identyfikator listy, które nie istnieją na Twoim koncie, zwrócą 404.
Aktualizacja kontaktu
PUT /contacts/{contactId}
Aktualizuje istniejący kontakt. Zmieniane są tylko uwzględnione pola — pomiń wszystko, czego nie chcesz zmieniać. Musisz wysłać co najmniej jedno pole, w przeciwnym razie otrzymasz 400 („Brak pól do aktualizacji”).
| Pole | Opis |
|---|---|
firstName |
Imię. |
lastName |
Nazwisko. |
email |
Adres e-mail. |
is_bot_active |
Czy asystent AI ma odpowiadać temu kontaktowi. |
is_private |
Oznacz jako prywatny. Ustawienie tej wartości na true wyłącza również asystenta AI. |
do_not_disturb |
Wstrzymaj zautomatyzowaną komunikację z tym kontaktem. Powoduje również zatrzymanie odpowiedzi AI. |
follow_ups_disabled |
Zatrzymaj wszystkie zautomatyzowane działania typu follow-up dla tego kontaktu (szybkie, cykliczne i dla zimnych leadów), podczas gdy AI nadal odpowiada na wiadomości od nich. Przydatne po dokonaniu zakupu. Pozostaje wyłączone, dopóki nie ustawisz z powrotem na false. |
lead_profile |
Notatki tekstowe dotyczące leada. |
custom_fields |
Obiekt pól niestandardowych. Scalane według klucza — zapisywane są tylko przesłane klucze, reszta istniejących pól niestandardowych zostaje zachowana. Możesz również przekazać klucze pól niestandardowych na najwyższym poziomie. Pola typowane są walidowane, a błędna wartość odrzuca żądanie z błędem field_errors — zobacz Typy pól niestandardowych. |
cURL
curl -X PUT "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "firstName": "Jane", "do_not_disturb": true }'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123", {
method: "PUT",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ firstName: "Jane", do_not_disturb: true }),
});
const data = await res.json();
console.log(data.message);
Python
import requests
res = requests.put(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"firstName": "Jane", "do_not_disturb": True},
)
print(res.json()["message"])
Odpowiedź
{
"success": true,
"message": "Contact updated successfully"
}
Pola niestandardowe są scalane, a nie zastępowane. Wysłanie
{ "custom_fields": { "tier": "gold" } }ustawia tylkotier— wszelkie inne pola niestandardowe w kontakcie pozostają bez zmian. Aby całkowicie usunąć pole niestandardowe ze wszystkich kontaktów, użyj Usuń pole niestandardowe.
Dodawanie lub usuwanie tagów
POST /contacts/{contactId}/tags
Dodaje i/lub usuwa tagi dla pojedynczego kontaktu w jednym wywołaniu. Przekaż identyfikatory tagów w addTagIds i removeTagIds. Co najmniej jedno z tych pól musi być niepuste.
Tagi muszą już istnieć na Twoim koncie — utwórz je najpierw za pomocą punktu końcowego tagów. Jeśli kontakt lub jakikolwiek powiązany tag nie istnieje, otrzymasz 404.
| Pole | Opis |
|---|---|
addTagIds |
Tablica identyfikatorów tagów do dodania do kontaktu. |
removeTagIds |
Tablica identyfikatorów tagów do usunięcia z kontaktu. |
cURL
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/tags?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "addTagIds": ["tagHotLead"], "removeTagIds": ["tagColdLead"] }'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/tags", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
addTagIds: ["tagHotLead"],
removeTagIds: ["tagColdLead"],
}),
});
const data = await res.json();
console.log(data.added, data.removed);
Python
import requests
res = requests.post(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/tags",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"addTagIds": ["tagHotLead"], "removeTagIds": ["tagColdLead"]},
)
data = res.json()
print(data["added"], data["removed"])
Odpowiedź
{
"success": true,
"contact_id": "contact_abc123",
"added": 1,
"removed": 1
}
Zarządzaj biblioteką tagów
Te punkty końcowe służą do zarządzania samą etykietą — zmiany jej nazwy lub usunięcia z konta — w przeciwieństwie do przypisywania lub usuwania etykiety u jednego kontaktu (zobacz Dodawanie lub usuwanie etykiet powyżej). Każda etykieta na Twoim koncie posiada identyfikator (tagId): ten widoczny w menedżerze etykiet w Twoim panelu oraz ten zwracany jako tag_id na najwyższym poziomie odpowiedzi podczas tworzenia etykiety za pomocą POST /tags i treści JSON { "name": "..." } (bez phoneNumber, email lub contactId).
Aktualizuj tag
PUT /tags/{tagId}
Wyślij tylko te pola, które zmieniasz.
| Pole | Opis |
|---|---|
name |
Nazwa tagu. |
curl -X PUT "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/tags/tagHotLead?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Hot lead (Q3)" }'
Odpowiedź
{ "success": true, "tag_id": "tagHotLead" }
tagId, który nie istnieje na Twoim koncie, zwraca 404.
Usuń tag
DELETE /tags/{tagId}
Usuwa jeden tag według identyfikatora. Tej operacji nie można cofnąć — kontakty posiadające ten tag po prostu go tracą. Usunięcie tagu, którego już nie ma (lub nigdy nie istniał), zwraca 200 z deleted: 0 zamiast 404, ponieważ nie ma czego wyliczać.
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/tags/tagColdLead?apiKey=YOUR_API_KEY"
Odpowiedź
{ "success": true, "deleted": 1 }
Usuń kilka tagów jednocześnie
DELETE /tags
| Pole | Opis |
|---|---|
tagIds |
Tablica identyfikatorów tagów do usunięcia (maks. 1000). |
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/tags?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "tagIds": ["tagColdLead", "tagUnsubscribed"] }'
Odpowiedź
{ "success": true, "deleted": 2 }
Identyfikatory, które nie istnieją lub należą do innego konta, są pomijane bez powiadomienia i nie są wliczane do deleted.
Masowe ustawianie flagi
POST /contacts/bulk-flag
Ustawia jedną flagę logiczną dla wielu kontaktów jednocześnie. Maksymalnie 500 identyfikatorów kontaktów na żądanie. Identyfikatory, które nie istnieją na Twoim koncie, są pomijane i zliczane w skipped.
| Pole | Opis |
|---|---|
contactIds |
Tablica identyfikatorów kontaktów do zaktualizowania (maks. 500). |
field |
Flaga do ustawienia. Jedna z bot_active (asystent AI włączony/wyłączony), dnd (wstrzymaj zautomatyzowany kontakt), spam, private. |
value |
Wartość logiczna, na którą ma zostać ustawiona flaga. |
cURL
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-flag?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contactIds": ["contactId1", "contactId2"],
"field": "bot_active",
"value": false
}'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-flag", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
contactIds: ["contactId1", "contactId2"],
field: "bot_active",
value: false,
}),
});
const data = await res.json();
console.log(data.updated, data.skipped);
Python
import requests
res = requests.post(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-flag",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"contactIds": ["contactId1", "contactId2"],
"field": "bot_active",
"value": False,
},
)
data = res.json()
print(data["updated"], data["skipped"])
Odpowiedź
{
"success": true,
"updated": 2,
"skipped": 0
}
Masowy import kontaktów
POST /contacts/import
Tworzy do 500 kontaktów w jednym wywołaniu z tablicy JSON. Każdy rekord wymaga phone_number w formacie międzynarodowym; wszystko inne jest opcjonalne. Rekordy z nieprawidłowymi numerami telefonów lub nieobsługiwanymi kanałami są pomijane (nie są tworzone), a każdy pominięty rekord jest raportowany wraz z indeksem i powodem — dzięki temu możesz poprawić tylko te, które nie zostały przetworzone, i ponowić próbę.
Numery telefonów, które już istnieją na Twoim koncie, są domyślnie pomijane jako duplicate — jednak podczas wysyłania listId, kontakt, który już posiadasz, jest nadal dodawany do tej listy i liczony w ramach added_to_list, więc ponowny import wypełnia listę bez wprowadzania zmian w samym kontakcie. Wyślij updateExisting: true, aby zaktualizować te kontakty: pola obecne w rekordzie nadpisują dane kontaktu (first_name, last_name, email, lead_profile oraz custom_fields są scalane klucz po kluczu), tags są dodawane, a kontakt jest przypisywany do listId. Kanał, numer telefonu oraz flagi bota nigdy nie są zmieniane w istniejącym kontakcie.
Możesz opcjonalnie dodać każdy zaimportowany (lub zaktualizowany) kontakt do listy za pomocą listId, ustawić defaultChannel dla rekordów, które go nie określają, oraz otagować rekordy za pomocą tags (nazwy tagów — brakujące tagi są tworzone, istniejące są dopasowywane bez uwzględniania wielkości liter).
Pola najwyższego poziomu
| Pole | Wymagane | Opis |
|---|---|---|
contacts |
Tak | Tablica rekordów kontaktów (maks. 500). |
listId |
Nie | Lista, do której mają zostać dodane wszystkie kontakty z tego żądania — zarówno utworzone, zaktualizowane, jak i te pominięte jako duplikaty. Musi to być lista istniejąca na Twoim koncie. |
defaultChannel |
Nie | Kanał przypisany do rekordów, w których pominięto channel. Jedna z wartości: whatsapp, sms, whatsapp_web. Domyślnie whatsapp. |
updateExisting |
Nie | true, aby zaktualizować kontakty, których numer telefonu już istnieje, zamiast pomijać je jako duplicate. Domyślnie false. |
Pola dla poszczególnych rekordów
| Pole | Wymagane | Opis |
|---|---|---|
phone_number |
Tak | Numer telefonu w formacie międzynarodowym (wiodący + jest dodawany, jeśli go brakuje). |
first_name |
Nie | Imię. |
last_name |
Nie | Nazwisko. |
email |
Nie | Adres e-mail. |
channel |
Nie | Jeden z whatsapp, sms, whatsapp_web. W razie braku używa defaultChannel. |
is_bot_active |
Nie | Czy asystent AI odpowiada. Domyślnie true. |
is_private |
Nie | Oznacz jako prywatny. Domyślnie false. |
lead_profile |
Nie | Notatki o leadzie w formie dowolnego tekstu. |
custom_fields |
Nie | Obiekt z kluczami i wartościami pól niestandardowych. |
tags |
Nie | Tablica nazw tagów (działa również pojedynczy ciąg "a; b"). Tagi, które nie istnieją, są tworzone; istniejące są dopasowywane bez uwzględniania wielkości liter. Maks. 25 na rekord. |
cURL
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contacts": [
{ "phone_number": "+12025551234", "first_name": "Ann", "last_name": "Lee", "tags": ["vip", "newsletter"] },
{ "phone_number": "+12025551235", "first_name": "Bob" }
],
"listId": "list123",
"defaultChannel": "whatsapp_web",
"updateExisting": true
}'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
contacts: [
{ phone_number: "+12025551234", first_name: "Ann", last_name: "Lee", tags: ["vip", "newsletter"] },
{ phone_number: "+12025551235", first_name: "Bob" },
],
listId: "list123",
defaultChannel: "whatsapp_web",
updateExisting: true,
}),
});
const data = await res.json();
console.log(`Imported ${data.imported}, updated ${data.updated}, skipped ${data.skipped.length}`);
Python
import requests
res = requests.post(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"contacts": [
{"phone_number": "+12025551234", "first_name": "Ann", "last_name": "Lee", "tags": ["vip", "newsletter"]},
{"phone_number": "+12025551235", "first_name": "Bob"},
],
"listId": "list123",
"defaultChannel": "whatsapp_web",
"updateExisting": True,
},
)
data = res.json()
print(f"Imported {data['imported']}, updated {data['updated']}, skipped {len(data['skipped'])}")
Odpowiedź
{
"success": true,
"imported": 2,
"contact_ids": ["contact_abc123", "contact_def456"],
"updated": 0,
"updated_contact_ids": [],
"added_to_list": 0,
"added_to_list_contact_ids": [],
"skipped": []
}
Jeśli niektóre rekordy nie mogą zostać utworzone, pojawią się w skipped wraz z powodem (tutaj bez updateExisting, więc istniejący numer jest pomijany):
{
"success": true,
"imported": 1,
"contact_ids": ["contact_abc123"],
"updated": 0,
"updated_contact_ids": [],
"added_to_list": 1,
"added_to_list_contact_ids": ["contact_def456"],
"skipped": [
{ "index": 1, "phone_number": "+12025551235", "reason": "duplicate" }
]
}
Pominięty wiersz nadal dołączył do listId, co jest tym, co zlicza added_to_list — żadne inne dane tego kontaktu nie zostały zmienione. Przy użyciu updateExisting: true to samo żądanie raportuje istniejący kontakt w ramach updated / updated_contact_ids.
Możliwe przyczyny pominięcia: invalid_record, missing_phone_number, invalid_phone_number, invalid_channel, duplicate_in_request, duplicate, contact_limit_reached, create_failed.
Limity planu. Jeśli limit kontaktów w Twoim planie nie pozwala na dodanie tak dużej liczby nowych kontaktów, całe żądanie zostanie odrzucone na wstępie z kodem
403. Jeśli limit zostanie osiągnięty w trakcie przetwarzania, pozostałe rekordy zostaną zwrócone jako pominięte z przyczynącontact_limit_reached.
Importowanie kontaktów z pliku CSV
W przypadku importów większych niż obsługuje import masowy (do około 50 000 wierszy), należy dodać asynchroniczne zadanie importu dla pliku CSV znajdującego się już w pamięci masowej konta, a następnie odpytywać o jego status do momentu zakończenia.
Rozpoczęcie importu
POST /contacts/import-csv
| Pole | Wymagane | Opis |
|---|---|---|
csvStoragePath |
Tak | Ścieżka w pamięci masowej do pliku CSV, w ramach users/{your account id}/imports/, kończąca się na .csv. |
listName |
Tak | Tworzy (lub ponownie wykorzystuje) listę o tej nazwie i dodaje do niej każdy zaimportowany kontakt. |
existingListRefs |
Nie | Tablica identyfikatorów istniejących list, do których również należy dodać każdy zaimportowany kontakt. |
defaultChannel |
Nie | Kanał przypisany do wierszy, które go nie określają. |
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import-csv?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"csvStoragePath": "users/abc123/imports/leads.csv",
"listName": "Webinar signups"
}'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import-csv", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
csvStoragePath: "users/abc123/imports/leads.csv",
listName: "Webinar signups",
}),
});
const data = await res.json();
console.log(data.job_id);
Python
import requests
res = requests.post(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import-csv",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"csvStoragePath": "users/abc123/imports/leads.csv",
"listName": "Webinar signups",
},
)
job_id = res.json()["job_id"]
Odpowiedź (202 — import został dodany do kolejki, jeszcze nie zakończony)
{
"success": true,
"job_id": "csvimp_abc123",
"status": "queued"
}
Umieszczanie pliku w pamięci masowej. Ten punkt końcowy uruchamia i śledzi zadanie importu; sam w sobie nie przyjmuje przesyłanego pliku. Plik CSV musi już znajdować się w
csvStoragePathprzed wywołaniem tego punktu — własny importer CSV w panelu nawigacyjnym wykonuje to jako pierwszy krok.
Odpytywanie zadania importu
GET /contacts/import-csv/{jobId}
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import-csv/csvimp_abc123?apiKey=YOUR_API_KEY"
Odpowiedź
{
"success": true,
"job_id": "csvimp_abc123",
"status": "completed",
"imported": 812,
"updated": 0,
"skipped": 14,
"errors": [],
"error_message": null
}
status przechodzi przez etapy queued → processing → completed lub failed z podaniem przyczyny w error_message. jobId, który nie istnieje na Twoim koncie, zwraca 404.
Eksportowanie kontaktów
Uruchamia asynchroniczny eksport kontaktów do pliku CSV i zwraca zadanie, którego status należy sprawdzać w celu potwierdzenia zakończenia.
Rozpocznij eksport
POST /contacts/export
| Pole | Wymagane | Opis |
|---|---|---|
listId |
Nie | Eksportuj tylko kontakty należące do tej listy. |
contactIds |
Nie | Eksportuj tylko te konkretne identyfikatory kontaktów. |
Pozostawienie obu pól pustych spowoduje wyeksportowanie wszystkich kontaktów na Twoim koncie.
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/export?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "listId": "list123" }'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/export", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ listId: "list123" }),
});
const data = await res.json();
console.log(data.job_id);
Python
import requests
res = requests.post(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/export",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"listId": "list123"},
)
job_id = res.json()["job_id"]
Odpowiedź (202 — eksport został dodany do kolejki)
{
"success": true,
"job_id": "export_abc123",
"status": "queued"
}
Sprawdź status zadania eksportu
GET /contacts/export/{jobId}
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/export/export_abc123?apiKey=YOUR_API_KEY"
Odpowiedź
{
"success": true,
"job_id": "export_abc123",
"status": "completed",
"export_id": "exp_xyz789",
"contact_count": 812,
"error_message": null
}
Gdy
statusbędzie"completed", otrzymaszexport_idorazcontact_count. Pobraniu wygenerowanego pliku CSV odbywa się ze strony Eksporty w Twoim panelu nawigacyjnym.
Wyślij wiadomość do kontaktu
POST /contacts/{contactId}/send-message
Wysyła wiadomość do istniejącego kontaktu za pośrednictwem kanału, z którego już korzysta. Wiadomość jest kolejkowana i dostarczana w tle — odpowiedź potwierdza jedynie przyjęcie żądania, a nie fakt dostarczenia wiadomości.
| Pole | Wymagane | Opis |
|---|---|---|
body |
Tak | Treść wysyłanej wiadomości. |
mediaUrl |
Nie | URL pliku multimedialnego do załączenia. |
mediaContentType |
Nie | Typ MIME załączonych mediów (np. image/jpeg). |
cURL
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/send-message?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "body": "Hi! Your appointment is confirmed." }'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/send-message", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ body: "Hi! Your appointment is confirmed." }),
});
const data = await res.json();
console.log(data.messageId);
Python
import requests
res = requests.post(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/send-message",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"body": "Hi! Your appointment is confirmed."},
)
print(res.json()["messageId"])
Odpowiedź
{
"success": true,
"messageId": "aB3dE5fG7hI9jK1lM2nO",
"contactId": "contact_abc123",
"channel": "whatsapp",
"message": "Message created successfully. Delivery is being processed."
}
Nie można teraz wysłać? Jeśli kontakt ma włączony tryb „nie przeszkadzać” lub tryb prywatny, albo nie korzysta z kanału, który może odbierać wiadomości wychodzące, żądanie zostanie odrzucone z kodem
422oraz wyjaśnieniem w poluerror.
Aby wysłać wiadomość za pomocą numeru telefonu, identyfikatora Instagram lub innego identyfikatora kanału zamiast identyfikatora kontaktu — oraz aby dowiedzieć się więcej o przesyłaniu wiadomości — zobacz Messages API.
Wyślij wiadomość uzupełniającą AI do kontaktu
POST /contacts/{contactId}/ai-follow-up
Sprawia, że agent AI pisze jedną wiadomość własnym głosem, na podstawie dotychczasowej konwersacji oraz podanej przez Ciebie instrukcji, a następnie wysyła ją na kanale kontaktu. Działa to tak samo jak krok automatyzacji Wyślij wiadomość uzupełniającą AI i kosztuje tyle samo kredytów co wiadomość uzupełniająca AI.
| Pole | Wymagane | Opis |
|---|---|---|
instruction |
Tak | Co wiadomość powinna zrobić, opisane prostymi słowami, do 2000 znaków. Na przykład: „Przypomnij im, że ich zarezerwowany link wygasa dziś wieczorem i zapytaj, czy chcą go zatwierdzić”. |
agentId |
Nie | Agent, który napisze wiadomość. Pozostaw puste, aby użyć agenta, który już obsługuje dany kontakt. |
mustContain |
Nie | Lista maksymalnie 5 fraz lub linków (każdy do 500 znaków), które wiadomość musi zawierać słowo w słowo. Jeśli AI pominie którykolwiek z nich, wiadomość nie zostanie wysłana, a skipReason będzie miało wartość text_check_failed. |
respectMessagingWindow |
Nie | Domyślnie true. W przypadku WhatsApp Business API, Instagrama i Messengera, pomiń, jeśli ostatnia wiadomość od kontaktu została wysłana ponad 24 godziny temu. |
skipWhenAiPaused |
Nie | Domyślnie true. Pomiń, jeśli AI jest wyłączone dla tego kontaktu. |
cURL
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/ai-follow-up" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"instruction": "Remind them their reserved link expires tonight and ask if they want to lock it in",
"mustContain": ["https://example.com/r/abc123"]
}'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/ai-follow-up", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
instruction: "Remind them their reserved link expires tonight and ask if they want to lock it in",
mustContain: ["https://example.com/r/abc123"],
}),
});
const data = await res.json();
if (!data.sent) console.log("Skipped:", data.skipReason);
Odpowiedź po wysłaniu wiadomości:
{
"sent": true,
"messageId": "ai-follow-up-aB3dE5fG7hI9jK1l",
"text": "Hi Ada, quick reminder: your reserved spot is held until 20:00 tonight. Want to lock it in? https://example.com/r/abc123",
"agentId": "agent_abc123"
}
Odpowiedź po pominięciu:
{
"sent": false,
"skipReason": "window_closed"
}
Oba zwracają status 200. skipReason to jeden z no_agent (brak agenta obsługującego kontakt i nie podano żadnego), dnd (nie przeszkadzać), ai_paused, window_closed (poza 24-godzinnym oknem lub kontakt nigdy nie napisał do Ciebie na tym kanale), no_credits, text_check_failed (brakował elementu mustContain), no_text (AI nie napisało nic użytecznego) lub linkedin_gap (lead z LinkedIn otrzymał automatyczną wiadomość mniej niż 24 godziny temu; odpowiedź zawiera wtedy również nextAllowedAt, czas, kiedy może zostać wysłana kolejna). Pominięcie jest normalną odpowiedzią, a nie błędem, więc sprawdź sent, zanim założysz, że wiadomość została wysłana. Pominięte wywołanie nie zużywa kredytów.
Treść, w której brakuje instruction lub która zawiera pole o niewłaściwym typie, otrzyma 400. Kontakt lub agent, którego nie ma na Twoim koncie, otrzyma 404. Błędy są zwracane jako { "success": false, "error": "..." }. Klucz członka zespołu wymaga uprawnień do edycji kontaktów.
Przypisz agenta AI do kontaktu
POST /contacts/{contactId}/assign-agent
Przenosi istniejącą konwersację do innego agenta AI, począwszy od następnej wiadomości. Jest to to samo, co Przypisz agenta AI w menu czatu oraz ten sam krok, którego używa akcja Przypisz agenta AI w automatyzacjach.
| Pole | Wymagane | Opis |
|---|---|---|
agentId |
Tak | Identyfikator agenta AI, który powinien przejąć konwersację, lub null, aby wyczyścić przypisanie, dzięki czemu konwersacja wróci do skrzynki odbiorczej Twojego zespołu. |
triggerAIResponse |
Nie | true sprawia, że nowo przypisany agent natychmiast odpowiada na ostatnie nieodebrane wiadomości kontaktu. Wartością domyślną jest false. Jeśli kontakt był już przypisany do tego agenta, a jego ostatnia wiadomość została już odebrana, odpowiedź nie jest inicjowana: odpowiedź zawiera aiResponseTriggered: false oraz aiTriggerError z wyjaśnieniem przyczyny. |
Zachowaj ostrożność przy
triggerAIResponse: true— wysyła on wiadomość do kontaktu natychmiast, więc używaj go tylko wtedy, gdy chcesz, aby wiadomość została dostarczona teraz. W przypadku Messengera i Instagrama wiadomość nie zostanie wysłana, jeśli kontakt ostatnio napisał do Ciebie ponad 24 godziny temu.
cURL
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/assign-agent?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "agentId": "agent_xyz789" }'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/assign-agent", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ agentId: "agent_xyz789" }),
});
const data = await res.json();
console.log(data.data.agentId);
Python
import requests
res = requests.post(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/assign-agent",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"agentId": "agent_xyz789"},
)
print(res.json()["data"]["agentId"])
Odpowiedź
{
"success": true,
"data": {
"contactId": "contact_abc123",
"agentId": "agent_xyz789",
"aiResponseTriggered": false
}
}
Agent musi należeć do tego samego konta co kontakt; w przeciwnym razie żądanie zostanie odrzucone z błędem
404lub403. Identyfikatory agentów można znaleźć na stronie Agentów AI (adres URL każdego agenta kończy się jego identyfikatorem).
Przypisz agenta AI do wielu kontaktów
POST /contacts/bulk-assign-agent
Przenosi wiele konwersacji do innego agenta AI w jednym wywołaniu — lub usuwa przypisanie dla nich wszystkich za pomocą null. Jest to czysta zmiana routingu: żadna wiadomość nie jest wysyłana, a agent nikomu nie odpowiada. Każdy kontakt po prostu otrzymuje nowego agenta, gdy napisze następnym razem. (Dlatego nie ma tutaj triggerAIResponse).
| Pole | Wymagane | Opis |
|---|---|---|
agentId |
Tak | Agent AI, który powinien przejąć obsługę, lub null, aby usunąć przypisanie. |
contactIds |
Jedno z trzech | Do 500 identyfikatorów kontaktów do przeniesienia. |
filter |
Jedno z trzech | Wybierz kontakty na serwerze zamiast ich wypisywania, od najnowszych. Przyjmuje te same klucze co filtry punktu końcowego liczby: agentId (lub none), channel, tag, listId, botActive, status. |
rules |
Jedno z trzech | Obiekt reguł listy inteligentnej — zobacz Kształt smart_rules. |
limit |
Nie | Ile kontaktów przenieść w tym wywołaniu przy wyborze za pomocą filter lub rules. Od 1 do 500, domyślnie 500. |
Wyślij dokładnie jeden z parametrów contactIds, filter lub rules.
cURL
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-assign-agent?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "agent_xyz789",
"filter": { "agentId": "agent_abc123", "channel": "messenger" }
}'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-assign-agent", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
agentId: "agent_xyz789",
filter: { agentId: "agent_abc123", channel: "messenger" },
}),
});
const data = await res.json();
console.log(data.updated, data.remaining);
Python
import requests
res = requests.post(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-assign-agent",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"agentId": "agent_xyz789",
"filter": {"agentId": "agent_abc123", "channel": "messenger"},
},
)
data = res.json()
print(data["updated"], data["remaining"])
Odpowiedź
{
"success": true,
"agentId": "agent_xyz789",
"matched": 3415,
"updated": 500,
"skipped": 0,
"remaining": 2915,
"filters": { "agentId": "agent_abc123" }
}
matched to liczba kontaktów znalezionych w sumie, updated to liczba kontaktów przeniesionych tym wywołaniem, skipped to liczba przesłanych identyfikatorów, których nie znaleziono na koncie, a remaining to liczba kontaktów, które nadal pasują do kryteriów po zakończeniu wywołania.
Przenoszenie wszystkich. Ponieważ jedno wywołanie przenosi maksymalnie 500 kontaktów, duża grupa wymaga kilku wywołań. Użyj filtra, który przestaje pasować do kontaktu po jego przeniesieniu — na przykład filter: { "agentId": "agent_abc123" } podczas przypisywania do agent_xyz789 — i powtarzaj to samo wywołanie, aż remaining zwróci 0. Gdy zamiast tego przekażesz contactIds, remaining zawsze wynosi 0.
Przypisz kontakt do działu
POST /contacts/{contactId}/department
„Przypisz ten lead do działu sprzedaży” — przypisuje kontakt do nazwanego działu i domyślnie przekazuje go osobie w tym dziale, która ma obecnie najmniej kontaktów. Jest to oddzielne działanie od przypisywania agenta AI: dział odpowiada na pytanie „który zespół jest właścicielem tego kontaktu”, agent odpowiada na pytanie „który agent AI odpowiada za ten kontakt”, a ustawienie jednego nigdy nie usuwa drugiego.
| Pole | Wymagane | Opis |
|---|---|---|
department_id |
Tak | Dział, do którego ma zostać przypisany kontakt. Przekaż null, aby wyczyścić to pole. |
hand_to_member |
Nie | Dodatkowo przekaż kontakt osobie z najmniejszą liczbą zadań w tym dziale. Wartość domyślna to true. Nigdy nie zmienia przypisania kontaktu, który ma już właściciela. |
cURL
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/department?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "department_id": "dept_sales" }'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/department", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ department_id: "dept_sales" }),
});
const data = await res.json();
console.log(data.assigned_to);
Python
import requests
res = requests.post(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/department",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"department_id": "dept_sales"},
)
print(res.json()["assigned_to"])
Odpowiedź
{
"success": true,
"department_id": "dept_sales",
"assigned_to": "member_uid_123"
}
assigned_to jest null, gdy kontakt miał już właściciela lub przekazano hand_to_member: false.
Połącz kontakt między kanałami
„Kontynuuj na WhatsApp” (lub SMS) znajduje lub tworzy kontakt tej osoby w innym kanale opartym na numerze telefonu i łączy je ze sobą, dzięki czemu reszta aplikacji rozpoznaje je jako tę samą osobę.
Łączenie z innym kanałem
POST /contacts/{contactId}/link-channel
| Pole | Wymagane | Opis |
|---|---|---|
channel |
Tak | Kanał, z którym należy nawiązać połączenie. Jeden z whatsapp, whatsapp_web, sms. |
phoneNumber |
Nie | Numer telefonu używany w nowym kanale. Domyślnie jest to własny numer kontaktu źródłowego. |
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/link-channel?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "channel": "sms" }'
Odpowiedź
{
"success": true,
"data": {
"contact_id": "contact_def456",
"person_id": "person_xyz789",
"created": true
}
}
created informuje, czy dla kanału docelowego utworzono nowy kontakt, czy znaleziono i połączono istniejący. Ponowne wywołanie tej metody jest bezpieczne — zwraca ten sam contact_id z created: false zamiast tworzyć duplikat.
422 oznacza, że konto nie może obecnie wykonać tego połączenia: kontakt znajduje się już w tej rodzinie kanałów, nie ma numeru telefonu do użycia lub nie ma połączonego nadawcy dla kanału docelowego. 409 oznacza, że oba kontakty są już powiązane z dwiema różnymi osobami — najpierw należy rozłączyć jeden z nich.
Wyświetlanie listy połączonych konwersacji kontaktu
GET /contacts/{contactId}/linked
Zwraca pozostałe konwersacje, które należą do tej samej osoby co dany kontakt. Rozłączony kontakt zwraca pustą tablicę, a nie 404 — „ta osoba nie ma innych kanałów” jest normalnym stanem.
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/linked?apiKey=YOUR_API_KEY"
Odpowiedź
{
"success": true,
"data": [
{
"contact_id": "contact_def456",
"channel": "sms",
"custom_channel": null,
"first_name": "Jane",
"last_name": "Smith",
"phone_number": "+15551234567",
"last_message": "Sounds good, thanks!",
"last_message_timestamp": "2026-06-09T10:21:00.000Z",
"linked_from": {
"contact_id": "contact_abc123",
"channel": "whatsapp",
"linked_at": "2026-06-01T09:00:00.000Z",
"reason": "continue_on_channel"
}
}
]
}
Rozłączanie kontaktu
DELETE /contacts/{contactId}/link
Usuwa ten kontakt z jego osoby jednostronnie — wszelkie inne kontakty nadal powiązane z tą osobą zachowują swoje połączenie, więc rozłączenie jednego z trzech nie powoduje rozwiązania grupy.
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/link?apiKey=YOUR_API_KEY"
Odpowiedź
{ "success": true }
Pobieranie zdjęcia profilowego kontaktu
POST /contacts/{contactId}/profile-pic
Pobiera (i buforuje) zdjęcie profilowe kontaktu z WhatsApp lub Meta na żądanie — to samo zdjęcie, które jest zwracane jako avatarUrl w Pobierz kontakt, po odświeżeniu.
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/profile-pic?apiKey=YOUR_API_KEY"
Odpowiedź
{
"success": true,
"avatar_url": "https://example.com/photo.jpg",
"cached": false
}
cached: true oznacza, że adres URL pochodzi z niedawnego pobrania, a nie ze świeżego wyszukiwania u dostawcy — zdjęcia są buforowane przez 7 dni, a kontakt, dla którego dostawca zgłasza brak dostępnego zdjęcia, jest buforowany jako niedostępny przez 24 godziny. Gdy nie ma zdjęcia do pobrania, avatar_url jest pomijane, a message wyjaśnia przyczynę.
Automatyczne tagowanie kontaktów za pomocą AI
Uruchamia reguły tagowania Twojego konta dla pełnej historii konwersacji jednego lub większej liczby kontaktów i stosuje (lub usuwa) tagi dokładnie tak samo, jak tagowanie w czasie rzeczywistym podczas czatu na żywo — te same reguły, ten sam koszt kredytów za tag.
Rozpocznij uruchomienie
POST /contacts/auto-tag
| Pole | Wymagane | Opis |
|---|---|---|
scope |
Tak | "contacts", aby otagować określone kontakty, lub "agent", aby otagować każdą konwersację obsługiwaną obecnie przez jednego agenta AI. |
contact_ids |
Wymagane, gdy scope to "contacts" |
Tablica identyfikatorów kontaktów, od 1 do 500. |
agent_id |
Wymagane, gdy scope to "agent" |
Agent AI, którego konwersacje mają zostać otagowane. Gdy scope to "contacts", jest to opcjonalne i jedynie zawęża zakres reguł tagowania agenta, które zostaną uruchomione. |
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/auto-tag?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "scope": "contacts", "contact_ids": ["contact_abc123", "contact_def456"] }'
Dla pojedynczego kontaktu uruchomienie następuje w trybie inline i zwraca wynik natychmiast:
{ "success": true, "result": { "tags_applied": 2, "tags_removed": 0 } }
Dwa lub więcej kontaktów (lub scope: "agent") uruchamianych jest jako zadanie w tle i zwraca 202 natychmiast:
{ "success": true, "run_id": "m1x2y3-a1b2c3d4", "total": 214 }
Odpytywanie o stan uruchomienia
GET /contacts/auto-tag/run
Zwraca bieżące (lub ostatnie) uruchomienie konta, dzięki czemu możesz odpytywać o postęp bez samodzielnego śledzenia run_id.
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/auto-tag/run?apiKey=YOUR_API_KEY"
Odpowiedź
{
"success": true,
"run": {
"run_id": "m1x2y3-a1b2c3d4",
"status": "running",
"total": 214,
"processed": 58,
"tagged_contacts": 12,
"tags_applied": 15,
"tags_removed": 2,
"credits_charged": 15
}
}
run to null, gdy konto nigdy nie rozpoczęło żadnego uruchomienia. status zmienia się z "running" na "completed" lub "failed".
Na koncie może trwać tylko jedno uruchomienie masowe jednocześnie — rozpoczęcie drugiego, gdy inne jest w toku, zwraca 409 z error_code: "auto_tag_run_in_progress". Brak kredytów przy uruchomieniu dla pojedynczego kontaktu zwraca 402 z error_code: "insufficient_credits"; uruchomienie masowe natomiast zatrzymuje się wcześniej i raportuje postęp w run.
Usuwanie kontaktu
DELETE /contacts/{contactId}
Trwale usuwa jeden kontakt według identyfikatora, wraz z jego historią wiadomości. Tej operacji nie można cofnąć. Aby usunąć kilka kontaktów w jednym wywołaniu, użyj Usuń kontakty poniżej.
cURL
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123?apiKey=YOUR_API_KEY"
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123", {
method: "DELETE",
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.success);
Python
import requests
res = requests.delete(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123",
headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["success"])
Odpowiedź
{
"success": true
}
Identyfikator kontaktu, który nie istnieje na Twoim koncie lub należy do innego konta, zwraca 404.
Usuń kontakty
DELETE /contacts
Trwale usuwa jeden lub więcej kontaktów według identyfikatora w jednym wywołaniu (do 500 identyfikatorów). Identyfikatory, które nie istnieją na Twoim koncie, zostaną pominięte i uwzględnione w skipped. Tej operacji nie można cofnąć.
| Pole | Opis |
|---|---|
contactIds |
Tablica identyfikatorów kontaktów do usunięcia (maks. 500). |
cURL
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "contactIds": ["contactId1", "contactId2"] }'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts", {
method: "DELETE",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ contactIds: ["contactId1", "contactId2"] }),
});
const data = await res.json();
console.log(`Deleted ${data.deleted}, skipped ${data.skipped}`);
Python
import requests
res = requests.delete(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"contactIds": ["contactId1", "contactId2"]},
)
data = res.json()
print(f"Deleted {data['deleted']}, skipped {data['skipped']}")
Odpowiedź
{
"success": true,
"deleted": 2,
"skipped": 0
}
Wyświetl pola niestandardowe
GET /users/me/custom-fields — zwraca klucze pól niestandardowych zarejestrowanych na Twoim koncie (tych, które są widoczne jako kolumny oraz w formularzu kontaktowym), dzięki czemu integracja może oferować je jako opcje do wyboru zamiast zgadywania.
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/users/me/custom-fields?apiKey=YOUR_API_KEY"
Odpowiedź (200)
{ "success": true, "custom_fields": ["tier", "company", "birthday"] }
Ustaw listę za pomocą PUT /users/me/custom-fields i tego samego kształtu tablicy.
Usuwanie pola niestandardowego
DELETE /contacts/custom-fields/{fieldKey}
Usuwa jeden klucz pola niestandardowego z każdego kontaktu na Twoim koncie. Użyj tej funkcji, aby posprzątać po zmianie nazwy lub wycofaniu pola niestandardowego. Klucz może zawierać tylko litery, cyfry, podkreślniki i myślniki. Zwraca liczbę zaktualizowanych kontaktów. Tej operacji nie można cofnąć.
cURL
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/custom-fields/webinar_date_nh?apiKey=YOUR_API_KEY"
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/custom-fields/webinar_date_nh", {
method: "DELETE",
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(`Removed from ${data.updated} contacts`);
Python
import requests
res = requests.delete(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/custom-fields/webinar_date_nh",
headers={"X-API-Key": "YOUR_API_KEY"},
)
print(f"Removed from {res.json()['updated']} contacts")
Odpowiedź
{
"success": true,
"updated": 42
}
Uwaga: Klucz pola zawierający niedozwolone znaki zwróci 400.
Typy pól niestandardowych
Każde pole niestandardowe na koncie posiada typ: text (domyślny), number, date, select (lista rozwijana ze stałymi opcjami) lub boolean (pole wyboru). Pole typowane jest walidowane przy każdym zapisie — tworzeniu, aktualizacji, imporcie, w silniku automatyzacji oraz podczas zbierania pól przez AI — a jego wartość jest przechowywana w ujednoliconej formie, dzięki czemu filtry i warunki automatyzacji porównują wartości tego samego typu:
| Typ | Przechowywane jako | Akceptowane na wejściu |
|---|---|---|
text |
Cokolwiek wyślesz | Cokolwiek (zachowanie bez zmian) |
number |
Liczba JSON | 42, "42", "19,99" |
date |
"YYYY-MM-DD" |
2026-02-01, data-godzina ISO (część daty jest zachowana), 01/02/2026 (dzień pierwszy lub miesiąc pierwszy zgodnie z formatem daty konta; jednoznaczna wartość taka jak 13/02/2026 zawsze wygrywa), February 1, 2026, 1 de febrero de 2026 |
select |
Jedna z opcji pola, w pisowni danej opcji | Dowolne dopasowanie opcji bez rozróżniania wielkości liter |
boolean |
true / false |
true/false, yes/no, 1/0, si/no, ja/nein … |
Wysłanie null lub pustego ciągu znaków czyści pole typowane. Klucze, które nie są zarejestrowane na koncie, oraz pola text są przechowywane dokładnie tak, jak zostały wysłane.
Odczyt definicji pól
GET /users/me/custom-fields
{
"success": true,
"custom_fields": ["company", "birthday", "plan", "vip"],
"definitions": [
{ "key": "company", "type": "text" },
{ "key": "birthday", "type": "date" },
{ "key": "plan", "type": "select", "options": ["Free", "Pro", "Enterprise"] },
{ "key": "vip", "type": "boolean" }
]
}
custom_fields to zwykła lista kluczy (niezmieniona dla starszych klientów); definitions zawiera typ dla każdego klucza.
Ustawianie definicji pól
PUT /users/me/custom-fields
Zastępuje całą listę. Każdy wpis to albo sam klucz (zachowuje typ, który pole już posiada, text dla nowego), albo obiekt z key, type oraz, dla select, options (od 1 do 100 unikalnych opcji, do 64 znaków każda). Maksymalnie 50 pól; klucze używają małych liter, cyfr i podkreślników oraz zaczynają się od litery lub podkreślnika.
curl -X PUT "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/users/me/custom-fields?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customFields": [
"company",
{ "key": "birthday", "type": "date" },
{ "key": "plan", "type": "select", "options": ["Free", "Pro", "Enterprise"] },
{ "key": "vip", "type": "boolean" }
]
}'
Zmiana typu pola nie powoduje nadpisania wartości już zapisanych w kontaktach; są one walidowane przy następnym zapisie.
Błędy walidacji
Tworzenie lub aktualizacja kontaktu, którego wartości pól niestandardowych nie pasują do ich typu, jest odrzucane w całości z błędem 400:
{
"success": false,
"error": "Invalid custom field value(s): birthday: \"birthday\" must be a date in YYYY-MM-DD format (for example 1990-02-01).",
"field_errors": [
{ "key": "birthday", "message": "\"birthday\" must be a date in YYYY-MM-DD format (for example 1990-02-01)." }
]
}
W przypadku importu masowego rekord z nieprawidłową wartością jest zgłaszany jako nieudany z tym samym komunikatem, a reszta partii jest przetwarzana.
Listy
Listy grupują kontakty. Lista może być statyczna (sam decydujesz, kto się na niej znajduje) lub inteligentna (członkostwo jest obliczane na podstawie reguł i automatycznie aktualizowane — zobacz Organizowanie list i kontaktów).
| Pole | Opis |
|---|---|
name |
Wymagane przy tworzeniu. Maksymalnie 100 znaków. |
status |
live (domyślnie) lub draft. Małe litery. |
contact_ids |
Tablica identyfikatorów kontaktów do umieszczenia na liście. Tylko listy statyczne. |
type |
static (domyślnie) lub smart. |
smart_rules |
Zestaw reguł — wymagany, gdy type to smart. Zobacz poniżej. |
Tworzenie listy
POST /lists
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/lists?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Hot leads (active)",
"type": "smart",
"smart_rules": {
"match": "all",
"conditions": [
{ "field": "tags", "op": "has_any", "value": ["tagHotLead"] },
{ "field": "last_activity_at", "op": "within_last", "value": { "amount": 90, "unit": "days" } }
]
}
}'
Odpowiedź
{
"success": true,
"list_id": "list_abc123",
"evaluation": { "added": 3, "removed": 0, "total": 3 }
}
Lista inteligentna jest oceniana wewnątrz tego samego żądania, więc evaluation informuje dokładnie, kto się na niej znalazł. W przypadku listy statycznej evaluation to null.
Aktualizacja listy
PUT /lists/{listId}
Prześlij tylko te pola, które zmieniasz. Zmiana smart_rules powoduje natychmiastową ponowną ocenę listy i zwraca ten sam obiekt evaluation.
curl -X PUT "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/lists/list_abc123?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "smart_rules": { "match": "any", "conditions": [ { "field": "tags", "op": "has_any", "value": ["tagHotLead", "tagWebinar"] } ] } }'
Możesz przełączać listę między dwoma rodzajami:
- Statyczna → inteligentna: wyślij
{ "type": "smart", "smart_rules": { … } }. Reguły zaczną działać natychmiast. - Inteligentna → statyczna: wyślij
{ "type": "static" }. Reguły zostaną usunięte, a osoby znajdujące się na liście pozostaną na niej.
Struktura smart_rules
{
"match": "all",
"conditions": [
{ "field": "tags", "op": "has_any", "value": ["tagHotLead"] },
{ "field": "channel", "op": "is_any", "value": ["whatsapp", "sms"] },
{ "field": "last_incoming_message_at", "op": "not_within_last", "value": { "amount": 7, "unit": "days" } },
{ "field": "created_at", "op": "after", "value": "2026-01-01" },
{ "field": "is_bot_active", "op": "is", "value": true },
{ "field": "email", "op": "is_set" },
{ "field": "custom_field", "key": "Plan", "op": "eq", "value": "pro" }
]
}
match—all(każdy warunek musi być spełniony) lubany(przynajmniej jeden).conditions— od 1 do 20 warunków, każdy z maksymalnie 100 wartościami, ciągi znaków do 200 znaków.
field |
op |
value |
|---|---|---|
tags |
has_any, has_all, has_none |
tablica identyfikatorów tagów |
lists |
in_any, not_in_any |
tablica identyfikatorów list (tylko listy statyczne — inteligentna lista nie może być utworzona na podstawie innej inteligentnej listy) |
channel |
is_any, is_none |
tablica kanałów |
status |
is_any, is_none |
tablica statusów kontaktu |
created_at, last_activity_at, last_incoming_message_at, last_outgoing_message_at, first_ai_interaction_at, last_ai_interaction_at |
within_last, not_within_last |
{ "amount": 1–3650, "unit": "hours" | "days" } |
| te same pola daty | before, after |
data ISO ("2026-01-01", porównywana jako pełne dni) lub pełna data i godzina ISO ("2026-01-01T14:30:00Z", porównywana z dokładnym momentem) |
| te same pola daty | is_set, not_set |
— |
has_interacted_with_ai |
is |
true / false — true dopasowuje kontakty, do których AI wysłało wiadomość przynajmniej raz (kiedykolwiek) |
is_bot_active, do_not_disturb, is_private, has_ever_responded |
is |
true / false |
email, phone_number, first_name, last_name |
is_set, not_set, contains, not_contains |
ciąg znaków dla formularzy contains |
current_campaign_id, assigned_agent |
is_any, is_none, is_set, not_set |
tablica identyfikatorów dla formularzy is_any / is_none |
custom_field (plus key) |
eq, neq, contains, not_contains, is_set, not_set |
ciąg znaków dla formularzy wartości |
not_within_last dopasowuje również kontakty, dla których data nigdy nie została ustawiona („więcej niż N temu, lub nigdy”), a porównania tekstowe ignorują wielkość liter.
Zaangażowanie AI. has_interacted_with_ai to flaga dożywotnia: true dla każdego kontaktu, do którego Twoje AI wysłało przynajmniej jedną wiadomość, false dla wszystkich pozostałych (w tym kontaktów, na które odpowiadał tylko Twój zespół). Jest ona nadawana przy pierwszej wiadomości AI do kontaktu i nigdy nie jest usuwana, więc wyłączenie odpowiedzi AI dla kontaktu lub przeniesienie go do innej kampanii nie resetuje jej. W przypadku okresu — „kontakty, którymi moje AI zajmowało się w tym miesiącu”, co jest typowym pytaniem rozliczeniowym — należy użyć zakresu last_ai_interaction_at:
{ "field": "last_ai_interaction_at", "op": "within_last", "value": { "amount": 30, "unit": "days" } }
Nie należy mylić żadnego z nich z is_bot_active (AI może odpowiadać, co nie oznacza, że to zrobiło) ani has_ever_responded (kontakt odpisał, komukolwiek). Te same dwa znaczniki są zwracane przy każdym kontakcie jako first_ai_interaction_at / last_ai_interaction_at, a cały zestaw reguł działa również na GET /contacts?rules=, więc możesz zliczać dopasowania bez tworzenia listy.
Podgląd zestawu reguł
POST /lists/preview
Zlicza i pobiera próbki kontaktów, które pasowałyby do zestawu reguł, bez tworzenia ani zmieniania czegokolwiek. Użyj tego, aby sprawdzić poprawność reguł przed ich zapisaniem.
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/lists/preview?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "smart_rules": { "match": "all", "conditions": [ { "field": "tags", "op": "has_any", "value": ["tagHotLead"] } ] } }'
Odpowiedź
{
"success": true,
"count": 3,
"sample": [
{
"id": "contact_abc123",
"first_name": "Sofia",
"last_name": "Martinez",
"phone_number": "+31600000000",
"email": "sofia@example.com",
"channel": "whatsapp"
}
]
}
sample przechowuje do 10 kontaktów, zaczynając od tych najbardziej aktywnych.
Ponowne uruchomienie listy inteligentnej
POST /lists/{listId}/evaluate
Wymusza natychmiastową ponowną ocenę (to samo, co robi przycisk Odśwież teraz w panelu). Listy inteligentne są aktualizowane automatycznie po zmianie kontaktu oraz co 15 minut w przypadku reguł opartych na czasie, więc jest to potrzebne tylko wtedy, gdy chcesz uzyskać wynik natychmiast.
Odpowiedź
{
"success": true,
"list_id": "list_abc123",
"evaluation": { "added": 2, "removed": 1, "total": 4 }
}
evaluation.skipped: true oznacza, że inna ocena tej samej listy była już w toku i to wywołanie nie przyniosło żadnego efektu.
Listy inteligentne nie akceptują ręcznie dodawanych członków
Punkty końcowe członkostwa zwracają 409 z "This is a smart list — its members are computed from its rules. Edit the rules instead.", gdy lista docelowa jest inteligentna. Dotyczy to POST /contacts/lists, DELETE /contacts/lists, POST /contacts/lists/batch, contact_ids w POST /lists oraz PUT /lists/{listId}, a także wyboru listy inteligentnej jako celu importu CSV. Zamiast tego zmień reguły.
Wywołanie POST /lists/{listId}/evaluate na liście statycznej również kończy się 409 — nie ma ona żadnych reguł do uruchomienia.
Błędy API kontaktów
Punkty końcowe (endpoints) kontaktów zwracają standardową kopertę błędu:
{
"success": false,
"error": "Contact not found"
}
Niektóre punkty końcowe zawierają również error_code, który zazwyczaj odpowiada statusowi HTTP — jedynym wyjątkiem jest przypadek duplikatu kontaktu poniżej, gdzie status HTTP wynosi 200, a tylko error_code zawiera 409. Kody specyficzne dla punktów końcowych kontaktów:
| Kod | Kiedy występuje w punkcie końcowym kontaktu |
|---|---|
400 |
Nieprawidłowe żądanie — brakujące/nieprawidłowe pole, pusty komunikat, nieprawidłowy kursor lub ponad 500 identyfikatorów w partii. |
402 |
Brak wystarczającej liczby kredytów na wykonanie tagowania AI dla jednego kontaktu (error_code: "insufficient_credits"). |
404 |
Kontaktu, listy lub tagu nie znaleziono na Twoim koncie. |
409 |
Kontakt o takim numerze telefonu już istnieje (podczas tworzenia). Zwracane jako error_code w treści z kodem stanu HTTP 200, więc należy tutaj rozgałęzić na error_code. Zwracane również, gdy masowe automatyczne tagowanie jest już w toku (error_code: "auto_tag_run_in_progress") lub gdy połączenie kontaktu z innym kanałem spowodowałoby połączenie dwóch kontaktów już powiązanych z dwiema różnymi osobami. |
422 |
Kontakt nie może obecnie odebrać wiadomości (tryb nie przeszkadzać, profil prywatny lub nieobsługiwany kanał). W punkcie końcowym łączenia kanałów obejmuje również brak numeru telefonu, nieobsługiwaną parę kanałów lub brak podłączonego nadawcy dla kanału docelowego. |
403 w punkcie końcowym kontaktu może również oznaczać problem z limitem kontaktów lub uprawnieniami do listy, a nie z dostępem w ramach planu. Wspólne kody, które może zwrócić każdy punkt końcowy — 401, 403 (Twój plan nie obejmuje dostępu do API), 429 (limit szybkości) oraz 500 — zostały wymienione wraz ze wskazówkami dotyczącymi ponawiania prób w sekcji Błędy i stronicowanie.
Następne kroki
- API wiadomości — wysyłaj wiadomości według identyfikatora kanału i zarządzaj konwersacjami.
- Dokumentacja API — pełna lista punktów końcowych, w tym tagi i listy.
- Dostęp do API — uwierzytelnianie, limity szybkości i obsługa błędów.