Upsert
curl --request PUT \
--url http://api.gu1.ai/entities/upsert \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"entity": {},
"options": {},
"options.conflictResolution": {},
"options.deduplicationStrategy": {},
"options.createRelationships": true
}
'import requests
url = "http://api.gu1.ai/entities/upsert"
payload = {
"entity": {},
"options": {},
"options.conflictResolution": {},
"options.deduplicationStrategy": {},
"options.createRelationships": True
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
entity: {},
options: {},
'options.conflictResolution': {},
'options.deduplicationStrategy': {},
'options.createRelationships': true
})
};
fetch('http://api.gu1.ai/entities/upsert', 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 => "http://api.gu1.ai/entities/upsert",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'entity' => [
],
'options' => [
],
'options.conflictResolution' => [
],
'options.deduplicationStrategy' => [
],
'options.createRelationships' => true
]),
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 := "http://api.gu1.ai/entities/upsert"
payload := strings.NewReader("{\n \"entity\": {},\n \"options\": {},\n \"options.conflictResolution\": {},\n \"options.deduplicationStrategy\": {},\n \"options.createRelationships\": true\n}")
req, _ := http.NewRequest("PUT", 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.put("http://api.gu1.ai/entities/upsert")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"entity\": {},\n \"options\": {},\n \"options.conflictResolution\": {},\n \"options.deduplicationStrategy\": {},\n \"options.createRelationships\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("http://api.gu1.ai/entities/upsert")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"entity\": {},\n \"options\": {},\n \"options.conflictResolution\": {},\n \"options.deduplicationStrategy\": {},\n \"options.createRelationships\": true\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"action": "<string>",
"entity": {},
"previousEntity": {},
"confidence": 123,
"reasoning": "<string>",
"conflicts": [
{}
]
}API Reference
Upsert a person entity
Create or update a person entity in gu1 with duplicate detection on external ID, national ID, email, and phone to avoid duplicate KYC records.
PUT
/
entities
/
upsert
Upsert
curl --request PUT \
--url http://api.gu1.ai/entities/upsert \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"entity": {},
"options": {},
"options.conflictResolution": {},
"options.deduplicationStrategy": {},
"options.createRelationships": true
}
'import requests
url = "http://api.gu1.ai/entities/upsert"
payload = {
"entity": {},
"options": {},
"options.conflictResolution": {},
"options.deduplicationStrategy": {},
"options.createRelationships": True
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
entity: {},
options: {},
'options.conflictResolution': {},
'options.deduplicationStrategy': {},
'options.createRelationships': true
})
};
fetch('http://api.gu1.ai/entities/upsert', 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 => "http://api.gu1.ai/entities/upsert",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'entity' => [
],
'options' => [
],
'options.conflictResolution' => [
],
'options.deduplicationStrategy' => [
],
'options.createRelationships' => true
]),
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 := "http://api.gu1.ai/entities/upsert"
payload := strings.NewReader("{\n \"entity\": {},\n \"options\": {},\n \"options.conflictResolution\": {},\n \"options.deduplicationStrategy\": {},\n \"options.createRelationships\": true\n}")
req, _ := http.NewRequest("PUT", 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.put("http://api.gu1.ai/entities/upsert")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"entity\": {},\n \"options\": {},\n \"options.conflictResolution\": {},\n \"options.deduplicationStrategy\": {},\n \"options.createRelationships\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("http://api.gu1.ai/entities/upsert")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"entity\": {},\n \"options\": {},\n \"options.conflictResolution\": {},\n \"options.deduplicationStrategy\": {},\n \"options.createRelationships\": true\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"action": "<string>",
"entity": {},
"previousEntity": {},
"confidence": 123,
"reasoning": "<string>",
"conflicts": [
{}
]
}Overview
The upsert endpoint intelligently creates a new person or updates an existing one based on configurable duplicate detection strategies. It automatically handles conflicts and prevents duplicate records using exact matching, fuzzy matching, or AI-powered similarity detection.Endpoint
PUT http://api.gu1.ai/entities/upsert
Authentication
Requires a valid API key in the Authorization header:Authorization: Bearer YOUR_API_KEY
Request Body
object
required
The person data (same structure as Create Person endpoint)
object
Configuration options for upsert behavior
enum
How to handle conflicts when an existing person is found:
source_wins- New data overwrites existing datatarget_wins- Keep existing data, ignore new datamanual_review- Flag for manual review without updatingsmart_merge(default) - Intelligently merge both datasets
enum
Strategy for detecting duplicate persons:
exact_match- Match by externalId and taxId (case-insensitive)fuzzy_match- Similarity matching on name and taxId (80% threshold)ai_similarity- AI-powered semantic similarity detectionhybrid(recommended) - Exact match with fuzzy fallback
boolean
default:"true"
Whether to automatically create relationships between entities
Response
boolean
Indicates if the operation succeeded
string
The action performed:
created or updatedobject
The final person state after upsert
object
The person state before update (null if newly created)
number
Confidence score (0-1) for the duplicate detection match
string
Explanation of why the person was created/updated
array
Array of field-level conflicts detected during merge (if any)
Examples
Simple Upsert (Default Behavior)
curl -X PUT http://api.gu1.ai/entities/upsert \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"entity": {
"type": "person",
"externalId": "customer_12345",
"name": "MarÃa González",
"countryCode": "AR",
"taxId": "20-12345678-9",
"entityData": {
"person": {
"firstName": "MarÃa",
"lastName": "González",
"dateOfBirth": "1985-03-15",
"occupation": "Software Engineer",
"income": 85000
}
}
}
}'
const response = await fetch('http://api.gu1.ai/entities/upsert', {
method: 'PUT',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
entity: {
type: 'person',
externalId: 'customer_12345',
name: 'MarÃa González',
countryCode: 'AR',
taxId: '20-12345678-9',
entityData: {
person: {
firstName: 'MarÃa',
lastName: 'González',
dateOfBirth: '1985-03-15',
occupation: 'Software Engineer',
income: 85000
}
}
}
})
});
const result = await response.json();
console.log(`Action: ${result.action}`); // 'created' or 'updated'
console.log(`Confidence: ${result.confidence}`);
import requests
response = requests.put(
'http://api.gu1.ai/entities/upsert',
headers={
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
json={
'entity': {
'type': 'person',
'externalId': 'customer_12345',
'name': 'MarÃa González',
'countryCode': 'AR',
'taxId': '20-12345678-9',
'entityData': {
'person': {
'firstName': 'MarÃa',
'lastName': 'González',
'dateOfBirth': '1985-03-15',
'occupation': 'Software Engineer',
'income': 85000
}
}
}
}
)
result = response.json()
print(f"Action: {result['action']}")
print(f"Confidence: {result['confidence']}")
Upsert with Fuzzy Matching
curl -X PUT http://api.gu1.ai/entities/upsert \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"entity": {
"type": "person",
"externalId": "customer_new_123",
"name": "Maria Gonzales",
"countryCode": "AR",
"taxId": "20-12345678-9",
"entityData": {
"person": {
"firstName": "Maria",
"lastName": "Gonzales"
}
}
},
"options": {
"deduplicationStrategy": "fuzzy_match",
"conflictResolution": "smart_merge"
}
}'
// Will match "Maria Gonzales" with existing "MarÃa González"
// due to 80%+ similarity threshold
const response = await fetch('http://api.gu1.ai/entities/upsert', {
method: 'PUT',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
entity: {
type: 'person',
externalId: 'customer_new_123',
name: 'Maria Gonzales', // Slight variation in spelling
countryCode: 'AR',
taxId: '20-12345678-9',
entityData: {
person: {
firstName: 'Maria',
lastName: 'Gonzales'
}
}
},
options: {
deduplicationStrategy: 'fuzzy_match',
conflictResolution: 'smart_merge'
}
})
});
const result = await response.json();
console.log(`Matched with confidence: ${result.confidence}`);
console.log(`Reasoning: ${result.reasoning}`);
import requests
# Will match "Maria Gonzales" with existing "MarÃa González"
response = requests.put(
'http://api.gu1.ai/entities/upsert',
headers={
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
json={
'entity': {
'type': 'person',
'externalId': 'customer_new_123',
'name': 'Maria Gonzales', # Slight variation
'countryCode': 'AR',
'taxId': '20-12345678-9',
'entityData': {
'person': {
'firstName': 'Maria',
'lastName': 'Gonzales'
}
}
},
'options': {
'deduplicationStrategy': 'fuzzy_match',
'conflictResolution': 'smart_merge'
}
}
)
result = response.json()
print(f"Matched with confidence: {result['confidence']}")
print(f"Reasoning: {result['reasoning']}")
Response Examples
Created New Person
{
"success": true,
"action": "created",
"entity": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"externalId": "customer_12345",
"type": "person",
"name": "MarÃa González",
...
},
"previousEntity": null,
"confidence": 1.0,
"reasoning": "No existing entity found matching criteria. Created new entity.",
"conflicts": []
}
Updated Existing Person
{
"success": true,
"action": "updated",
"entity": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"externalId": "customer_12345",
"type": "person",
"name": "MarÃa González",
"entityData": {
"person": {
"income": 95000
}
},
...
},
"previousEntity": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"entityData": {
"person": {
"income": 85000
}
},
...
},
"confidence": 1.0,
"reasoning": "Exact match found on externalId. Updated existing entity with smart merge.",
"conflicts": [
{
"field": "entityData.person.income",
"oldValue": 85000,
"newValue": 95000,
"resolution": "source_wins"
}
]
}
Use Cases
Data Import from CRM
// Import customer data from CRM, avoiding duplicates
async function importCustomer(crmData) {
const response = await fetch('http://api.gu1.ai/entities/upsert', {
method: 'PUT',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
entity: {
type: 'person',
externalId: crmData.customerId,
name: crmData.fullName,
countryCode: crmData.country,
taxId: crmData.taxId,
entityData: {
person: {
firstName: crmData.firstName,
lastName: crmData.lastName,
income: crmData.annualIncome
}
},
attributes: {
source: 'crm_import',
importDate: new Date().toISOString()
}
},
options: {
deduplicationStrategy: 'hybrid',
conflictResolution: 'smart_merge'
}
})
});
return response.json();
}
Progressive Data Enrichment
def enrich_person_data(external_id, new_data):
"""Progressively add data to person as it becomes available"""
response = requests.put(
'http://api.gu1.ai/entities/upsert',
headers={
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
json={
'entity': {
'type': 'person',
'externalId': external_id,
'name': new_data.get('name'),
'countryCode': new_data.get('country'),
'entityData': new_data.get('details', {}),
'attributes': new_data.get('attributes', {})
},
'options': {
'deduplicationStrategy': 'exact_match',
'conflictResolution': 'smart_merge' # Merge new with existing
}
}
)
result = response.json()
if result['action'] == 'updated':
print(f"Enriched existing person with new data")
return result
Best Practices
-
Choose the Right Strategy:
exact_matchfor clean, structured data with reliable IDsfuzzy_matchfor user-entered data with potential typoshybridfor most production scenarios
-
Handle Conflicts Gracefully:
- Use
smart_mergefor automatic resolution - Use
manual_reviewfor critical data - Check
conflictsarray in response for important changes
- Use
-
Monitor Confidence Scores:
- Scores below 0.7 may indicate weak matches
- Log low-confidence updates for review
Error Responses
400 Bad Request
{
"error": "Invalid tax ID format for country"
}
500 Internal Server Error
{
"error": "Failed to upsert entity"
}
Next Steps
- List Persons - Query upserted persons
- Update Person - Make targeted updates
- Get Person - Retrieve full person details
Was this page helpful?