An API request can be accepted even when some of its data isn’t written. Some request settings also have defaults that can unexpectedly change user profiles or trigger live journeys.
Use the checks on this page when building and monitoring an Insider One API integration. The examples focus on the Upsert User Data API, but the same principle applies whenever an API accepts a request before completing its processing.
Do not use the HTTP status code as the only measure of integration success. Check the response body and verify the resulting data.
A 200 OK response does not always mean an event was written
A 200 OK response confirms that Insider One accepted a structurally valid request. It does not guarantee that every event in the request was written to a user profile.
One common cause is the event's Time to Live (TTL). Insider One calculates TTL from the event's original timestamp, not from the date on which the API receives the event. If the timestamp falls outside the event's retention period, the event is not written to the User Profile.
For example, assume that a custom event has a TTL of 90 days:
{
"event_name": "store_visit",
"timestamp": "2025-01-01T10:00:00Z"
}If you send this event after its TTL has expired, the request can still receive 200 OK, but the event will not be available on the user's profile.
Before sending historical events:
Check the TTL of each event type.
Compare the event's original
timestampwith the current date.Exclude expired events from the import.
Verify a sample of accepted events on the User Profiles page.
The Attributes and Events ingestion graph can show that Insider One received an expired event. This does not mean that the event was retained on the User Profile.
Always inspect the response body
The Upsert User Data API can return 200 OK with a fail block. This means the request format was accepted, but one or more user records failed validation or processing.
For example:
{
"data": {
"successful": {},
"fail": {
"count": 1,
"errors": {
"users.0.identifiers.required": [
"either insider_id or identifiers must be specified"
]
}
}
}
}Do not mark a request as successful based only on its HTTP status. Your integration should inspect:
data.successful.countfor successfully accepted user records.data.fail.countfor failed user records.data.fail.errorsfor the affected fields and validation messages.
Treat any response with a fail.count greater than 0 as a partial or complete processing failure. Log the error details, correct the invalid data, and retry only the affected records.
A successful response typically resembles:
{
"data": {
"successful": {
"count": 1
},
"fail": {}
}
}Set skip_hook explicitly for historical data
The skip_hook parameter controls whether imported data can trigger active Architect journeys and data streams.
Refer to the table below to understand how it behaves:
Value | Behavior |
|---|---|
| The data can trigger journeys and data streams. |
| The data is written without triggering journeys or data streams. |
Omitted | Treated as |
If you omit skip_hook while importing historical events, those events can trigger live journeys.
For example, suppose an active journey uses the purchase event as its On Event starter. If you import historical purchase events without setting skip_hook to true, eligible users can enter the journey and receive its messages.
Use the following structure for a historical import:
{
"skip_hook": true,
"users": [
{
"identifiers": {
"email": "sample@example.com"
},
"events": [
{
"event_name": "purchase",
"timestamp": "2026-06-10T12:00:00Z",
"event_params": {
"event_group_id": "ORDER-123",
"unit_sale_price": 49.99,
"currency": "USD",
"quantity": 1
}
}
]
}
]
}Set the flag explicitly instead of relying on its default:
Use
"skip_hook": truefor historical imports and backfills that must not trigger live flows.Use
"skip_hook": falseonly when the data should trigger eligible journeys and data streams.
skip_hookcontrols triggers. It does not override TTL or make an expired event eligible for storage.
Put identifiers only in the identifiers object
Fields used to identify a user must be placed inside the identifiers object. Sending the same field only as an attribute does not make it an identifier for the request.
The following structure is incorrect when email is intended to identify the user:
{
"users": [
{
"attributes": {
"email": "sample@example.com",
"language": "en_US"
}
}
]
}Use this structure instead:
{
"users": [
{
"identifiers": {
"email": "sample@example.com"
},
"attributes": {
"language": "en_US"
}
}
]
}An identifier can also appear as an attribute when the API supports storing that field on the profile. However, the value used for identity resolution must still be present under identifiers.
Before sending a request:
Confirm which fields are enabled as identifiers in Identity Resolution Management.
Place those fields under
users[].identifiers.Verify that multiple identifiers in the same user object belong to the same person.
Use
insider_idwhen you need to target a known Insider One profile directly.
Each user object must also contain data to write, such as an
attributesobject or aneventsobject. An identifiers-only user object is not a valid profile update.
Do not mix append and overwrite behavior in one request
Standard array attributes are appended by default. You can overwrite their existing values by using not_append or append.
Append example
{
"users": [
{
"identifiers": {
"email": "sample@example.com"
},
"attributes": {
"custom": {
"favorite_categories": [
"electronics"
]
}
}
}
]
}If the profile already contains:
[
"fashion",
"home"
]The resulting array contains the existing and newly submitted values:
[
"fashion",
"home",
"electronics"
]Overwrite example
{
"users": [
{
"identifiers": {
"email": "sample@example.com"
},
"not_append": true,
"attributes": {
"custom": {
"favorite_categories": [
"electronics"
]
}
}
}
]
}The resulting array is:
[
"electronics"
]The append or overwrite selection applies to all standard array attributes covered by the setting. You cannot append one standard array attribute while overwriting another in the same request.
If different arrays require different behaviors:
Put the arrays that should be appended in one request.
Put the arrays that should be overwritten in a separate request.
Verify the resulting values on a test profile before running a bulk update.
If both append and not_append are present, append takes priority. Use one option consistently to avoid ambiguous payloads.
This rule concerns standard array attributes. Array of Objects attributes use action-based operations such as
add,merge,replace, andremove.
Pre-launch checklist
Before enabling an API integration in production:
Validate historical event timestamps against each event's TTL.
Parse the full response body, including the
successfulandfailblocks.Treat
200 OKwith a non-emptyfailblock as a failed or partially failed operation.Set
skip_hookexplicitly for every historical import.Place identity fields under
identifiers.Do not send an identifiers-only user object.
Separate append and overwrite operations when arrays need different behavior.
Test with a minimal single-user request.
Confirm the resulting attributes and events on the User Profile.
Log failed records with enough information to correct and retry them.