API Kontak
Kontak adalah satu orang yang Anda kirimi pesan — nama, nomor telepon, email, saluran, tag, kolom kustom, serta daftar dan kampanye tempat mereka berada. API Kontak memungkinkan Anda membuat kontak, mencarinya, memperbaruinya, memberi tag, mengimpornya secara massal, dan menghapusnya, semuanya tanpa menggunakan dasbor.
Semua jalur di halaman ini bersifat relatif terhadap URL dasar:
https://api.youraiconnector.com/v1
Jadi /contacts berarti https://api.youraiconnector.com/v1/contacts.
Baru menggunakan API? Baca Akses API terlebih dahulu — bagian ini mencakup cara membuat kunci API Anda, tiga cara untuk melakukan autentikasi, batas kecepatan, dan format kesalahan. Semua yang ada di halaman ini mengasumsikan Anda sudah memiliki kunci API yang berfungsi.
Tentang ID kontak
Setiap kontak memiliki ID unik. ID yang Anda dapatkan saat membuat kontak (di data.contactId) adalah ID yang sama yang Anda gunakan di tempat lain — untuk mengambil, memperbarui, memberi tag, mengirim pesan, atau menghapus kontak tersebut. Simpan sekali dan gunakan kembali.
Anda tidak perlu membuat kontak untuk mendapatkan ID-nya. Anda juga dapat mencarinya berdasarkan nomor telepon atau email (lihat Mendapatkan kontak), atau menelusuri semua kontak Anda (lihat Daftar kontak). Masing-masing metode tersebut mengembalikan ID yang sama.
Membuat kontak
POST /contacts
Menambahkan kontak baru ke akun Anda. Nomor telepon dengan kode negara wajib diisi — email saja tidak cukup. Semua kolom lainnya bersifat opsional.
Anda dapat secara opsional memasukkan kontak baru langsung ke dalam satu atau beberapa daftar dengan listId (satu daftar) atau listIds (sebuah array). Jika keduanya dikirim, listIds yang akan digunakan.
Setiap kolom yang Anda kirimkan yang bukan merupakan salah satu kolom pembuatan standar yang tercantum dalam tabel kolom Buat kontak di bawah (phoneNumber, firstName, lastName, email, channel, is_bot_active, is_private, lead_profile, listId, listIds, custom_fields) akan disimpan secara otomatis sebagai kolom kustom — sehingga payload datar dari alat seperti Make atau Zapier dapat berfungsi tanpa perlu melakukan nesting. Anda juga dapat meneruskan objek custom_fields secara eksplisit.
| Bidang | Wajib | Deskripsi |
|---|---|---|
phoneNumber |
Ya | Nomor telepon kontak, dengan kode negara (contoh: +15551234567). |
firstName |
Tidak | Nama depan. |
lastName |
Tidak | Nama belakang. |
email |
Tidak | Alamat email. |
channel |
Tidak | Saluran pesan. Salah satu dari whatsapp, sms, whatsapp_web. Default ke whatsapp. |
is_bot_active |
Tidak | Apakah asisten AI membalas kontak ini. Default ke true. |
is_private |
Tidak | Tandai kontak sebagai pribadi. Saat true, asisten AI dimatikan untuk mereka. Default ke false. |
lead_profile |
Tidak | Catatan teks bebas tentang prospek. |
listId |
Tidak | ID daftar tunggal untuk menambahkan kontak. |
listIds |
Tidak | Larik ID daftar untuk menambahkan kontak (lebih diutamakan daripada listId). |
custom_fields |
Tidak | Objek bidang kunci/nilai Anda sendiri. Anda juga dapat meneruskannya sebagai kunci tingkat atas. Bidang dengan tipe (tanggal, angka, dropdown, kotak centang) divalidasi — lihat Tipe bidang kustom. |
Nama alternatif umum diterima jika nama standar tidak ada: phone atau phone_number untuk nomor, first_name dan last_name untuk nama, serta full_name atau name, yang dipisah menjadi nama depan dan nama belakang. Oleh karena itu, kesalahan Missing required fields: phoneNumber berarti tidak ada nilai telepon yang diterima sama sekali, bukan karena kuncinya dieja secara berbeda.
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"])
Respons
{
"success": true,
"data": {
"message": "Successfully created new contact",
"contactId": "contact_abc123",
"listsAdded": ["list123", "list456"]
}
}
ID kontak baru ada di data.contactId. Daftar tempat kontak tersebut ditambahkan akan dikembalikan dalam data.listsAdded.
Duplikat tidak dibuat. Jika kontak dengan nomor telepon yang sama sudah ada di saluran yang Anda buat, panggilan buat (create call) tidak membuat kontak kedua. Respons yang dikembalikan memiliki status HTTP
200danerror_codebernilai409di dalam body, jadi lakukan percabangan padaerror_codealih-alih pada status HTTP. ID kontak yang sudah ada dikembalikan sebagaicontactId(dandata.contactId), sehingga Anda dapat langsung melanjutkannya:{ "success": false, "error_code": 409, "error": "A contact with this phone number already exists for the current user.", "contactId": "abc123", "data": { "contactId": "abc123" } }Hal yang sama berlaku untuk alamat email duplikat di saluran Email. Jika seat tim Anda hanya dapat melihat kontaknya sendiri dan kontak yang ada bukan salah satunya, ID tersebut tidak disertakan; cari kontak tersebut dengan Dapatkan kontak berdasarkan telepon atau email sebagai gantinya.
Ejaan WhatsApp yang setara dihitung sebagai nomor yang sama. Beberapa negara memiliki dua ejaan yang valid untuk saluran seluler yang sama dan WhatsApp mungkin melaporkan salah satunya: Meksiko (
+52…dan+521…lama), Brasil (dengan atau tanpa digit kesembilan), dan Argentina (dengan atau tanpa9setelah+54). Pemeriksaan duplikat saat pembuatan dan pencocokanGET /contacts?phoneNumber=berlaku untuk kedua ejaan tersebut, sehingga Anda akan mendapatkan kembali kontak yang sudah ada, apa pun bentuk yang Anda kirimkan.phone_numberyang tersimpan pada kontak tidak akan pernah ditulis ulang.
Mendapatkan kontak berdasarkan telepon atau email
GET /contacts?phoneNumber=... atau GET /contacts?email=...
Mencari satu kontak dan mengembalikan objek kontak lengkap yang diperkaya — termasuk daftar, tag, dan kampanye yang diselesaikan menjadi pasangan { id, name }, ditambah pesan terakhir yang dipertukarkan.
Berikan salah satu phoneNumber (dalam format internasional) atau email. Jika Anda tidak memberikan keduanya, endpoint yang sama ini akan beralih ke mode Daftar kontak.
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"])
Respons
{
"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 adalah klik pada iklan yang mengarahkan ke situs web Anda, yang dicocokkan dengan kontak ini oleh skrip klik iklan, atau null jika tidak ada. Bidang-bidang tersebut dijelaskan dalam Atribusi klik iklan untuk WhatsApp.
ID kontak dikembalikan baik di tingkat atas (contactId) maupun di dalam objek (contact.id). Jika tidak ada yang cocok, Anda akan mendapatkan 404 dengan { "success": false, "message": "Contact not found" }.
avatarUrladalah foto profil kontak, yang diambil dari WhatsApp atau Meta saat mereka mengirim pesan kepada Anda. Ini bersifat baca-saja: Anda tidak dapat mengaturnya, dan ini adalahnulluntuk kontak yang tidak memiliki foto atau yang menghubungi Anda melalui saluran yang tidak membagikannya. Perlakukan tautan tersebut sebagai sementara alih-alih menyimpannya, karena beberapa tautan foto ini kedaluwarsa dan diperbarui secara otomatis. (Dalam endpoint daftar di bawah, nilai yang sama disebutavatar_url.)
Nomor telepon di URL. Tanda
+dalam string kueri harus di-encode URL sebagai%2B, jika tidak, tanda tersebut akan dibaca sebagai spasi. Contoh di atas sudah melakukannya untuk Anda.
Kontak yang nomornya disembunyikan
Kontak WhatsApp Web dapat muncul tanpa nomor telepon jika WhatsApp menyembunyikannya dari perangkat yang ditautkan (lihat Kontak yang Nomornya Disembunyikan). Setiap kontak yang Anda baca memiliki dua kolom tambahan untuk hal ini: number_hidden (numberHidden pada bentuk kontak tunggal) dan whatsapp_lid (whatsappLid).
- Untuk kontak dengan nomor tersembunyi,
phone_numberadalahnull,number_hiddenadalahtrue, danwhatsapp_lidberisi ID internal WhatsApp, dalam format153055538446346@lid. Ini bukan nomor yang dapat dihubungi. Cocokkan kontak tersebut berdasarkan ID itu dari sistem Anda sendiri, dan teruskan sebagaiphoneNumberke pencarian ini untuk menemukan kembali kontak tersebut. - Untuk setiap kontak lainnya,
number_hiddenadalahfalsedanwhatsapp_lidadalah ID yang dilaporkan WhatsApp untuk kontak tersebut, ataunull.
Webhook keluar dan payload otomatisasi yang menyertakan kontak membawa tiga kolom yang sama, dan ekspor CSV kontak memiliki kolom Number Hidden dan WhatsApp LID di samping Phone Number. Setelah WhatsApp membagikan nomor aslinya, kontak akan diperbarui secara otomatis dan phone_number akan terisi.
Mendapatkan kontak berdasarkan ID
GET /contacts/{contactId}
Jika Anda sudah memiliki ID kontak, ambil datanya secara langsung. Bentuk responsnya identik dengan pencarian di atas.
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"])
ID kontak yang tidak ada di akun Anda akan mengembalikan 404.
Mendapatkan statistik kontak
GET /contacts/{contactId}/stats
Mengembalikan statistik pesan gabungan untuk satu kontak: total, balasan AI vs manusia, kredit yang digunakan, serta stempel waktu pesan pertama/terakhir.
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"])
Respons
{
"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 adalah penghitung pesan AI yang sama dengan yang diatur ulang oleh tombol “reset” dalam aplikasi pada sebuah kontak. creditsUsed adalah total kredit berjalan untuk kontak ini, bukan hanya angka untuk respons ini saja. ID kontak yang tidak ada di akun Anda akan mengembalikan 404.
Mendapatkan biaya kontak
GET /contacts/{contactId}/cost
Mengembalikan kredit yang telah digunakan oleh satu kontak, dibagi per percakapan (sesi obrolan), per jenis tindakan, dan biaya rata-rata percakapan yang ditangani AI sendiri dibandingkan dengan percakapan yang melibatkan tim Anda. Gunakan ini untuk menunjukkan kepada klien berapa biaya sebuah percakapan.
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"])
Respons
{
"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
}
Catatan kolom:
total_creditsmenghitung setiap biaya pada kontak (balasan AI, panggilan alat, tag, tindak lanjut, ringkasan, langkah evaluasi latar belakang, biaya templat dan pengiriman) dengan pengurangan pengembalian dana pada kontak. Pemberian kredit, isi ulang, dan pembaruan tidak pernah menjadi milik kontak. Uji coba di Playground tidak disertakan.- Biaya masuk ke dalam percakapan yang jendelanya mencakup biaya tersebut. Jendela berjalan dari awal percakapan hingga ditutup atau percakapan berikutnya dimulai, sehingga tidak ada biaya yang dihitung dua kali. Biaya di luar setiap jendela berada di
unattributed_credits(dan dalam total). outcome:containedberarti AI membalas dan tidak ada seorang pun dari tim Anda yang perlu turun tangan;handed_offberarti seseorang mengirim pesan setelah AI membalas, atau AI memberi tahu manusia;no_aiberarti AI tidak pernah membalas dalam percakapan tersebut.- Hingga 200 percakapan dicantumkan, yang terbaru terlebih dahulu;
sessions_truncatedadalahtruejika ada lebih banyak. - Untuk klien agensi, berikan
sub_account_id; angka-angka tersebut dalam kredit klien itu sendiri. creditsUsedpada Dapatkan statistik kontak adalah penghitung berjalan yang disimpan pada kontak. Titik akhir ini membangun kembali angka dari buku besar kredit, sehingga keduanya bisa sedikit berbeda, terutama pada kontak yang lebih lama.- Anggota tim memerlukan izin penagihan. Kontak yang tidak ada di akun Anda akan mengembalikan
404;503berarti angka tidak dapat dihitung saat ini, coba lagi sebentar lagi.
Mencantumkan kontak
GET /contacts
Panggil GET /contacts tanpa phoneNumber maupun email untuk menelusuri semua kontak Anda, dimulai dari yang terbaru. Setiap halaman mengembalikan ringkasan kontak yang ringkas (daftar, tag, dan kampanye dikembalikan sebagai array ID, bukan objek lengkap) dan sebuah next_cursor.
| Parameter kueri | Deskripsi |
|---|---|
limit |
Ukuran halaman. Default-nya 50, maksimum 100. |
cursor |
Nilai next_cursor dari halaman sebelumnya. Hilangkan pada halaman pertama. |
listId |
Opsional. Hanya mengembalikan kontak yang termasuk dalam daftar ini. |
Untuk menelusuri setiap halaman: lakukan panggilan pertama tanpa kursor, lalu terus berikan next_cursor yang dikembalikan sebagai cursor. Berhenti saat next_cursor bernilai null — itu berarti tidak ada lagi hasil yang tersisa.
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
Respons
{
"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"
}
Setiap kontak membawa ad_attribution: klik pada iklan yang mengarahkan ke situs web Anda, yang dicocokkan dengan kontak tersebut, dengan bidang yang sama seperti adAttribution pada Mendapatkan kontak berdasarkan ID, atau null jika tidak ada.
Catatan: Memfilter berdasarkan listId yang tidak ada di akun Anda akan mengembalikan 404. cursor yang tidak valid akan mengembalikan 400.
Menghitung kontak
GET /contacts/count
Mengembalikan berapa banyak kontak yang cocok dengan filter, ditambah pembagian per saluran, tanpa perlu melakukan paging. Ini adalah panggilan yang tepat untuk pertanyaan “berapa banyak” — ubin dasbor, otomatisasi, atau bertanya kepada Champ. Semua filter bersifat opsional, dan menggabungkan beberapa filter akan mempersempit jumlahnya (kontak harus cocok dengan setiap filter yang Anda kirim).
| Parameter kueri | Deskripsi |
|---|---|
agentId |
Hanya kontak yang ditetapkan ke agen AI ini. Berikan none untuk kontak tanpa agen yang ditetapkan (kontak tersebut dijawab oleh agen default saluran). |
channel |
Hanya kontak di saluran ini, contohnya whatsapp, messenger, instagram, sms, email, chat_widget. |
tag |
Hanya kontak yang memiliki tag ini, berdasarkan nama tag (huruf besar/kecil tidak berpengaruh). Nama tag yang tidak Anda miliki akan mengembalikan 404. |
listId |
Hanya kontak di daftar ini. |
botActive |
true atau false — hanya kontak yang asisten AI-nya aktif atau nonaktif. |
status |
Hanya kontak dengan status ini, contohnya Lead. |
rules |
Objek aturan JSON yang di-encode URL, menggunakan bentuk yang sama dengan daftar cerdas (lihat Bentuk smart_rules di bawah). Tidak dapat digabungkan dengan filter lainnya. |
Jangan kirim filter apa pun dan Anda akan mendapatkan jumlah total kontak di akun Anda.
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"])
Respons
{
"success": true,
"total": 3423,
"by_channel": { "messenger": 2744, "instagram": 667, "none": 12 },
"filters": { "agentId": "agent_xyz789" }
}
by_channel membagi total yang sama per saluran; kontak yang tidak ada di saluran mana pun dihitung di bawah none. filters menggemakan kembali filter yang diterapkan, sehingga Anda dapat memeriksa apakah panggilan tersebut melakukan apa yang Anda maksudkan.
Catatan: Mengirim rules bersama dengan filter lain, atau nilai rules yang bukan JSON valid, akan mengembalikan 400. Nama tag atau ID daftar yang tidak ada di akun Anda akan mengembalikan 404.
Memperbarui kontak
PUT /contacts/{contactId}
Memperbarui kontak yang sudah ada. Hanya kolom yang Anda sertakan yang akan diubah — abaikan kolom yang tidak ingin Anda ubah. Anda harus mengirim setidaknya satu kolom, atau Anda akan mendapatkan 400 (“Tidak ada kolom untuk diperbarui”).
| Bidang | Deskripsi |
|---|---|
firstName |
Nama depan. |
lastName |
Nama belakang. |
email |
Alamat email. |
is_bot_active |
Apakah asisten AI membalas kontak ini. |
is_private |
Tandai pribadi. Mengaturnya ke true juga mematikan asisten AI. |
do_not_disturb |
Jeda penjangkauan otomatis ke kontak ini. Juga menghentikan AI dari membalas. |
follow_ups_disabled |
Hentikan semua tindak lanjut otomatis untuk kontak ini (cepat, siklus, dan prospek dingin) sementara AI terus membalas pesan yang mereka kirim. Berguna setelah seseorang membeli. Tetap mati sampai Anda mengaturnya kembali ke false. |
lead_profile |
Catatan prospek teks bebas. |
custom_fields |
Objek bidang kustom. Digabungkan per kunci — hanya kunci yang Anda kirim yang ditulis, sisa bidang kustom yang ada tetap disimpan. Anda juga dapat meneruskan kunci bidang kustom di tingkat atas. Bidang bertipe divalidasi dan nilai yang buruk akan menolak permintaan dengan field_errors — lihat Tipe bidang kustom. |
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"])
Respons
{
"success": true,
"message": "Contact updated successfully"
}
Kolom kustom digabungkan, bukan diganti. Mengirim
{ "custom_fields": { "tier": "gold" } }hanya akan mengaturtier— kolom kustom lainnya pada kontak akan tetap seperti semula. Untuk menghapus kolom kustom sepenuhnya di semua kontak, gunakan Hapus kolom kustom.
Menambah atau menghapus tag
POST /contacts/{contactId}/tags
Menambah dan/atau menghapus tag pada satu kontak dalam satu panggilan. Teruskan ID tag di addTagIds dan removeTagIds. Setidaknya salah satu dari keduanya harus tidak kosong.
Tag tersebut harus sudah ada di akun Anda — buat tag terlebih dahulu melalui endpoint tag. Jika kontak atau tag yang dirujuk tidak ada, Anda akan mendapatkan 404.
| Bidang | Deskripsi |
|---|---|
addTagIds |
Larik ID tag untuk ditambahkan ke kontak. |
removeTagIds |
Larik ID tag untuk dihapus dari kontak. |
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"])
Respons
{
"success": true,
"contact_id": "contact_abc123",
"added": 1,
"removed": 1
}
Mengelola pustaka tag Anda
Endpoint ini mengelola tag itu sendiri — mengganti nama atau menghapusnya di akun Anda — berbeda dengan menerapkan atau menghapus tag pada satu kontak (lihat Menambah atau menghapus tag di atas). Setiap tag di akun Anda memiliki ID (tagId): ID yang ditampilkan di pengelola tag dasbor Anda, dan ID yang dikembalikan sebagai tag_id di tingkat atas respons saat Anda membuat tag dengan POST /tags dan isi JSON { "name": "..." } (tanpa phoneNumber, email, atau contactId).
Memperbarui tag
PUT /tags/{tagId}
Kirim hanya kolom yang ingin Anda ubah.
| Kolom | Deskripsi |
|---|---|
name |
Nama tag. |
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)" }'
Respons
{ "success": true, "tag_id": "tagHotLead" }
tagId yang tidak ada di akun Anda akan mengembalikan 404.
Menghapus tag
DELETE /tags/{tagId}
Menghapus satu tag berdasarkan ID. Tindakan ini tidak dapat dibatalkan — kontak yang memiliki tag tersebut akan kehilangan tag tersebut. Menghapus tag yang sudah tidak ada (atau tidak pernah ada) akan mengembalikan 200 dengan deleted: 0 alih-alih 404, karena tidak ada yang perlu dihitung.
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/tags/tagColdLead?apiKey=YOUR_API_KEY"
Respons
{ "success": true, "deleted": 1 }
Menghapus beberapa tag sekaligus
DELETE /tags
| Field | Description |
|---|---|
tagIds |
Array ID tag untuk dihapus (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"] }'
Respons
{ "success": true, "deleted": 2 }
ID yang tidak ada, atau milik akun lain, akan dilewati secara diam-diam dan tidak dihitung dalam deleted.
Mengatur flag secara massal
POST /contacts/bulk-flag
Mengatur satu flag boolean pada banyak kontak sekaligus. Hingga 500 ID kontak per permintaan. ID yang tidak ada di akun Anda akan dilewati dan dihitung dalam skipped.
| Bidang | Deskripsi |
|---|---|
contactIds |
Larik ID kontak untuk diperbarui (maks 500). |
field |
Flag mana yang akan diatur. Salah satu dari bot_active (asisten AI aktif/nonaktif), dnd (jeda jangkauan otomatis), spam, private. |
value |
Nilai boolean untuk mengatur flag tersebut. |
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"])
Respons
{
"success": true,
"updated": 2,
"skipped": 0
}
Impor kontak massal
POST /contacts/import
Membuat hingga 500 kontak dalam satu panggilan dari array JSON. Setiap catatan memerlukan phone_number dalam format internasional; sisanya bersifat opsional. Catatan dengan nomor telepon yang tidak valid atau saluran yang tidak didukung akan dilewati (tidak dibuat), dan setiap catatan yang dilewati akan dilaporkan beserta indeks dan alasannya — sehingga Anda dapat memperbaiki kegagalan tersebut dan mencoba lagi.
Nomor telepon yang sudah ada di akun Anda akan dilewati sebagai duplicate secara default — namun saat Anda mengirim listId, kontak yang sudah Anda miliki tetap ditambahkan ke daftar tersebut dan dihitung di bawah added_to_list, sehingga impor ulang akan mengisi daftar tanpa mengubah apa pun pada kontak tersebut. Kirim updateExisting: true untuk memperbarui kontak tersebut sebagai gantinya: bidang yang ada dalam catatan akan menimpa (first_name, last_name, email, lead_profile, dan custom_fields digabungkan kunci demi kunci), tags akan ditambahkan, dan kontak akan ditambahkan ke listId. Saluran, nomor telepon, dan flag bot tidak pernah diubah pada kontak yang sudah ada.
Anda dapat menambahkan setiap kontak yang diimpor (atau diperbarui) ke daftar dengan listId secara opsional, menetapkan defaultChannel untuk catatan yang tidak menentukannya, dan menandai catatan dengan tags (nama tag — tag yang tidak ada akan dibuat, tag yang sudah ada dicocokkan tanpa mempedulikan huruf besar/kecil).
Bidang tingkat atas
| Bidang | Wajib | Deskripsi |
|---|---|---|
contacts |
Ya | Larik catatan kontak (maks 500). |
listId |
Tidak | Daftar untuk menambahkan setiap kontak dalam permintaan ini — baik yang dibuat, diperbarui, maupun yang dilewati sebagai duplikat. Harus berupa daftar di akun Anda. |
defaultChannel |
Tidak | Saluran yang diterapkan ke catatan yang menghilangkan channel. Salah satu dari whatsapp, sms, whatsapp_web. Default ke whatsapp. |
updateExisting |
Tidak | true untuk memperbarui kontak yang nomor teleponnya sudah ada alih-alih melewatinya sebagai duplicate. Default ke false. |
Bidang per catatan
| Bidang | Wajib | Deskripsi |
|---|---|---|
phone_number |
Ya | Nomor telepon dalam format internasional (+ di depan akan ditambahkan jika tidak ada). |
first_name |
Tidak | Nama depan. |
last_name |
Tidak | Nama belakang. |
email |
Tidak | Alamat email. |
channel |
Tidak | Salah satu dari whatsapp, sms, whatsapp_web. Kembali ke defaultChannel. |
is_bot_active |
Tidak | Apakah asisten AI membalas. Default ke true. |
is_private |
Tidak | Tandai pribadi. Default ke false. |
lead_profile |
Tidak | Catatan prospek teks bebas. |
custom_fields |
Tidak | Objek kunci dan nilai bidang kustom. |
tags |
Tidak | Array nama tag (satu string "a; b" juga berfungsi). Tag yang tidak ada akan dibuat; tag yang sudah ada dicocokkan dengan mengabaikan huruf besar/kecil. Maks 25 per catatan. |
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'])}")
Respons
{
"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": []
}
Jika beberapa catatan tidak dapat dibuat, catatan tersebut akan muncul di skipped beserta alasannya (di sini tanpa updateExisting, sehingga nomor yang sudah ada dilewati):
{
"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" }
]
}
Baris yang dilewati tetap bergabung dengan listId, yang merupakan apa yang dihitung oleh added_to_list — tidak ada hal lain pada kontak tersebut yang disentuh. Dengan updateExisting: true, permintaan yang sama melaporkan kontak yang ada di bawah updated / updated_contact_ids sebagai gantinya.
Kemungkinan alasan dilewati: invalid_record, missing_phone_number, invalid_phone_number, invalid_channel, duplicate_in_request, duplicate, contact_limit_reached, create_failed.
Batas paket. Jika batas kontak paket Anda tidak memungkinkan penambahan kontak sebanyak ini, seluruh permintaan akan ditolak di awal dengan
403. Jika batas tercapai di tengah proses, catatan yang tersisa akan dikembalikan sebagai dilewati dengan alasancontact_limit_reached.
Impor kontak dari file CSV
Untuk impor yang lebih besar dari yang didukung oleh impor massal (hingga sekitar 50.000 baris), antrekan pekerjaan impor asinkron terhadap file CSV yang sudah ada di penyimpanan akun Anda, lalu lakukan polling hingga selesai.
Memulai impor
POST /contacts/import-csv
| Field | Wajib | Deskripsi |
|---|---|---|
csvStoragePath |
Ya | Jalur penyimpanan file CSV, di bawah users/{your account id}/imports/, diakhiri dengan .csv. |
listName |
Ya | Membuat (atau menggunakan kembali) daftar dengan nama ini dan menambahkan setiap kontak yang diimpor ke dalamnya. |
existingListRefs |
Tidak | Array ID daftar yang ada untuk juga menambahkan setiap kontak yang diimpor ke dalamnya. |
defaultChannel |
Tidak | Saluran yang diterapkan ke baris yang tidak menentukannya. |
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"]
Respons (202 — impor sedang diantrekan, belum selesai)
{
"success": true,
"job_id": "csvimp_abc123",
"status": "queued"
}
Memasukkan file ke penyimpanan. Endpoint ini memulai dan melacak pekerjaan impor; endpoint ini tidak menerima unggahan secara langsung. File CSV harus sudah berada di
csvStoragePathsebelum Anda memanggilnya — pengimpor CSV dasbor itu sendiri melakukan ini sebagai langkah pertamanya.
Polling pekerjaan impor
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"
Respons
{
"success": true,
"job_id": "csvimp_abc123",
"status": "completed",
"imported": 812,
"updated": 0,
"skipped": 14,
"errors": [],
"error_message": null
}
status bergerak melalui queued → processing → completed, atau failed dengan alasan di error_message. jobId yang tidak ada di akun Anda akan mengembalikan 404.
Ekspor kontak
Memulai ekspor CSV asinkron dari kontak Anda dan mengembalikan pekerjaan yang dapat Anda polling untuk penyelesaiannya.
Memulai ekspor
POST /contacts/export
| Bidang | Wajib | Deskripsi |
|---|---|---|
listId |
Tidak | Hanya ekspor kontak yang termasuk dalam daftar ini. |
contactIds |
Tidak | Hanya ekspor ID kontak spesifik ini. |
Jika keduanya dikosongkan, semua kontak di akun Anda akan diekspor.
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"]
Respons (202 — ekspor sedang dalam antrean)
{
"success": true,
"job_id": "export_abc123",
"status": "queued"
}
Memeriksa status pekerjaan ekspor
GET /contacts/export/{jobId}
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/export/export_abc123?apiKey=YOUR_API_KEY"
Respons
{
"success": true,
"job_id": "export_abc123",
"status": "completed",
"export_id": "exp_xyz789",
"contact_count": 812,
"error_message": null
}
Setelah
statusberstatus"completed", Anda akan mendapatkanexport_iddancontact_count. Pengunduhan file CSV yang dihasilkan dilakukan melalui halaman Ekspor di dasbor Anda.
Kirim pesan ke kontak
POST /contacts/{contactId}/send-message
Mengirim pesan ke kontak yang sudah ada di saluran mana pun yang sedang mereka gunakan. Pesan akan diantrekan dan dikirim di latar belakang — respons mengonfirmasi bahwa pesan telah diterima, bukan bahwa pesan tersebut sudah terkirim.
| Bidang | Wajib | Deskripsi |
|---|---|---|
body |
Ya | Teks pesan yang akan dikirim. |
mediaUrl |
Tidak | URL file media untuk dilampirkan. |
mediaContentType |
Tidak | Tipe MIME dari media yang dilampirkan (contoh: 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"])
Respons
{
"success": true,
"messageId": "aB3dE5fG7hI9jK1lM2nO",
"contactId": "contact_abc123",
"channel": "whatsapp",
"message": "Message created successfully. Delivery is being processed."
}
Tidak dapat mengirim sekarang? Jika kontak mengaktifkan mode jangan ganggu atau mode pribadi, atau tidak berada di saluran yang dapat menerima pesan keluar, permintaan akan ditolak dengan
422danerroryang menjelaskan alasannya.
Untuk mengirim melalui nomor telepon, ID Instagram, atau identitas saluran lainnya alih-alih ID kontak — dan untuk informasi lebih lanjut tentang pengiriman pesan secara umum — lihat Messages API.
Kirim tindak lanjut AI ke kontak
POST /contacts/{contactId}/ai-follow-up
Meminta agen AI menulis satu pesan dengan suaranya sendiri, berdasarkan percakapan yang telah berlangsung dan instruksi yang Anda berikan, lalu mengirimkannya melalui saluran kontak tersebut. Ini sama dengan langkah otomatisasi Kirim tindak lanjut AI, dan biayanya sama dengan tindak lanjut AI.
| Bidang | Wajib | Deskripsi |
|---|---|---|
instruction |
Ya | Apa yang harus dilakukan pesan tersebut, dalam kata-kata sederhana, hingga 2.000 karakter. Contoh: “Ingatkan mereka bahwa tautan reservasi mereka akan kedaluwarsa malam ini dan tanyakan apakah mereka ingin menguncinya”. |
agentId |
Tidak | Agen yang menulis pesan. Kosongkan untuk menggunakan agen yang sudah menangani kontak tersebut. |
mustContain |
Tidak | Daftar hingga 5 frasa atau tautan (masing-masing hingga 500 karakter) yang harus disertakan dalam pesan kata demi kata. Jika AI melewatkan salah satunya, tidak ada pesan yang dikirim dan skipReason adalah text_check_failed. |
respectMessagingWindow |
Tidak | Default true. Pada WhatsApp Business API, Instagram, dan Messenger, lewati jika pesan terakhir kontak sudah lebih dari 24 jam. |
skipWhenAiPaused |
Tidak | Default true. Lewati jika AI dimatikan untuk kontak ini. |
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);
Respons saat pesan berhasil dikirim:
{
"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"
}
Respons saat pesan dilewati:
{
"sent": false,
"skipReason": "window_closed"
}
Keduanya kembali dengan status 200. skipReason adalah salah satu dari no_agent (tidak ada agen yang menangani kontak dan tidak ada yang diberikan), dnd (jangan ganggu), ai_paused, window_closed (di luar jendela 24 jam, atau kontak tidak pernah menulis kepada Anda di saluran tersebut), no_credits, text_check_failed (item mustContain tidak ditemukan), no_text (AI tidak menulis apa pun yang dapat digunakan) atau linkedin_gap (prospek LinkedIn mendapatkan pesan otomatis kurang dari 24 jam yang lalu; jawaban tersebut kemudian juga membawa nextAllowedAt, waktu kapan pesan berikutnya dapat dikirim). Lewati (skip) adalah jawaban normal, bukan kesalahan, jadi periksa sent sebelum berasumsi bahwa pesan telah terkirim. Panggilan yang dilewati tidak menggunakan kredit.
Isi yang kehilangan instruction atau memiliki bidang dengan tipe yang salah akan mendapatkan 400. Kontak atau agen yang tidak ada di akun Anda akan mendapatkan 404. Kesalahan dikembalikan sebagai { "success": false, "error": "..." }. Kunci anggota tim memerlukan akses edit ke kontak.
Menetapkan agen AI ke kontak
POST /contacts/{contactId}/assign-agent
Memindahkan percakapan yang ada ke agen AI yang berbeda, mulai dari pesan berikutnya dan seterusnya. Ini sama dengan Tetapkan Agen AI di menu obrolan, dan langkah yang sama yang digunakan oleh tindakan Tetapkan agen AI dalam Otomatisasi.
| Bidang | Wajib | Deskripsi |
|---|---|---|
agentId |
Ya | ID agen AI yang harus mengambil alih, atau null untuk menghapus penugasan agar percakapan kembali ke kotak masuk tim Anda. |
triggerAIResponse |
Tidak | true membuat agen yang baru ditugaskan langsung membalas pesan kontak terbaru yang belum terjawab. Default-nya adalah false. Jika kontak sudah berada di agen ini dan pesan terakhirnya sudah terjawab, tidak ada balasan yang dimulai: respons membawa aiResponseTriggered: false dan aiTriggerError yang menjelaskan alasannya. |
Berhati-hatilah dengan
triggerAIResponse: true— ini mengirim pesan ke kontak saat itu juga, jadi gunakan hanya jika Anda ingin mereka dikirimi pesan sekarang. Di Messenger dan Instagram, pesan tersebut gagal jika kontak terakhir menulis kepada Anda lebih dari 24 jam yang lalu.
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"])
Respons
{
"success": true,
"data": {
"contactId": "contact_abc123",
"agentId": "agent_xyz789",
"aiResponseTriggered": false
}
}
Agen harus berasal dari akun yang sama dengan kontak; jika tidak, permintaan akan ditolak dengan
404atau403. Temukan ID agen di halaman Agen AI (URL setiap agen diakhiri dengan ID-nya).
Menetapkan agen AI ke banyak kontak
POST /contacts/bulk-assign-agent
Memindahkan banyak percakapan ke agen AI yang berbeda dalam satu panggilan — atau menghapus penetapan untuk semuanya dengan null. Ini murni perubahan perutean: tidak ada pesan yang dikirim dan agen tidak membalas siapa pun. Setiap kontak cukup mendapatkan agen baru saat mereka menulis pesan berikutnya. (Itulah sebabnya tidak ada triggerAIResponse di sini.)
| Bidang | Wajib | Deskripsi |
|---|---|---|
agentId |
Ya | Agen AI yang harus mengambil alih, atau null untuk menghapus penetapan. |
contactIds |
Salah satu dari tiga | Hingga 500 ID kontak untuk dipindahkan. |
filter |
Salah satu dari tiga | Pilih kontak di server alih-alih mencantumkannya, yang terbaru terlebih dahulu. Menggunakan kunci yang sama dengan filter endpoint hitung: agentId (atau none), channel, tag, listId, botActive, status. |
rules |
Salah satu dari tiga | Objek aturan daftar cerdas — lihat Bentuk smart_rules. |
limit |
Tidak | Berapa banyak kontak yang akan dipindahkan dalam panggilan ini saat Anda memilih dengan filter atau rules. 1 hingga 500, defaultnya 500. |
Kirim tepat satu dari contactIds, filter, atau 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"])
Respons
{
"success": true,
"agentId": "agent_xyz789",
"matched": 3415,
"updated": 500,
"skipped": 0,
"remaining": 2915,
"filters": { "agentId": "agent_abc123" }
}
matched adalah berapa banyak kontak yang ditemukan oleh pilihan tersebut secara total, updated berapa banyak yang dipindahkan oleh panggilan ini, skipped berapa banyak ID yang Anda kirim tidak ditemukan di akun Anda, dan remaining berapa banyak yang masih cocok sekarang setelah panggilan ini selesai.
Memindahkan semua orang. Karena satu panggilan memindahkan paling banyak 500 kontak, grup besar memerlukan beberapa panggilan. Gunakan filter yang berhenti mencocokkan kontak setelah dipindahkan — misalnya filter: { "agentId": "agent_abc123" } saat menetapkan ke agent_xyz789 — dan ulangi panggilan yang sama persis hingga remaining kembali sebagai 0. Saat Anda memberikan contactIds sebagai gantinya, remaining selalu 0.
Menetapkan kontak ke departemen
POST /contacts/{contactId}/department
“Tetapkan prospek ini ke Penjualan” — mengarsipkan kontak di bawah departemen tertentu dan, secara default, menyerahkannya kepada siapa pun di departemen tersebut yang saat ini memiliki kontak paling sedikit. Ini terpisah dari menetapkan agen AI: departemen menjawab “tim mana yang memiliki ini,” agen menjawab “AI mana yang menjawab ini,” dan menetapkan salah satunya tidak akan pernah menghapus yang lain.
| Bidang | Wajib | Deskripsi |
|---|---|---|
department_id |
Ya | Departemen tempat kontak akan diarsipkan. Berikan null untuk menghapusnya. |
hand_to_member |
Tidak | Juga serahkan kontak kepada orang dengan beban kerja paling sedikit di departemen tersebut. Default-nya adalah true. Tidak akan pernah menetapkan ulang kontak yang sudah dimiliki seseorang. |
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"])
Respons
{
"success": true,
"department_id": "dept_sales",
"assigned_to": "member_uid_123"
}
assigned_to bernilai null jika kontak tersebut sudah dimiliki oleh seseorang, atau Anda memberikan hand_to_member: false.
Menautkan kontak lintas saluran
“Lanjutkan di WhatsApp” (atau SMS) akan mencari atau membuat kontak orang ini di saluran berbasis telepon lain dan menautkan keduanya, sehingga aplikasi mengenali mereka sebagai orang yang sama.
Tautkan ke saluran lain
POST /contacts/{contactId}/link-channel
| Bidang | Wajib | Deskripsi |
|---|---|---|
channel |
Ya | Saluran untuk ditautkan. Salah satu dari whatsapp, whatsapp_web, sms. |
phoneNumber |
Tidak | Nomor telepon yang digunakan pada saluran baru. Default-nya adalah nomor kontak sumber itu sendiri. |
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" }'
Respons
{
"success": true,
"data": {
"contact_id": "contact_def456",
"person_id": "person_xyz789",
"created": true
}
}
created memberi tahu Anda apakah kontak baru dibuat untuk saluran target atau kontak yang sudah ada ditemukan dan ditautkan. Memanggil ini untuk kedua kalinya aman — fungsi ini mengembalikan contact_id yang sama dengan created: false alih-alih membuat duplikat.
422 berarti akun tidak dapat melakukan penautan ini saat ini: kontak sudah berada di keluarga saluran tersebut, tidak memiliki nomor telepon untuk digunakan, atau tidak ada pengirim yang terhubung untuk saluran target. 409 berarti kedua kontak sudah ditautkan ke dua orang yang berbeda — batalkan tautan salah satunya terlebih dahulu.
Mencantumkan percakapan yang ditautkan dari kontak
GET /contacts/{contactId}/linked
Mengembalikan percakapan lain yang merupakan orang yang sama dengan kontak ini. Kontak yang tidak ditautkan akan mengembalikan array kosong, bukan 404 — “orang ini tidak memiliki saluran lain” adalah status yang normal.
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/linked?apiKey=YOUR_API_KEY"
Respons
{
"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"
}
}
]
}
Batalkan tautan kontak
DELETE /contacts/{contactId}/link
Menghapus kontak ini dari orangnya, secara sepihak — kontak lain yang masih ditautkan ke orang tersebut tetap mempertahankan tautannya, jadi membatalkan tautan satu dari tiga kontak tidak akan membubarkan grup tersebut.
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/link?apiKey=YOUR_API_KEY"
Respons
{ "success": true }
Mengambil foto profil kontak
POST /contacts/{contactId}/profile-pic
Mengambil (dan menyimpan dalam cache) foto profil WhatsApp atau Meta kontak sesuai permintaan — foto yang sama dikembalikan sebagai avatarUrl pada Dapatkan kontak, yang diperbarui.
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/profile-pic?apiKey=YOUR_API_KEY"
Respons
{
"success": true,
"avatar_url": "https://example.com/photo.jpg",
"cached": false
}
cached: true berarti URL berasal dari pengambilan terbaru, bukan pencarian penyedia yang baru — foto disimpan dalam cache selama 7 hari, dan kontak yang menurut penyedia tidak memiliki foto yang dapat dijangkau akan disimpan dalam cache sebagai tidak tersedia selama 24 jam. Jika tidak ada foto untuk diambil, avatar_url dihilangkan dan message menjelaskan alasannya.
Menandai kontak secara otomatis dengan AI
Menjalankan aturan tag akun Anda pada riwayat percakapan lengkap satu atau beberapa kontak dan menerapkan (atau menghapus) tag persis seperti penandaan waktu nyata yang berjalan selama obrolan langsung — aturan yang sama, biaya kredit per tag yang sama.
Memulai proses
POST /contacts/auto-tag
| Bidang | Wajib | Deskripsi |
|---|---|---|
scope |
Ya | "contacts" untuk menandai kontak tertentu, atau "agent" untuk menandai setiap percakapan yang saat ini ditangani oleh satu agen AI. |
contact_ids |
Wajib jika scope adalah "contacts" |
Larik ID kontak, 1 hingga 500. |
agent_id |
Wajib jika scope adalah "agent" |
Agen AI yang percakapannya akan ditandai. Jika scope adalah "contacts", bidang ini bersifat opsional dan hanya mempersempit aturan tag agen mana yang dijalankan. |
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"] }'
Satu kontak dijalankan secara inline dan langsung mengembalikan hasilnya:
{ "success": true, "result": { "tags_applied": 2, "tags_removed": 0 } }
Dua atau lebih kontak (atau scope: "agent") dijalankan sebagai tugas latar belakang dan langsung mengembalikan 202:
{ "success": true, "run_id": "m1x2y3-a1b2c3d4", "total": 214 }
Melakukan polling proses
GET /contacts/auto-tag/run
Mengembalikan proses akun saat ini (atau yang terbaru), sehingga Anda dapat melakukan polling kemajuan tanpa harus melacak run_id sendiri.
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/auto-tag/run?apiKey=YOUR_API_KEY"
Respons
{
"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 adalah null jika akun belum pernah memulai satu proses pun. status berpindah dari "running" ke "completed" atau "failed".
Hanya satu proses massal yang dapat berjalan per akun dalam satu waktu — memulai proses kedua saat proses lain sedang berjalan akan mengembalikan 409 dengan error_code: "auto_tag_run_in_progress". Kehabisan kredit pada proses satu kontak akan mengembalikan 402 dengan error_code: "insufficient_credits"; proses massal justru akan berhenti lebih awal dan melaporkan sejauh mana proses tersebut berjalan di run.
Menghapus kontak
DELETE /contacts/{contactId}
Menghapus satu kontak secara permanen berdasarkan ID, beserta riwayat pesannya. Tindakan ini tidak dapat dibatalkan. Untuk menghapus beberapa kontak dalam satu panggilan, gunakan Hapus kontak di bawah ini.
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"])
Respons
{
"success": true
}
ID kontak yang tidak ada di akun Anda, atau milik akun yang berbeda, akan mengembalikan 404.
Menghapus kontak
DELETE /contacts
Menghapus satu atau beberapa kontak secara permanen berdasarkan ID dalam satu panggilan (hingga 500 ID). ID yang tidak ada di akun Anda akan dilewati dan dihitung dalam skipped. Tindakan ini tidak dapat dibatalkan.
| Bidang | Deskripsi |
|---|---|
contactIds |
Larik ID kontak yang akan dihapus (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']}")
Respons
{
"success": true,
"deleted": 2,
"skipped": 0
}
Daftar kolom kustom
GET /users/me/custom-fields — mengembalikan kunci kolom kustom yang terdaftar di akun Anda (yang ditampilkan sebagai kolom dan di formulir kontak), sehingga integrasi dapat menawarkannya sebagai pemilih alih-alih menebaknya.
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/users/me/custom-fields?apiKey=YOUR_API_KEY"
Respons (200)
{ "success": true, "custom_fields": ["tier", "company", "birthday"] }
Tetapkan daftar dengan PUT /users/me/custom-fields dan bentuk array yang sama.
Menghapus bidang kustom
DELETE /contacts/custom-fields/{fieldKey}
Menghapus satu kunci bidang kustom dari setiap kontak di akun Anda. Gunakan ini untuk membersihkan data setelah mengganti nama atau menonaktifkan bidang kustom. Kunci hanya boleh berisi huruf, angka, garis bawah, dan tanda hubung. Mengembalikan jumlah kontak yang diperbarui. Tindakan ini tidak dapat dibatalkan.
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")
Respons
{
"success": true,
"updated": 42
}
Catatan: Kunci kolom dengan karakter yang tidak didukung akan mengembalikan 400.
Tipe bidang kustom
Setiap bidang kustom pada akun memiliki tipe: text (default), number, date, select (dropdown dengan opsi tetap), atau boolean (kotak centang). Bidang bertipe divalidasi pada setiap penulisan — buat, perbarui, impor, mesin otomatisasi, dan pengumpulan bidang AI itu sendiri — dan nilainya disimpan dalam satu bentuk kanonik, sehingga filter dan kondisi otomatisasi membandingkan hal yang sama dengan yang sama:
| Tipe | Disimpan sebagai | Diterima pada input |
|---|---|---|
text |
Apa pun yang Anda kirim | Apa pun (perilaku tidak berubah) |
number |
Angka JSON | 42, "42", "19,99" |
date |
"YYYY-MM-DD" |
2026-02-01, tanggal-waktu ISO (bagian tanggal disimpan), 01/02/2026 (hari-pertama atau bulan-pertama sesuai format tanggal akun; nilai yang tidak ambigu seperti 13/02/2026 selalu menang), February 1, 2026, 1 de febrero de 2026 |
select |
Salah satu opsi bidang, dalam ejaan opsi itu sendiri | Kecocokan apa pun yang tidak peka huruf besar-kecil dari sebuah opsi |
boolean |
true / false |
true/false, yes/no, 1/0, si/no, ja/nein … |
Mengirim null atau string kosong akan menghapus bidang bertipe. Kunci yang tidak terdaftar di akun, dan bidang text, disimpan persis seperti yang dikirim.
Baca definisi bidang
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 adalah daftar kunci biasa (tidak berubah untuk klien lama); definitions membawa tipe per kunci.
Tetapkan definisi bidang
PUT /users/me/custom-fields
Menggantikan seluruh daftar. Setiap entri adalah kunci kosong (mempertahankan tipe yang sudah dimiliki bidang, text untuk yang baru) atau objek dengan key, type dan, untuk select, options (1 hingga 100 opsi unik, masing-masing hingga 64 karakter). Hingga 50 bidang; kunci menggunakan huruf kecil, angka, dan garis bawah serta dimulai dengan huruf atau garis bawah.
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" }
]
}'
Mengubah tipe bidang tidak menulis ulang nilai yang sudah disimpan pada kontak; nilai tersebut divalidasi saat berikutnya ditulis.
Kesalahan validasi
Pembuatan atau pembaruan kontak yang nilai bidang kustomnya tidak sesuai dengan tipenya akan ditolak secara keseluruhan dengan 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)." }
]
}
Pada impor massal, data dengan nilai yang salah akan dilaporkan gagal dengan pesan yang sama dan sisa batch lainnya akan tetap diproses.
Daftar
Daftar mengelompokkan kontak. Daftar bisa berupa statis (Anda yang menentukan siapa yang ada di dalamnya) atau cerdas (keanggotaan dihitung berdasarkan aturan dan diperbarui secara otomatis — lihat Mengatur Daftar & Kontak).
| Bidang | Deskripsi |
|---|---|
name |
Wajib diisi saat pembuatan. Maksimal 100 karakter. |
status |
live (default) atau draft. Huruf kecil. |
contact_ids |
Larik ID kontak untuk dimasukkan ke dalam daftar. Hanya untuk daftar statis. |
type |
static (default) atau smart. |
smart_rules |
Kumpulan aturan — wajib diisi saat type adalah smart. Lihat di bawah. |
Membuat daftar
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" } }
]
}
}'
Respons
{
"success": true,
"list_id": "list_abc123",
"evaluation": { "added": 3, "removed": 0, "total": 3 }
}
Daftar cerdas dievaluasi secara inline, dalam permintaan yang sama, sehingga evaluation memberi tahu Anda dengan tepat siapa saja yang masuk ke dalamnya. Pada daftar statis, evaluation adalah null.
Memperbarui daftar
PUT /lists/{listId}
Kirim hanya bidang yang ingin Anda ubah. Mengubah smart_rules akan mengevaluasi ulang daftar secara langsung dan mengembalikan objek evaluation yang sama.
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"] } ] } }'
Anda dapat mengubah jenis daftar di antara keduanya:
- Statis → cerdas: kirim
{ "type": "smart", "smart_rules": { … } }. Aturan akan langsung diterapkan. - Cerdas → statis: kirim
{ "type": "static" }. Aturan akan dihapus dan siapa pun yang ada di dalam daftar akan tetap berada di sana.
Bentuk 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(setiap kondisi harus benar) atauany(setidaknya satu).conditions— 1 hingga 20 kondisi, masing-masing maksimal 100 nilai, string hingga 200 karakter.
field |
op |
value |
|---|---|---|
tags |
has_any, has_all, has_none |
larik ID tag |
lists |
in_any, not_in_any |
larik ID daftar (hanya daftar statis — daftar cerdas tidak dapat dibuat dari daftar cerdas lainnya) |
channel |
is_any, is_none |
larik saluran |
status |
is_any, is_none |
larik status kontak |
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" } |
| kolom tanggal yang sama | before, after |
tanggal ISO ("2026-01-01", dibandingkan sebagai hari penuh) atau tanggal-waktu ISO lengkap ("2026-01-01T14:30:00Z", dibandingkan hingga momen yang tepat) |
| kolom tanggal yang sama | is_set, not_set |
— |
has_interacted_with_ai |
is |
true / false — true mencocokkan kontak yang pernah dikirimi pesan oleh AI setidaknya sekali (kapan pun) |
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 |
string untuk formulir contains |
current_campaign_id, assigned_agent |
is_any, is_none, is_set, not_set |
larik ID untuk formulir is_any / is_none |
custom_field (ditambah key) |
eq, neq, contains, not_contains, is_set, not_set |
string untuk formulir nilai |
not_within_last juga mencocokkan kontak yang tanggalnya tidak pernah ditetapkan (“lebih dari N yang lalu, atau tidak pernah”), dan perbandingan teks mengabaikan huruf besar/kecil.
Keterlibatan AI. has_interacted_with_ai adalah penanda seumur hidup: true untuk setiap kontak yang telah dikirimi setidaknya satu pesan oleh AI Anda, false untuk yang lainnya (termasuk kontak yang hanya pernah dijawab oleh tim Anda). Penanda ini dibubuhkan pada pesan pertama AI ke kontak dan tidak pernah dihapus, jadi mematikan balasan AI untuk kontak tersebut atau memindahkan mereka ke kampanye lain tidak akan meresetnya. Untuk periode — “kontak yang ditangani AI saya bulan ini”, pertanyaan penagihan yang umum — gunakan rentang last_ai_interaction_at sebagai gantinya:
{ "field": "last_ai_interaction_at", "op": "within_last", "value": { "amount": 30, "unit": "days" } }
Jangan tertukar dengan is_bot_active (AI diizinkan untuk membalas, bukan berarti sudah membalas) atau has_ever_responded (kontak membalas, kepada siapa pun). Kedua penanda yang sama dikembalikan pada setiap kontak sebagai first_ai_interaction_at / last_ai_interaction_at, dan seluruh rangkaian aturan juga berfungsi pada GET /contacts?rules=, sehingga Anda dapat menghitung kecocokan tanpa membuat daftar.
Pratinjau kumpulan aturan
POST /lists/preview
Menghitung dan mengambil sampel kontak yang akan dicocokkan oleh kumpulan aturan, tanpa membuat atau mengubah apa pun. Gunakan ini untuk memeriksa kewajaran aturan sebelum Anda menyimpannya.
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"] } ] } }'
Respons
{
"success": true,
"count": 3,
"sample": [
{
"id": "contact_abc123",
"first_name": "Sofia",
"last_name": "Martinez",
"phone_number": "+31600000000",
"email": "sofia@example.com",
"channel": "whatsapp"
}
]
}
sample menampung hingga 10 kontak, dengan yang paling baru aktif di urutan pertama.
Jalankan ulang daftar cerdas sekarang
POST /lists/{listId}/evaluate
Memaksa evaluasi ulang segera (sama dengan fungsi Segarkan sekarang di dasbor). Daftar cerdas sudah diperbarui saat kontak berubah, dan setiap 15 menit untuk aturan berbasis waktu, jadi ini hanya diperlukan saat Anda menginginkan hasilnya saat ini juga.
Respons
{
"success": true,
"list_id": "list_abc123",
"evaluation": { "added": 2, "removed": 1, "total": 4 }
}
evaluation.skipped: true berarti evaluasi lain dari daftar yang sama sudah berjalan dan panggilan ini tidak melakukan apa pun.
Daftar cerdas menolak anggota yang dipilih secara manual
Endpoint keanggotaan mengembalikan 409 dengan "This is a smart list — its members are computed from its rules. Edit the rules instead." saat daftar target adalah daftar cerdas. Ini mencakup POST /contacts/lists, DELETE /contacts/lists, POST /contacts/lists/batch, contact_ids pada POST /lists dan PUT /lists/{listId}, serta memilih daftar cerdas sebagai target impor CSV. Ubah aturannya sebagai gantinya.
Memanggil POST /lists/{listId}/evaluate pada daftar statis juga merupakan 409 — daftar tersebut tidak memiliki aturan untuk dijalankan.
Kesalahan API Kontak
Endpoint kontak mengembalikan amplop error standar:
{
"success": false,
"error": "Contact not found"
}
Beberapa endpoint juga menyertakan error_code, yang biasanya cocok dengan status HTTP — satu-satunya pengecualian adalah kasus kontak duplikat di bawah, di mana status HTTP adalah 200 dan hanya error_code yang membawa 409. Kode yang spesifik untuk endpoint kontak:
| Kode | Kapan ini terjadi pada endpoint kontak |
|---|---|
400 |
Permintaan buruk — kolom yang hilang/tidak valid, isi kosong, kursor buruk, atau lebih dari 500 ID dalam satu batch. |
402 |
Kredit tidak cukup untuk menyelesaikan proses penandaan AI pada satu kontak (error_code: "insufficient_credits"). |
404 |
Kontak, daftar, atau tag tidak ditemukan di akun Anda. |
409 |
Kontak dengan nomor telepon tersebut sudah ada (saat pembuatan). Dikembalikan sebagai error_code di dalam isi dengan status HTTP 200, jadi buat percabangan pada error_code di sini. Juga dikembalikan saat proses penandaan otomatis massal sedang berlangsung (error_code: "auto_tag_run_in_progress"), atau saat menautkan kontak ke saluran lain yang akan menggabungkan dua kontak yang sudah ditautkan ke dua orang berbeda. |
422 |
Kontak tidak dapat menerima pesan saat ini (jangan ganggu, pribadi, atau saluran yang tidak didukung). Pada endpoint penautan saluran, ini juga mencakup tidak ada nomor telepon, pemasangan saluran yang tidak didukung, atau tidak ada pengirim yang terhubung untuk saluran target. |
403 pada endpoint kontak juga dapat berarti masalah batas kontak atau izin daftar, bukan akses paket. Kode bersama yang dapat dikembalikan oleh setiap endpoint — 401, 403 (paket Anda tidak menyertakan akses API), 429 (batas kecepatan), dan 500 — tercantum beserta panduan percobaan ulang di Kesalahan & Penomoran Halaman.
Langkah berikutnya
- API Pesan — mengirim pesan berdasarkan identitas saluran dan mengelola percakapan.
- Referensi API — daftar endpoint lengkap, termasuk tag dan daftar.
- Akses API — autentikasi, batas kecepatan, dan penanganan error.