Create or replace object relationships in bulk
/data_objects/object_relationships
Use this endpoint to create or replace up to 50 object-to-object relationships in one request.

Data Objects is generally available, but your workspace must be provisioned for Accounts before you can use this endpoint. To request provisioning, go to Data Settings > Accounts and complete the questionnaire. For details, see Enable Accounts.
Braze accepts the request and processes it in the background. A 202 response means the batch is queued, not applied. To change one relationship and see the change right away, use Replace object relationship. For when to use each, see Bulk endpoints.
Prerequisites
To use this endpoint, you need an API key with data_objects.object_relationships.bulk_upsert. The single-record permissions, such as data_objects.object_relationships.create, don’t grant access to this endpoint.
Rate limit
This endpoint is in the Data Objects bulk write bucket with a default limit of 1,000 requests per minute per workspace. The limit is shared across all six bulk endpoints and with Cloud Data Ingestion syncs of data objects, and is separate from the single-record write limit.
Request parameters
The following table lists and describes the JSON request body parameters for the /data_objects/object_relationships endpoint.
| Parameter | Required | Data Type | Description |
|---|---|---|---|
records |
Required | Array | The object relationships to create or replace. Must contain 1 to 50 records. Braze rejects any other top-level field. |
records[].type_name |
Required | String | Type of the source object |
records[].object_id |
Required | String | Identifier of the source object |
records[].rel_kind |
Required | String | Name of the relationship type. To list valid values, use List object relationship types. |
records[].related_type_name |
Required | String | Type of the target object |
records[].related_object_id |
Required | String | Identifier of the target object |
records[].attributes |
Required | Object | Relationship attributes, keyed by field name. Send {} if the relationship type has none. |
How Braze processes the batch
- Validation is all-or-nothing. If any record fails validation, Braze rejects the whole request with
400, stores nothing, and lists every error. Fix the errors and resend the whole request. - Lookups happen during processing. Braze checks whether the types, relationship type, and objects exist, and whether the attributes match the relationship type’s fields, when it processes the batch. A request that returns
202can still produce failures for individual records later. See Processing failures. - Records are ordered by receipt time. Braze stamps every record with the time it received the request, so don’t send
updated_at. Braze rejects a request that includes it. When two requests change the same relationship, the one Braze received later wins, and Braze skips an older write that arrives afterward. - Relationships can wait on their objects. A relationship whose source or target object doesn’t exist yet waits instead of failing right away. Braze retries for about 40 hours (10 attempts). If the object still doesn’t exist after that, the record fails with
data-object-dependency-not-found.
Example request
This section includes a sample JSON payload and a sample cURL request.
Sample request payload
{
"records": [
{
"type_name": "contact",
"object_id": "contact-001",
"rel_kind": "works_at",
"related_type_name": "account",
"related_object_id": "acct-001",
"attributes": {"since": "2024-01-15T00:00:00Z"}
}
]
}
Sample cURL request
This example links contact-001 to acct-001 with the works_at relationship type and records when the relationship started.
curl --location --request PUT 'https://rest.iad-01.braze.com/data_objects/object_relationships' \
--header 'Authorization: Bearer YOUR_REST_API_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
"records": [
{
"type_name": "contact",
"object_id": "contact-001",
"rel_kind": "works_at",
"related_type_name": "account",
"related_object_id": "acct-001",
"attributes": {"since": "2024-01-15T00:00:00Z"}
}
]
}'
Response
This section includes a sample successful response and the response fields.
Example success response
The status code 202 could return the following response body.
{
"source_import_id": "api-20261022T153012123456-9f3a1c2b",
"status": "queued",
"record_count": 1
}
Response parameters
The following table lists and describes the fields in a successful response.
| Parameter | Required | Data Type | Description |
|---|---|---|---|
source_import_id |
Required | String | The identifier Braze generated for this request. Every request is its own import. |
status |
Required | String | Always queued. The batch is accepted and waiting to be processed. |
record_count |
Required | Integer | The number of records in the request |
Processing failures
Braze doesn’t return these codes from the endpoint. It records them for individual records when it processes a queued batch.
| Code | Meaning |
|---|---|
data-object-type-not-found |
type_name or related_type_name names no active type in the workspace. |
data-object-relationship-type-not-found |
rel_kind names no active relationship type for these types. |
data-object-schema-mismatch |
attributes don’t match the relationship type’s fields. The message names every attribute at fault. |
data-object-ingestion-rejected |
Any other rejection. |
data-object-dependency-not-found |
The source or target object was still missing after about 40 hours of retries. |
Errors
A 400 response means Braze stored nothing. Each error has an id, a message, the parameter at fault when there is one, and, for record-level errors, the record’s index (its 0-based position in records).
{
"errors": [
{"index": 0, "id": "schema-mismatch", "parameter": "rel_kind", "message": "rel_kind is required"}
]
}
The following table lists common errors for this endpoint and how to resolve them.
| Status | Cause | Guidance |
|---|---|---|
400 |
Invalid request (invalid-request): the body isn’t a JSON object or is malformed JSON, records is missing or has fewer than 1 or more than 50 entries, or the body contains a field Braze sets (source_type, source_import_id, source_chunk_id, or record_type) |
Send a JSON object whose only field is records, with 1 to 50 records. |
400 |
Schema mismatch (schema-mismatch): a record is missing a required field, has an unrecognized field (including updated_at), or has a field of the wrong type or an empty string |
Fix every listed record and resend the whole request. |
401 |
Missing or invalid REST API key | Verify the Authorization header uses Bearer YOUR_REST_API_KEY and that the key is active. |
403 |
API key lacks permission or request is blocked by allowlist | Confirm the key has data_objects.object_relationships.bulk_upsert and that your source IP is on the key allowlist, if configured. |
404 |
Data Objects isn’t enabled for your company | Confirm your workspace is provisioned for Accounts. |
429 |
Rate limit exceeded | Retry after X-RateLimit-Reset and reduce request frequency. |
500 |
The batch couldn’t be accepted (internal-error) |
Braze accepted nothing, so it’s safe to retry the request. |