From ea2c449e4f5503f7327371f32417d2e09d50d049 Mon Sep 17 00:00:00 2001 From: Viljami Kuosmanen Date: Thu, 1 Oct 2026 10:29:23 +0300 Subject: [PATCH] docs(integration-toolkit): relation_refs append to existing targets; entity order is not a processing order - Entities mapped from one event are processed in parallel; replace the "Process parent entities before children" best practice. - Document that a relation_ref whose item is missing on an existing target is appended and retried (never replaces the attribute), and when RELATION_REF_ITEM_NOT_FOUND is still raised. Refs STABLE360-13057. Co-Authored-By: Claude Opus 5.5 --- .../integration-toolkit/inbound/relations.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/integrations/integration-toolkit/inbound/relations.md b/docs/integrations/integration-toolkit/inbound/relations.md index 470b3e3..2ccbc9c 100644 --- a/docs/integrations/integration-toolkit/inbound/relations.md +++ b/docs/integrations/integration-toolkit/inbound/relations.md @@ -425,7 +425,7 @@ While `$relation` links to an entity, `$relation_ref` links to: ### Processing Flow 1. **Find or create the related entity**: Uses `unique_ids` to find/create the contact -2. **Set the attribute value**: Upserts the `address` attribute on the contact with the provided value +2. **Write the attribute value**: If the contact is created, the `address` attribute is written with the provided value (using `value.operation`). If the contact already exists but has no matching address, the value is **appended** — the contact's other addresses are always kept, whatever `value.operation` is configured 3. **Preserve `_id` values**: Automatically matches existing address items by their content and preserves their `_id` to avoid regeneration 4. **Create the reference**: Links the main entity to the specific address item using `$relation_ref` @@ -485,6 +485,16 @@ The system automatically preserves `_id` values when updating repeatable attribu - If a match is found, the existing `_id` is preserved - This ensures stable references even when data is updated +### When the Referenced Item Doesn't Exist Yet + +If the related entity exists but none of its items match the value (for example new bank details from the ERP, or an empty attribute), the relation_ref is deferred like a missing relation: the value is appended to the related entity, then the update is retried and the reference is created. + +Only if the item still can't be matched after its value was written — for example because the mapped value is invalid, or the entity API normalized it so it no longer deep-equals — is the relation_ref skipped with the warning `RELATION_REF_ITEM_NOT_FOUND` in the integration monitoring. The rest of the update is still applied. + +:::tip +If the related entity's own mapping writes the same attribute with `_set` (or without an operation), it replaces the items that relation_refs appended on every sync, and they are appended again under a new `_id`. If the related entity should keep every referenced item, map that attribute with `_append` as well. +::: + ### Relation Reference Operations Relation references support the same three operations as relations: @@ -530,9 +540,11 @@ Search for Related Entity ## Best Practices -### Order Your Entity Processing +### Don't Rely on Entity Order + +The order of the `entities` array is **not** a processing order. The entities mapped from one event are processed in parallel, so a contract can be processed before the contact it relates to — even when the contact is listed first. -Process parent entities before children: +You don't need to order anything for correctness: relations and relation_refs that point to an entity which isn't there yet are deferred and retried (see [Relation Resolution Strategy](#relation-resolution-strategy)). Listing parent entities first still keeps a configuration easy to read: ```json {