Contacts
Write many contacts in one request
Runs up to 1000 operations. Each operation answers for itself.
The status code describes the request, not the rows. A request that
Kai understands returns 200, even when every operation in it failed.
Read summary and results to see what happened.
Kai runs the operations in order. Two operations that name the same person are applied one after the other.
POST
/
contacts
/
batch
Write many contacts in one request
curl --request POST \
--url https://kaisupport.com/api/v1/contacts/batch \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"operations": [
{
"op": "upsert",
"data": {
"external_id": "DK-1042",
"name": "Ayşe Yılmaz",
"attributes": {
"package": "Tam Destek"
}
}
},
{
"op": "assign",
"external_id": "DK-1043",
"owner": "baran@example.com"
},
{
"op": "delete",
"id": "8b2f1c40-1f2e-4a9d-9d61-0c4f5a6b7c88"
}
]
}
'import requests
url = "https://kaisupport.com/api/v1/contacts/batch"
payload = { "operations": [
{
"op": "upsert",
"data": {
"external_id": "DK-1042",
"name": "Ayşe Yılmaz",
"attributes": { "package": "Tam Destek" }
}
},
{
"op": "assign",
"external_id": "DK-1043",
"owner": "baran@example.com"
},
{
"op": "delete",
"id": "8b2f1c40-1f2e-4a9d-9d61-0c4f5a6b7c88"
}
] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
operations: [
{
op: 'upsert',
data: {
external_id: 'DK-1042',
name: 'Ayşe Yılmaz',
attributes: {package: 'Tam Destek'}
}
},
{op: 'assign', external_id: 'DK-1043', owner: 'baran@example.com'},
{op: 'delete', id: '8b2f1c40-1f2e-4a9d-9d61-0c4f5a6b7c88'}
]
})
};
fetch('https://kaisupport.com/api/v1/contacts/batch', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://kaisupport.com/api/v1/contacts/batch",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'operations' => [
[
'op' => 'upsert',
'data' => [
'external_id' => 'DK-1042',
'name' => 'Ayşe Yılmaz',
'attributes' => [
'package' => 'Tam Destek'
]
]
],
[
'op' => 'assign',
'external_id' => 'DK-1043',
'owner' => 'baran@example.com'
],
[
'op' => 'delete',
'id' => '8b2f1c40-1f2e-4a9d-9d61-0c4f5a6b7c88'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://kaisupport.com/api/v1/contacts/batch"
payload := strings.NewReader("{\n \"operations\": [\n {\n \"op\": \"upsert\",\n \"data\": {\n \"external_id\": \"DK-1042\",\n \"name\": \"Ayşe Yılmaz\",\n \"attributes\": {\n \"package\": \"Tam Destek\"\n }\n }\n },\n {\n \"op\": \"assign\",\n \"external_id\": \"DK-1043\",\n \"owner\": \"baran@example.com\"\n },\n {\n \"op\": \"delete\",\n \"id\": \"8b2f1c40-1f2e-4a9d-9d61-0c4f5a6b7c88\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://kaisupport.com/api/v1/contacts/batch")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"operations\": [\n {\n \"op\": \"upsert\",\n \"data\": {\n \"external_id\": \"DK-1042\",\n \"name\": \"Ayşe Yılmaz\",\n \"attributes\": {\n \"package\": \"Tam Destek\"\n }\n }\n },\n {\n \"op\": \"assign\",\n \"external_id\": \"DK-1043\",\n \"owner\": \"baran@example.com\"\n },\n {\n \"op\": \"delete\",\n \"id\": \"8b2f1c40-1f2e-4a9d-9d61-0c4f5a6b7c88\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://kaisupport.com/api/v1/contacts/batch")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"operations\": [\n {\n \"op\": \"upsert\",\n \"data\": {\n \"external_id\": \"DK-1042\",\n \"name\": \"Ayşe Yılmaz\",\n \"attributes\": {\n \"package\": \"Tam Destek\"\n }\n }\n },\n {\n \"op\": \"assign\",\n \"external_id\": \"DK-1043\",\n \"owner\": \"baran@example.com\"\n },\n {\n \"op\": \"delete\",\n \"id\": \"8b2f1c40-1f2e-4a9d-9d61-0c4f5a6b7c88\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"summary": {
"total": 3,
"created": 1,
"updated": 0,
"deleted": 1,
"assigned": 1,
"errors": 0
},
"results": [
{
"index": 0,
"op": "upsert",
"status": "created",
"id": "4f1d6f2a-6a1e-4a0b-9c53-2a9a1f0c77d1"
},
{
"index": 1,
"op": "assign",
"status": "assigned",
"id": "7d2b8e11-3c4a-4f5b-8a9c-2e1d0f3a4b56"
},
{
"index": 2,
"op": "delete",
"status": "deleted",
"id": "8b2f1c40-1f2e-4a9d-9d61-0c4f5a6b7c88"
}
]
}{
"error": {
"type": "invalid_request",
"message": "Send at least one of \"external_id\", \"email\", \"phone\" or \"identities\" so the contact can be matched."
}
}{
"error": {
"type": "unauthorized",
"message": "That API key is not valid."
}
}{
"error": {
"type": "forbidden",
"message": "This key does not have the \"contacts:write\" permission."
}
}Authorizations
Send your API key as Authorization: Bearer kai_live_....
Make and revoke keys in Kai, under Settings → API. Kai stores only a hash of the key, so the full key is shown one time.
Body
application/json
Required array length:
1 - 1000 elementsShow child attributes
Show child attributes
⌘I
Write many contacts in one request
curl --request POST \
--url https://kaisupport.com/api/v1/contacts/batch \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"operations": [
{
"op": "upsert",
"data": {
"external_id": "DK-1042",
"name": "Ayşe Yılmaz",
"attributes": {
"package": "Tam Destek"
}
}
},
{
"op": "assign",
"external_id": "DK-1043",
"owner": "baran@example.com"
},
{
"op": "delete",
"id": "8b2f1c40-1f2e-4a9d-9d61-0c4f5a6b7c88"
}
]
}
'import requests
url = "https://kaisupport.com/api/v1/contacts/batch"
payload = { "operations": [
{
"op": "upsert",
"data": {
"external_id": "DK-1042",
"name": "Ayşe Yılmaz",
"attributes": { "package": "Tam Destek" }
}
},
{
"op": "assign",
"external_id": "DK-1043",
"owner": "baran@example.com"
},
{
"op": "delete",
"id": "8b2f1c40-1f2e-4a9d-9d61-0c4f5a6b7c88"
}
] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
operations: [
{
op: 'upsert',
data: {
external_id: 'DK-1042',
name: 'Ayşe Yılmaz',
attributes: {package: 'Tam Destek'}
}
},
{op: 'assign', external_id: 'DK-1043', owner: 'baran@example.com'},
{op: 'delete', id: '8b2f1c40-1f2e-4a9d-9d61-0c4f5a6b7c88'}
]
})
};
fetch('https://kaisupport.com/api/v1/contacts/batch', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://kaisupport.com/api/v1/contacts/batch",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'operations' => [
[
'op' => 'upsert',
'data' => [
'external_id' => 'DK-1042',
'name' => 'Ayşe Yılmaz',
'attributes' => [
'package' => 'Tam Destek'
]
]
],
[
'op' => 'assign',
'external_id' => 'DK-1043',
'owner' => 'baran@example.com'
],
[
'op' => 'delete',
'id' => '8b2f1c40-1f2e-4a9d-9d61-0c4f5a6b7c88'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://kaisupport.com/api/v1/contacts/batch"
payload := strings.NewReader("{\n \"operations\": [\n {\n \"op\": \"upsert\",\n \"data\": {\n \"external_id\": \"DK-1042\",\n \"name\": \"Ayşe Yılmaz\",\n \"attributes\": {\n \"package\": \"Tam Destek\"\n }\n }\n },\n {\n \"op\": \"assign\",\n \"external_id\": \"DK-1043\",\n \"owner\": \"baran@example.com\"\n },\n {\n \"op\": \"delete\",\n \"id\": \"8b2f1c40-1f2e-4a9d-9d61-0c4f5a6b7c88\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://kaisupport.com/api/v1/contacts/batch")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"operations\": [\n {\n \"op\": \"upsert\",\n \"data\": {\n \"external_id\": \"DK-1042\",\n \"name\": \"Ayşe Yılmaz\",\n \"attributes\": {\n \"package\": \"Tam Destek\"\n }\n }\n },\n {\n \"op\": \"assign\",\n \"external_id\": \"DK-1043\",\n \"owner\": \"baran@example.com\"\n },\n {\n \"op\": \"delete\",\n \"id\": \"8b2f1c40-1f2e-4a9d-9d61-0c4f5a6b7c88\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://kaisupport.com/api/v1/contacts/batch")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"operations\": [\n {\n \"op\": \"upsert\",\n \"data\": {\n \"external_id\": \"DK-1042\",\n \"name\": \"Ayşe Yılmaz\",\n \"attributes\": {\n \"package\": \"Tam Destek\"\n }\n }\n },\n {\n \"op\": \"assign\",\n \"external_id\": \"DK-1043\",\n \"owner\": \"baran@example.com\"\n },\n {\n \"op\": \"delete\",\n \"id\": \"8b2f1c40-1f2e-4a9d-9d61-0c4f5a6b7c88\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"summary": {
"total": 3,
"created": 1,
"updated": 0,
"deleted": 1,
"assigned": 1,
"errors": 0
},
"results": [
{
"index": 0,
"op": "upsert",
"status": "created",
"id": "4f1d6f2a-6a1e-4a0b-9c53-2a9a1f0c77d1"
},
{
"index": 1,
"op": "assign",
"status": "assigned",
"id": "7d2b8e11-3c4a-4f5b-8a9c-2e1d0f3a4b56"
},
{
"index": 2,
"op": "delete",
"status": "deleted",
"id": "8b2f1c40-1f2e-4a9d-9d61-0c4f5a6b7c88"
}
]
}{
"error": {
"type": "invalid_request",
"message": "Send at least one of \"external_id\", \"email\", \"phone\" or \"identities\" so the contact can be matched."
}
}{
"error": {
"type": "unauthorized",
"message": "That API key is not valid."
}
}{
"error": {
"type": "forbidden",
"message": "This key does not have the \"contacts:write\" permission."
}
}