From 91028cf4f446bd86221ac15e8d5c738841d96813 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 07:15:25 +0000 Subject: [PATCH] docs(integration-toolkit): partner sharing for inbound mappings New page for the `sharing` block on inbound entity mappings: config, always-sync behaviour, errors and monitoring codes, simulation output. Link it from the mapping page, add it to the configuration schema, and regenerate the monitoring codes (SHARING_FAILED, SHARING_PARTNER_NOT_CONNECTED, SHARING_REJECTED). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013euB9J9k5P4AJFfZbk4dHw --- .../integration-toolkit/configuration.md | 5 + .../integration-toolkit/inbound/mapping.md | 1 + .../inbound/partner-sharing.md | 102 ++++++++++++++++++ .../integration-toolkit/monitoring/codes.md | 5 +- 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 docs/integrations/integration-toolkit/inbound/partner-sharing.md diff --git a/docs/integrations/integration-toolkit/configuration.md b/docs/integrations/integration-toolkit/configuration.md index aa44e919..3efbfc17 100644 --- a/docs/integrations/integration-toolkit/configuration.md +++ b/docs/integrations/integration-toolkit/configuration.md @@ -358,6 +358,11 @@ Everything beyond the configuration contract — the polling and acknowledgement "items_jsonata": "string", "result_mapping": {}, "on_error": "fail | skip | warn" + }, + "sharing": { + "partners": [ + { "partner_org_id": "string", "enabled": "boolean | JSONata string" } + ] } } ], diff --git a/docs/integrations/integration-toolkit/inbound/mapping.md b/docs/integrations/integration-toolkit/inbound/mapping.md index dbfb213d..0ed199a7 100644 --- a/docs/integrations/integration-toolkit/inbound/mapping.md +++ b/docs/integrations/integration-toolkit/inbound/mapping.md @@ -814,4 +814,5 @@ Transform and validate in one expression: - [Relations](./relations.md) - Link entities together - [Pricing](./pricing.md) - Map ERP line items and calculate prices - [Meter Readings](./meter-readings.md) - Handle meter reading data +- [Partner Sharing](./partner-sharing.md) - Share inbound entities with partner organizations - [Key/Value Maps](../key-value-maps.md) - Re-usable lookup tables for enum-like values diff --git a/docs/integrations/integration-toolkit/inbound/partner-sharing.md b/docs/integrations/integration-toolkit/inbound/partner-sharing.md new file mode 100644 index 00000000..c4fe502f --- /dev/null +++ b/docs/integrations/integration-toolkit/inbound/partner-sharing.md @@ -0,0 +1,102 @@ +--- +sidebar_position: 7.5 +title: Partner Sharing +description: Share inbound entities with partner organizations, kept in sync with every event +--- + +# Partner Sharing + +An inbound entity mapping can keep the entity it writes shared with [partner organizations](../../../collaboration/sharing.md). Use it when your system knows which partner works on a record. For example, a sales partner represents a customer. The partner then sees the record in their own organization while you keep owning it. + +Sharing is configured per entity mapping, next to `fields`, and kept in sync with every event: + +1. The entity is written as usual. +2. It is **shared** with every listed partner whose `enabled` is true. +3. It is **unshared** from every listed partner whose `enabled` is false. + +## Prerequisites + +Each partner organization needs an existing partnership with your organization. Sharing does not create partnerships or send invitations. A partner without one is skipped and reported as [`SHARING_PARTNER_NOT_CONNECTED`](#errors-and-monitoring). + +## Configuration + +Add a `sharing` block to the entity mapping, with one entry per partner organization: + +```json +{ + "entity_schema": "contact", + "unique_ids": ["customer_number"], + "fields": [ + { "attribute": "customer_number", "field": "customerNo" }, + { "attribute": "first_name", "field": "firstName" }, + { "attribute": "last_name", "field": "lastName" } + ], + "sharing": { + "partners": [ + { "partner_org_id": "739224", "enabled": "salesPartner.number = 'SP-1001'" }, + { "partner_org_id": "739225", "enabled": "salesPartner.number = 'SP-1002'" } + ] + } +} +``` + +| Property | Required | Description | +| --- | --- | --- | +| `partners` | Yes | One entry per partner organization. At least one. | +| `partners[].partner_org_id` | Yes | ID of the partner organization. | +| `partners[].enabled` | No | `true`, `false`, or a JSONata expression. Evaluated against the payload item, like field-level [`enabled`](./mapping.md). Defaults to `true`. | + +With the configuration above, this event shares the contact with organization `739224`: + +```json +{ + "customerNo": "C-100234", + "firstName": "Erika", + "lastName": "Mustermann", + "salesPartner": { "number": "SP-1001" } +} +``` + +If a later event names `SP-1002`, the contact is unshared from `739224` and shared with `739225`. If a later event names no sales partner, it is unshared from both. + +:::note +`enabled: false` does not mean "skip this partner". Sharing is always kept in sync, so it means "this entity must not be shared with this partner": a share that exists is removed. +::: + +To share several entities of one event, for example the contact and its contract, add a `sharing` block to each entity mapping. Only the mapped entity itself is shared, not its relations. + +## How it works + +- **Only changes are sent.** The target state is compared with who the entity is currently shared with. A repeated event with the same outcome makes no sharing call, and partners get no duplicate notification. +- **Runs on unchanged entities too.** Sharing is checked even when the entity's attributes did not change. A changed `enabled` result therefore applies on the next event. +- **Upserts only.** Sharing is skipped for the `delete` and `purge` modes. +- **The entity is written first.** A sharing problem never undoes the entity write. + +## Errors and monitoring + +Each case has its own monitoring code, so you can filter on it in the Monitoring tab or set up notifications for it. + +| Situation | Behaviour | Code | +| --- | --- | --- | +| No partnership with a listed partner organization | That partner is skipped, the others are synced. Not retried. | `SHARING_PARTNER_NOT_CONNECTED` (warning) | +| The sharing service rejects the request | Nothing is synced for this entity. Not retried. | `SHARING_REJECTED` (warning) | +| Temporary failure (service unavailable, timeout, throttling) | The entity update is retried automatically. | `SHARING_FAILED` (error), once all retries are used up | + +Warning events carry the affected `partner_org_ids` in their details. When sharing changed, the entity's success event carries `sharing.shared` and `sharing.unshared` with the partner organization IDs. + +See [Monitoring Codes](../monitoring/codes.md) for the full list. + +## Testing a mapping + +The mapping simulation in the Integration Hub's test view returns `sharing` on each entity update. Every partner's `enabled` is shown already evaluated against the test payload: + +```json +"sharing": { + "partners": [ + { "partner_org_id": "739224", "enabled": true }, + { "partner_org_id": "739225", "enabled": false } + ] +} +``` + +The simulation does not call the sharing service. It shows the target state, not the changes that would be sent. diff --git a/docs/integrations/integration-toolkit/monitoring/codes.md b/docs/integrations/integration-toolkit/monitoring/codes.md index 0b849cd2..98d189ef 100644 --- a/docs/integrations/integration-toolkit/monitoring/codes.md +++ b/docs/integrations/integration-toolkit/monitoring/codes.md @@ -15,7 +15,7 @@ Every monitoring event carries a **code** and a **level**. The code says what happened; the level says how much you should care. Both are filterable in the Integration Hub's [Monitoring tab](./overview.md) and through the events API. -There are 99 codes. You are most likely here because you saw one in a failed +There are 102 codes. You are most likely here because you saw one in a failed event — find it below. :::tip @@ -81,6 +81,7 @@ Something failed and the event did not do what it was meant to do. These are wha | `SECURE_PROXY_IP_NOT_ALLOWED` | The target IP address is not in the allowlist | | `SECURE_PROXY_NOT_FOUND` | The secure proxy use case was not found | | `SECURE_PROXY_UNAVAILABLE` | Secure proxy service is unavailable | +| `SHARING_FAILED` | Sharing the entity with its partner orgs kept failing with a temporary error and the retries were used up. The entity itself was written; the next event for it syncs its sharing again | | `SIGNATURE_VERIFICATION_FAILED` | Request signature could not be verified | | `SIGNATURE_VERIFICATION_UNAVAILABLE` | The file service could not be reached to verify the request signature | | `STEP_DISABLED` | A request step's "run this step when" expression returned false, so this step and every step after it were skipped. This is the configuration working as written, not a fault. | @@ -114,6 +115,8 @@ Processing continued, but something needs a human eye — often a retry in fligh | `INTERACTIVE_RATE_LIMITED` | The organization asked for interactive processing more often than its agreed rate, so these events were processed on its normal lane instead. Nothing was lost and the correlation can still be waited on — it will simply take as long as a bulk event does | | `LOOKUP_UNMAPPED` | A value was not listed in a lookup table and its fallback was used — see lookup_name and lookup_key for the gap | | `MSG_LATE_ARRIVAL` | An event arrived after the poll consumer had already received later events, so it was placed at the end of the stream instead of at its event time — it is delivered, but out of event-time order | +| `SHARING_PARTNER_NOT_CONNECTED` | The entity was written but not shared with the partner orgs in partner_org_ids: this organization has no partnership with them. Set up the partnership, or fix the partner_org_id in the mapping | +| `SHARING_REJECTED` | The entity was written but the sharing service rejected sharing it with its partner orgs (details.status). Check the sharing configuration of the partners in partner_org_ids | | `SOFT_DELETED_ENTITY_MATCHED` | A soft-deleted entity matched the unique ID — it will be resurrected on upsert, or referenced as-is by a relation. Investigate why the ERP source is sending events for a deleted entity. | ## Success