Skip to Content
IntegrationsAPNConflicts & resolution

APN Sync — Conflicts and Resolution

How conflicts are detected

FlowState checks, on every inbound APN event, whether HubSpot and AWS have each been modified since the last successful sync:

HubSpot modified since last sync?AWS modified since last sync?Outcome
NoNoNo-op. Nothing to do.
NoYesApply AWS changes to HubSpot. No conflict.
YesNoHubSpot has newer data. Ignore the inbound event (next outbound cycle reconciles).
YesYesBoth sides changed — a candidate conflict, confirmed per field (below).

The comparison is per-deal. Different deals can be in different conflict states independently.

Both sides changing does not by itself mean a conflict

Each side may have changed a different field. The mapped user-editable field is the close date, so FlowState confirms a real conflict by comparing the close date on both sides against the value they last agreed on:

  • Only HubSpot changed the close date (you edited it; AWS only changed, say, its review status on approval) → not a conflict. The edit is left intact and the next outbound cycle pushes it to APN. APN’s stale date never overwrites your edit.
  • Only APN changed the close date → not a conflict. It is mirrored to HubSpot.
  • Both changed the close date to different values → a real conflict (below).

So approving an opportunity in APN while a close-date edit is pending in HubSpot does not raise a false conflict.

Field ownership and conflict scope

Only conflict-detected fields (fields owned by “Both can update”) can enter a conflict state. These fields are:

  • LifeCycle.Stage (mapped to/from dealstage)
  • LifeCycle.TargetCloseDate (mapped to/from closedate)
  • ExpectedCustomerSpend.Amount (mapped to/from apn_mrr) — the partner drives APN with it, and AWS re-forecasts it during co-sell review
  • Customer.Account.Address.PostalCode (mapped to/from apn_postal_code) — the partner’s own address correction and AWS’s can race the same way
  • LifeCycle.NextSteps (not mapped to a HubSpot property by default; can be added via set_apn_field_mapping)

apn_mrr and apn_postal_code are confirmed the same way as closedate: compared per-field against the last-synced value, not just by timestamp.

AWS-owned fields (e.g., LifeCycle.ReviewStatus) are always applied from APN to HubSpot without conflict detection — they cannot conflict because HubSpot is not the source of truth for them.

HubSpot-owned fields (e.g., dealname, Customer.*, Project.*) are always taken from HubSpot — inbound APN events do not overwrite them.

Conflict states

Each deal has a conflict state:

ValueMeaning
noneNo active conflict. Sync is running normally.
hubspot_newerOnly HubSpot has been updated since the last sync. Resolved automatically on the next outbound sync. Not shown as a conflict in the UI.
aws_newerOnly AWS has been updated. Applied automatically by the inbound handler. Not shown as a conflict in the UI.
both_divergedBoth HubSpot and AWS have been updated on a conflict-detected field. Requires manual resolution. apn_sync_status is set to conflict on the HubSpot deal.

Conflict notifications

When both_diverged is detected, FlowState:

  1. Sets apn_sync_status = conflict on the HubSpot deal record.
  2. Records the conflict, listing each conflicting field with the HubSpot value and the AWS value — read by the conflict-resolution page below, not mirrored to a HubSpot property.
  3. Sends a apn.conflict_detected notification to subscribed tenant members (email and Slack, if configured).
  4. Pauses outbound sync for the affected deal until the conflict is resolved.
  5. Continues processing all other deals normally.

Resolving conflicts

Option 1 — UI conflict resolution page

Go to Deals → APN Sync → Conflicts in FlowState. The conflict page shows a three-pane diff for each conflicting field:

  • Left pane: the value in HubSpot.
  • Center pane: the value at the last successful sync (the common ancestor).
  • Right pane: the value in AWS.

Choose Use HubSpot value, Use AWS value, or (for fields that support it) Set a custom value.

Click Resolve and resume sync to apply the resolution. FlowState writes the winning value to both sides and re-enqueues the deal for a reconciliation sync.

Option 2 — MCP tool

Use resolve_apn_conflict(hubspot_deal_id, resolution) where resolution is one of:

  • "prefer_hubspot" — all conflict-detected fields take the HubSpot value.
  • "prefer_aws" — all conflict-detected fields take the AWS value.
  • "merge" — specify per-field resolutions via merge_fields: {"LifeCycle.Stage": "prefer_hubspot", "LifeCycle.TargetCloseDate": "prefer_aws"}.

Option 3 — Get conflict detail first

Use get_apn_conflict_detail(hubspot_deal_id) to retrieve the side-by-side diff programmatically before calling resolve_apn_conflict.

After resolution

  1. The conflict state is cleared: conflict_state = none, apn_sync_status = pending.
  2. FlowState enqueues a reconciliation sync for the deal (treated as a full sync).
  3. The reconciliation applies the resolved values to both HubSpot and APN.
  4. On success, apn_sync_status advances to submitted or live, depending on where the opportunity is in its lifecycle.

Sync resumes automatically — no further action is required after clicking Resolve.