Records
Update a CRM Record Relation
Add or remove individual record and team-member links on a relation or user attribute of a Zeeg CRM record without overwriting existing linked IDs
PATCH
/
crm
/
{objectSlug}
/
{recordId}
/
relation
Update a CRM record relation
curl --request PATCH \
--url https://api.zeeg.me/v2/crm/{objectSlug}/{recordId}/relation \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"attributeSlug": "products"
}
'import requests
url = "https://api.zeeg.me/v2/crm/{objectSlug}/{recordId}/relation"
payload = { "attributeSlug": "products" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({attributeSlug: 'products'})
};
fetch('https://api.zeeg.me/v2/crm/{objectSlug}/{recordId}/relation', 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://api.zeeg.me/v2/crm/{objectSlug}/{recordId}/relation",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'attributeSlug' => 'products'
]),
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://api.zeeg.me/v2/crm/{objectSlug}/{recordId}/relation"
payload := strings.NewReader("{\n \"attributeSlug\": \"products\"\n}")
req, _ := http.NewRequest("PATCH", 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.patch("https://api.zeeg.me/v2/crm/{objectSlug}/{recordId}/relation")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"attributeSlug\": \"products\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.zeeg.me/v2/crm/{objectSlug}/{recordId}/relation")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"attributeSlug\": \"products\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"status": 200,
"record": {
"id": "c1d2e3f4-a5b6-7890-cdef-123456789012",
"objectSlug": "products",
"attributes": {
"sku": "DRESS-001",
"price": 29,
"inventory_count": 150
},
"createdAt": "2025-06-01T10:00:00+00:00",
"updatedAt": "2025-06-01T12:00:00+00:00"
}
}{
"message": "Unauthenticated."
}{
"error": {
"type": "insufficient_scope",
"message": "Your token is missing the required scope: crm:write.",
"error_id": "4d13a763-2e44-43db-bed2-4d758ea659bb",
"required_scopes": [
"crm:write"
]
}
}{
"success": false,
"message": "Record not found.",
"status": 404
}{
"message": "The given data was invalid.",
"errors": {
"field_name": [
"The field_name field is required."
]
}
}Adds or removes individual IDs from a
Rules:
Passing this endpoint for a
Use
relation or user attribute on a CRM record. Unlike Patch, which replaces the entire array, this endpoint performs surgical add/remove operations — existing linked IDs not mentioned in the request are untouched.
Path parameters
| Parameter | Type | Description |
|---|---|---|
objectSlug | string | The slug of the custom CRM object the record belongs to. |
recordId | string (UUID) | The UUID of the record whose relation attribute you want to modify. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
attributeSlug | string | Yes | The key of the relation or user attribute to modify. Must exist on the object and be of type relation or user. |
add | string[] | No | IDs to add to the relation. For relation attributes: UUIDs of records in the related object. For user attributes: UUIDs of organization members. |
remove | string[] | No | IDs to remove from the relation. The same ID format as add. |
- At least one of
addorremovemust be present and non-empty. - The same ID cannot appear in both
addandremovein the same request. - Passing an ID in
addthat is already linked is a no-op (not an error). - Passing an ID in
removethat is not linked is a no-op (not an error).
Attribute types that support this endpoint
| Attribute type | What IDs to pass |
|---|---|
relation | UUIDs of records from the related object (obtained from GET /v2/crm/{relatedObjectSlug}) |
user | UUIDs of organization members (obtained from GET /v2/organizations/users) |
text, number, select, or any non-relation attribute returns an error. Use Patch for those.
Example: link products to an order
Yourorders object has a relation attribute called products that links to the products object. To add two products to an order:
PATCH /v2/crm/orders/a1b2c3d4-e5f6-7890-abcd-ef1234567890/relation
{
"attributeSlug": "products",
"add": [
"c1d2e3f4-a5b6-7890-cdef-123456789012",
"d2e3f4a5-b6c7-8901-defa-234567890123"
]
}
Example: swap a linked record
Remove one product and add a replacement in a single request:{
"attributeSlug": "products",
"add": ["new-product-uuid-here"],
"remove": ["old-product-uuid-here"]
}
Example: unlink all products one at a time
To remove products, pass their IDs inremove:
{
"attributeSlug": "products",
"remove": ["c1d2e3f4-a5b6-7890-cdef-123456789012"]
}
Example: assign a team member to a deal
Yourdeals object has a user attribute called owners. To assign a team member:
PATCH /v2/crm/deals/deal-uuid-here/relation
{
"attributeSlug": "owners",
"add": ["org-member-uuid-here"]
}
Finding the attribute slug
Relation attribute slugs are defined when the attribute is created. You can look them up viaGET /v2/crm/objects/{slug} — look for attributes with type: relation or type: user and use their key field as attributeSlug.
Update Relation vs. PATCH
PATCH /relation | PATCH /{recordId} | |
|---|---|---|
| Operation | Adds/removes individual IDs | Replaces the entire attribute value |
| Existing IDs | Preserved unless explicitly removed | Overwritten |
| Use case | Add or remove one or a few links | Set the complete list in one go |
PATCH /relation when you need additive or subtractive changes. Use PATCH /{recordId} when you want to set the exact final state of a relation (replacing whatever was there before).
When to use this endpoint
- Add a linked record — a user picks a related product, company, or person to attach.
- Remove a linked record — a user detaches a relation without affecting others.
- Assign / unassign owners — add or remove team members from a user-type attribute.
- Incremental sync — when your source system sends delta events (“product X was added to order Y”) rather than full state.
Authorizations
Path Parameters
Slug of the custom CRM object.
UUID of the record to update.
Body
application/json
Response
OK
Example:
true
Example:
200
A record for a custom CRM object.
Hide child attributes
Hide child attributes
Unique identifier for the record.
Example:
"c1d2e3f4-a5b6-7890-cdef-123456789012"
Slug of the CRM object this record belongs to.
Example:
"products"
Key/value pairs for the custom attributes defined on the object. Keys are attribute slugs.
Example:
{ "sku": "DRESS-001", "price": 29, "inventory_count": 150 }
ISO 8601 timestamp when the record was created.
Example:
"2025-06-01T10:00:00+00:00"
ISO 8601 timestamp when the record was last updated.
Example:
"2025-06-01T12:00:00+00:00"
Last modified on June 11, 2026
⌘I
Update a CRM record relation
curl --request PATCH \
--url https://api.zeeg.me/v2/crm/{objectSlug}/{recordId}/relation \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"attributeSlug": "products"
}
'import requests
url = "https://api.zeeg.me/v2/crm/{objectSlug}/{recordId}/relation"
payload = { "attributeSlug": "products" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({attributeSlug: 'products'})
};
fetch('https://api.zeeg.me/v2/crm/{objectSlug}/{recordId}/relation', 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://api.zeeg.me/v2/crm/{objectSlug}/{recordId}/relation",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'attributeSlug' => 'products'
]),
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://api.zeeg.me/v2/crm/{objectSlug}/{recordId}/relation"
payload := strings.NewReader("{\n \"attributeSlug\": \"products\"\n}")
req, _ := http.NewRequest("PATCH", 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.patch("https://api.zeeg.me/v2/crm/{objectSlug}/{recordId}/relation")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"attributeSlug\": \"products\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.zeeg.me/v2/crm/{objectSlug}/{recordId}/relation")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"attributeSlug\": \"products\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"status": 200,
"record": {
"id": "c1d2e3f4-a5b6-7890-cdef-123456789012",
"objectSlug": "products",
"attributes": {
"sku": "DRESS-001",
"price": 29,
"inventory_count": 150
},
"createdAt": "2025-06-01T10:00:00+00:00",
"updatedAt": "2025-06-01T12:00:00+00:00"
}
}{
"message": "Unauthenticated."
}{
"error": {
"type": "insufficient_scope",
"message": "Your token is missing the required scope: crm:write.",
"error_id": "4d13a763-2e44-43db-bed2-4d758ea659bb",
"required_scopes": [
"crm:write"
]
}
}{
"success": false,
"message": "Record not found.",
"status": 404
}{
"message": "The given data was invalid.",
"errors": {
"field_name": [
"The field_name field is required."
]
}
}