Create or update a contact
Matches the contact by identity, then creates it or updates it.
Send at least one identity: external_id, email, phone or an entry
in identities. A request with no identity is refused, because it
would make a new contact each time you send it.
A key that you do not send keeps its stored value. A key set to null
clears the value.
curl --request POST \
--url https://kaisupport.com/api/v1/contacts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"external_id": "DK-1042",
"name": "Ayşe Yılmaz",
"email": "ayse@example.com",
"phone": "+90 532 111 22 33",
"owner": "baran@example.com",
"attributes": {
"package": "Tam Destek",
"exam_year": 2027
}
}
'import requests
url = "https://kaisupport.com/api/v1/contacts"
payload = {
"external_id": "DK-1042",
"name": "Ayşe Yılmaz",
"email": "ayse@example.com",
"phone": "+90 532 111 22 33",
"owner": "baran@example.com",
"attributes": {
"package": "Tam Destek",
"exam_year": 2027
}
}
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({
external_id: 'DK-1042',
name: 'Ayşe Yılmaz',
email: 'ayse@example.com',
phone: '+90 532 111 22 33',
owner: 'baran@example.com',
attributes: {package: 'Tam Destek', exam_year: 2027}
})
};
fetch('https://kaisupport.com/api/v1/contacts', 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",
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([
'external_id' => 'DK-1042',
'name' => 'Ayşe Yılmaz',
'email' => 'ayse@example.com',
'phone' => '+90 532 111 22 33',
'owner' => 'baran@example.com',
'attributes' => [
'package' => 'Tam Destek',
'exam_year' => 2027
]
]),
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"
payload := strings.NewReader("{\n \"external_id\": \"DK-1042\",\n \"name\": \"Ayşe Yılmaz\",\n \"email\": \"ayse@example.com\",\n \"phone\": \"+90 532 111 22 33\",\n \"owner\": \"baran@example.com\",\n \"attributes\": {\n \"package\": \"Tam Destek\",\n \"exam_year\": 2027\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")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"external_id\": \"DK-1042\",\n \"name\": \"Ayşe Yılmaz\",\n \"email\": \"ayse@example.com\",\n \"phone\": \"+90 532 111 22 33\",\n \"owner\": \"baran@example.com\",\n \"attributes\": {\n \"package\": \"Tam Destek\",\n \"exam_year\": 2027\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://kaisupport.com/api/v1/contacts")
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 \"external_id\": \"DK-1042\",\n \"name\": \"Ayşe Yılmaz\",\n \"email\": \"ayse@example.com\",\n \"phone\": \"+90 532 111 22 33\",\n \"owner\": \"baran@example.com\",\n \"attributes\": {\n \"package\": \"Tam Destek\",\n \"exam_year\": 2027\n }\n}"
response = http.request(request)
puts response.read_body{
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"attributes": {},
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"external_id": "<string>",
"name": "<string>",
"email": "<string>",
"phone": "<string>",
"note": "<string>",
"owner": {
"member_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"email": "<string>"
}
}
}{
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"attributes": {},
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"external_id": "<string>",
"name": "<string>",
"email": "<string>",
"phone": "<string>",
"note": "<string>",
"owner": {
"member_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"email": "<string>"
}
}
}{
"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."
}
}{
"error": {
"type": "identity_conflict",
"message": "Those identifiers already belong to two different contacts. Kai does not merge contacts automatically.",
"details": [
{
"kind": "email",
"value": "ayse@example.com"
},
{
"kind": "phone",
"value": "905321112233"
}
]
}
}{
"error": {
"type": "invalid_attributes",
"message": "Some attributes could not be stored.",
"details": [
{
"key": "exam_year",
"message": "expected a number"
},
{
"key": "cohort",
"message": "no such workspace attribute"
}
]
}
}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
A key that you do not send keeps its stored value. A key set to null
clears the value.
Values by API name. Kai refuses an unknown or archived key with a 422 response, and names the key that it refused.
Show child attributes
Show child attributes
A member id, the email of a member, or null to clear the owner.
More handles that identify this person.
Show child attributes
Show child attributes
Response
The contact was found and updated.
Show child attributes
Show child attributes
curl --request POST \
--url https://kaisupport.com/api/v1/contacts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"external_id": "DK-1042",
"name": "Ayşe Yılmaz",
"email": "ayse@example.com",
"phone": "+90 532 111 22 33",
"owner": "baran@example.com",
"attributes": {
"package": "Tam Destek",
"exam_year": 2027
}
}
'import requests
url = "https://kaisupport.com/api/v1/contacts"
payload = {
"external_id": "DK-1042",
"name": "Ayşe Yılmaz",
"email": "ayse@example.com",
"phone": "+90 532 111 22 33",
"owner": "baran@example.com",
"attributes": {
"package": "Tam Destek",
"exam_year": 2027
}
}
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({
external_id: 'DK-1042',
name: 'Ayşe Yılmaz',
email: 'ayse@example.com',
phone: '+90 532 111 22 33',
owner: 'baran@example.com',
attributes: {package: 'Tam Destek', exam_year: 2027}
})
};
fetch('https://kaisupport.com/api/v1/contacts', 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",
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([
'external_id' => 'DK-1042',
'name' => 'Ayşe Yılmaz',
'email' => 'ayse@example.com',
'phone' => '+90 532 111 22 33',
'owner' => 'baran@example.com',
'attributes' => [
'package' => 'Tam Destek',
'exam_year' => 2027
]
]),
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"
payload := strings.NewReader("{\n \"external_id\": \"DK-1042\",\n \"name\": \"Ayşe Yılmaz\",\n \"email\": \"ayse@example.com\",\n \"phone\": \"+90 532 111 22 33\",\n \"owner\": \"baran@example.com\",\n \"attributes\": {\n \"package\": \"Tam Destek\",\n \"exam_year\": 2027\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")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"external_id\": \"DK-1042\",\n \"name\": \"Ayşe Yılmaz\",\n \"email\": \"ayse@example.com\",\n \"phone\": \"+90 532 111 22 33\",\n \"owner\": \"baran@example.com\",\n \"attributes\": {\n \"package\": \"Tam Destek\",\n \"exam_year\": 2027\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://kaisupport.com/api/v1/contacts")
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 \"external_id\": \"DK-1042\",\n \"name\": \"Ayşe Yılmaz\",\n \"email\": \"ayse@example.com\",\n \"phone\": \"+90 532 111 22 33\",\n \"owner\": \"baran@example.com\",\n \"attributes\": {\n \"package\": \"Tam Destek\",\n \"exam_year\": 2027\n }\n}"
response = http.request(request)
puts response.read_body{
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"attributes": {},
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"external_id": "<string>",
"name": "<string>",
"email": "<string>",
"phone": "<string>",
"note": "<string>",
"owner": {
"member_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"email": "<string>"
}
}
}{
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"attributes": {},
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"external_id": "<string>",
"name": "<string>",
"email": "<string>",
"phone": "<string>",
"note": "<string>",
"owner": {
"member_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"email": "<string>"
}
}
}{
"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."
}
}{
"error": {
"type": "identity_conflict",
"message": "Those identifiers already belong to two different contacts. Kai does not merge contacts automatically.",
"details": [
{
"kind": "email",
"value": "ayse@example.com"
},
{
"kind": "phone",
"value": "905321112233"
}
]
}
}{
"error": {
"type": "invalid_attributes",
"message": "Some attributes could not be stored.",
"details": [
{
"key": "exam_year",
"message": "expected a number"
},
{
"key": "cohort",
"message": "no such workspace attribute"
}
]
}
}