Skip to content
Connected Services Framework
Part 2 — Implementation
Markdown

#7. CP Transitions

Connected Services Framework (CSF) — Part 2: Implementation — Version 2.0

#7.1 Overview

CPs are free to transition between MAPs, subject to standard commercial terms. The CSF is designed to make this transition seamless, with built-in mechanisms for identity portability, in-flight order transfer, and conflict resolution.

A CP can operate through only one MAP at any given time for a given RCPID. This relationship is established through DNS records controlled by the CP.

#7.2 Reasons for Moving

A CP may move to a new MAP for various reasons:

  • Service dissatisfaction: The existing MAP fails to meet agreed standards
  • Provider termination: The current MAP enters administration or issues a termination notice
  • Strategic realignment: The CP's business strategy requires a different MAP
  • Cost optimisation: The CP wishes to become a MAP of 1 or consolidate providers
  • M&A - A CP is purchased by another CP and their brand rolls into that of the acquirer

#7.3 Transition Process

#Step 1 — Establish with New MAP

The CP establishes a commercial relationship with the new MAP and completes the CP onboarding process:

  • New PKI key pair is generated by the new MAP
  • The new MAP does NOT yet add the CP to its directory

#Step 2 — DNS Update

The CP updates its DNS TXT records. CPs should lower their TTL on the existing records in the days/hours prior:

  • [RCPID]._domainkey.[cp-domain] — new public key from the new MAP
  • [RCPID]._mapkey.[cp-domain] — URL of the new MAP's message endpoint

Note: This temporarily stops message flow via the original MAP. Messages sent by other MAPs will fail PKI verification against the old MAP and be queued locally for retry.

#Step 3 — Directory Update

The new MAP performs the directory-side activation steps that make the CP visible and routable through it:

  1. Add the CP entry to the new MAP's CP Registry under list[].identity[], with the agreed processSupport[] array covering each SforB process the CP is signing up for (businessSwitchMatch, businessSwitchOrder, businessSwitchOrderUpdate, businessSwitchOrderTrigger, businessSwitchOrderCancellation, etc).
  2. Set per-CP status. During cut-over the entry SHOULD be published with processSupport[].status = TEST if the new MAP is still onboarding the CP; or ACTIVE if the new MAP itself is already an ACTIVE participant and the only change is the move. The status is later transitioned to ACTIVE in Step 6, after in-flight reconciliation has cleared.
  3. Bump the registry version timestamp so other MAPs polling the directory observe the change on their next refresh.
  4. Re-sign and re-publish the new MAP's CP Registry per Directory API §5. Do NOT attempt to suppress the entry that still exists in the original MAP's registry — until Step 5 completes there is a deliberate, brief overlap which the conflict-resolution rules in §7.5 are designed to absorb.
  5. Wait for cache propagation. Receiving MAPs cache registry data; allow at least one configured polling cycle (typically a few minutes) before relying on the entry being visible network-wide. The new MAP's registry endpoint MUST honour If-Modified-Since so peers can confirm freshness without polling the full payload.
  6. DNS already settled. DNS was updated in Step 2; the directory entry is the authoritative advertisement of the routing change but the underlying authority comes from _mapkey, which receivers will re-resolve before sending sensitive traffic.

Note: Until Step 5 has run and the original MAP's registry no longer lists the CP, the CP appears under two MAPs simultaneously. This is the expected transient state and is what triggers the conflict-detection rules in §7.5.

#Step 4 — RCPID Status Export Request

The new MAP sends a signed rcpidStatusRequest to the original MAP via the Letterbox API. This request is the formal handover trigger and authorises the original MAP to release in-flight order data and decommission its registry entry for the CP.

Authorisation model. Before responding, the original MAP MUST independently verify all three of:

  1. The DKIM signature on the inbound request is valid per PKI §3 and the signing CP's _domainkey resolves to the new MAP.
  2. The moving CP's _mapkey DNS record now resolves to the requesting MAP. This is the cryptographic proof that the CP authorised the move — DNS is controlled by the CP, so a _mapkey pointing at the requesting MAP can only have been set with the CP's consent.
  3. The requesting MAP's registry advertises the CP under it (at any status — TEST or ACTIVE).

Request payload.

{
  "envelope": {
    "source":      { "type": "RCPID", "identity": "<new-MAP RCPID>",  "correlationID": "<uuid>" },
    "destination": { "type": "RCPID", "identity": "<old-MAP RCPID>" },
    "routingID":   "rcpidStatusRequest",
    "auditData":   [ { "name": "test", "value": "false" } ]
  },
  "rcpidStatusRequest": {
    "movingRCPID":   "<the moving CP's RCPID>",
    "purpose":       "MAP_TRANSFER",
    "newMAP":        "<new-MAP RCPID>",
    "newMAPDomain":  "letterbox.newmap.example",
    "requestedAt":   "2026-04-29T10:00:00Z",
    "specVersion":   "GPLB-SG SforB Msg Spec v3.0"
  }
}

Response time and failure modes.

ConditionResponseHTTP / Code
All authorisation checks passSynchronous rcpidStatusConfirmation carrying the export (see §7.4)202
DNS _mapkey does not resolve to the requesterReject — handover not authorised403 / 9301
DKIM verification failsReject — signature invalid403 / 8101
No in-flight SORs found for the moving CPrcpidStatusConfirmation with empty orders[] array; registry entry still removed202
Original MAP cannot comply within SLArcpidStatusFailure with retry guidance503 / 9302

SLA expectations. The original MAP SHOULD respond within 30 seconds during core business working hours for an export of fewer than 100 in-flight SORs and within 2 minutes for a complete export. A delayed response forces the new MAP to retry; the original MAP MUST handle idempotency by correlating retries against the original correlationID.

Side effects on the original MAP. As soon as the original MAP commits to processing the export, it MUST:

  • Quiesce the moving CP's mailbox — block any new outbound SforB messages from the CP and queue inbound messages addressed to it until the response has been sealed, so that the export captures a consistent snapshot.
  • Remove the CP entry from its CP Registry and re-sign and re-publish the registry version (so that other MAPs can stop attempting to route to it).
  • Persist the export alongside the inbound rcpidStatusRequest correlation ID for at least 30 days for audit, regardless of the response outcome.

#Step 5 — In-Flight Order Reconciliation

Step 5 is where the in-flight Switch Orders move from the original MAP's state engine into the new MAP's state engine, and where every counterparty MAP gets the cross-check it needs to keep treating the customer journey as continuous.

#7.3.5.1 Suggested Export Routine (run by the original MAP)

The export routine runs server-side on the original MAP between accepting the rcpidStatusRequest and emitting the response. Each step is idempotent so the routine can be safely re-entered after a partial failure:

  1. Hold the moving CP's mailbox. Block new outbound SforB messages from the moving CP (the GRCP path) and queue any inbound SforB messages addressed to it (the LRCP path). This freeze prevents new in-flight state from being created during the export window.
  2. Enumerate SORs. Walk the SforB order store for every switchOrderReference where the moving CP is either the GRCP (originator of the match/order) or the LRCP (counterparty). Each SOR is processed independently.
  3. Capture the last completed interaction only. For each SOR, identify the most recent completed request/response pair (e.g. businessSwitchOrderRequestbusinessSwitchOrderConfirmation) per service. Partially completed exchanges are dropped, per the existing rule in §7.4.5.
  4. Resolve counterparty routing. For every SOR, record the counterparty CP's RCPID and that CP's current MAP — read live from the CP Registry, not from the cached SOR record — so the new MAP knows where to direct follow-up rcpidStatusRequest cross-checks in the reconciliation phase.
  5. Snapshot per-service state. For every service inside the SOR (IAS, NBICS, NBICSRange, etc.) capture the current status from the SforB state machine (matched, ordered, triggered, cancelled, failed), the active plannedSwitchDate, and any fault block recorded against the service. This mirrors the structure documented in the GPLB-SG SforB Message Specification v3.0 §3.3 — Business Switch Order Request (canonical source published by GPLB-SG via the FCS).
  6. Capture original-match identity context. Carry forward the customer-identifying fields (grcpBrandName, name, account, identity[], address) from the businessSwitchMatchRequest/Confirmation that opened the SOR. This allows the new MAP to validate continuity if the counterparty asks for a re-match.
  7. Compute integrity hashes. Canonicalise each SOR record and compute a SHA-256 recordHash, then an aggregateHash over the whole orders[] array. Sign aggregateHash with the original MAP's _domainkey private key (the MAP-level key from PKI §3, NOT the CP-level key being moved).
  8. Wrap and return. Emit the response payload described in §7.4 as the body of the rcpidStatusConfirmation message.
  9. Finalise registry removal. Once the response has been transmitted, finalise removal of the CP from the registry as already initiated in Step 4 and re-publish.

#7.3.5.2 Reconciliation Performed by the New MAP

After receiving the export the new MAP:

  1. Verifies the aggregate signature against the original MAP's _domainkey — this establishes chain-of-custody for the data being imported.
  2. Imports each SOR into its own switching state engine, preserving the switchOrderReference UUID so the customer journey is not restarted. New correlation IDs are minted at the envelope layer for any subsequent outbound traffic, per the §7.4.5 rule.
  3. Issues an rcpidStatusRequest to each counterparty MAP named in the export — one per counterparty — to:
    • Notify them that the moving CP is now reachable via the new MAP.
    • Cross-check the SOR state from the counterparty's perspective. Any mismatch (e.g. counterparty believes a service is triggered when the export says ordered) is flagged and resolved bilaterally before further messages flow.
  4. Resumes message flow by re-publishing the CP entry with processSupport[].status = ACTIVE in the new MAP's CP Registry once all reconciliation responses are received and clean.
  5. Notifies the moving CP with a transition report (count of SORs imported, any reconciliation flags raised, and estimated time to full resumption).

#7.3.5.3 What Must Be in the Export — Minimum Detail per SOR

The full schema is documented in §7.4. At minimum, every in-flight SOR record MUST carry:

Field groupPurpose
switchOrderReference, role, currentStateIdentity of the order and which side the moving CP is on
counterparty.{rcpid, mapRcpid}Tells the new MAP where to send the cross-check request
originalMatch.{grcpBrandName, name, account, identity[], address}Identity context preserved from the opening match
services[].{serviceType, serviceIdentifier, status, plannedSwitchDate, fault}Authoritative per-service state at the moment of export
lastInteraction.{routingID, timestamp, sourceCorrelationID, destCorrelationID, payloadHash}The most recent completed exchange — the resumption baseline
expectedNext.{messageType, deadline, slaWindow}What message is due next and the SLA clock the new MAP must continue to honour
auditTrail[]Hashes (not bodies) of every prior completed exchange so anomalies can be investigated bilaterally without exposing message body content

#Step 6 — Resume Operations

With the export imported and counterparty cross-checks clean, the new MAP can promote the moving CP to fully operational state. Treat the lists below as a sign-off checklist; nothing in Step 6 can be skipped without recording a deviation.

#New MAP

  • Cross-check responses received from every counterparty MAP referenced in the export, and any mismatches resolved bilaterally before resuming traffic.
  • Per-CP processSupport[].status flipped from TEST to ACTIVE in the registry; registry version bumped, re-signed and re-published.
  • Synthetic round-trip executed against the moving CP via the TAG Baseline Test Suite — §8.5 Letterbox API to confirm end-to-end delivery.
  • Audit log entry written: time of completion, count of SORs imported, aggregateHash of received export, original MAP RCPID, signing key fingerprint.
  • Telemetry resumed: outbound messages no longer carry auditData.test=true (unless the new MAP is itself still in TEST per Onboarding & Testing Process §5).
  • CP-facing portal or dashboard reflects every imported SOR with continuity preserved (same switchOrderReference, same per-service status).

#Original MAP

  • CP entry removed from registry; registry version bumped, re-signed and re-published.
  • _domainkey retained for the agreed retention window so receivers verifying historic signatures within that window MUST still be able to resolve them.
  • Inbound queue for the moving CP drained: each remaining queued message either forwarded under cover of an rcpidStatusRequest to the new MAP, or rejected with code 9999 (in migration) if forwarding is not feasible.
  • Export package archived alongside the inbound rcpidStatusRequest correlation ID for at least 30 days.
  • Operational support for the moving CP closed off cleanly, with the agreed handover support window communicated to the CP and the new MAP.

#Counterparty MAPs

  • Registry caches refreshed; the new MAP entry observed for the moving CP.
  • _mapkey DNS resolved at least once after the directory change to confirm the authoritative MAP.
  • Locally queued messages drained — those that were retrying against the original MAP MUST be re-signed and re-routed to the new MAP's letterbox.
  • Bilateral SOR cross-check with the new MAP reported clean (or anomalies escalated through the conflict-resolution path in §7.5).

#The CP

  • DNS records propagated globally — verify from at least three geographic resolvers before declaring the migration complete.
  • All in-flight customer journeys visible in the new MAP's portal/dashboards.
  • Old MAP support contact still reachable for the agreed handover support window.
  • Stakeholder communications sent: customers, downstream CPs, and any affected industry forum.

#7.4 RCPID Status Export Schema

The schema below is a CSF design proposal layered onto the GPLB-SG SforB Message Specification v3.0 to support lossless MAP migration. It is published here so that the TAG can ratify the field set and the canonicalisation rules used for aggregateHash/recordHash before the first MAP migration runs in production.

#7.4.1 Top-Level Envelope

The export rides inside the standard CSF envelope. The body element is rcpidStatusConfirmation and contains two top-level objects: exportMetadata and orders[].

{
  "envelope": { "...": "as per Letterbox §4.3" },
  "rcpidStatusConfirmation": {
    "exportMetadata": { "...": "see §7.4.2" },
    "orders":         [ { "...": "see §7.4.3" } ]
  }
}

#7.4.2 Export Metadata

FieldDescriptionTypeRequired
exportVersionSchema version of this export. Current: 1.0.StringYes
generatedAtUTC timestamp at which the export was sealed.String (ISO 8601)Yes
generatedByRCPID of the original (sending) MAP.StringYes
movingRCPIDRCPID of the CP being transferred.StringYes
newMAPRCPID of the new MAP — echoes the requester.StringYes
dnsVerifiedAtUTC timestamp at which the original MAP confirmed _mapkey resolution to the new MAP.StringYes
directoryRemovedAtUTC timestamp at which the moving CP was removed from the original MAP's registry.StringYes
specReferenceVersion of the SforB Message Spec the orders were created against (e.g. GPLB-SG SforB Msg Spec v3.0).StringYes
orderCountNumber of records in orders[].IntegerYes
aggregateHashSHA-256 hash over the canonicalised orders[] array.String (hex)Yes
signatureBase64 signature over aggregateHash using the original MAP's _domainkey private key (algorithm rsa-sha256 or ed25519-sha256 per PKI §3).StringYes

#7.4.3 Per-Order Record

Each entry in orders[] is a full snapshot of a single in-flight Switch Order. The schema is designed to map 1:1 onto the SforB state machine in the GPLB-SG SforB Message Specification v3.0 §3 so the new MAP can resume handling without re-deriving state.

FieldDescriptionTypeRequired
switchOrderReferenceUUID of the SOR. Preserved verbatim — the new MAP MUST NOT mint a new SOR.StringYes
rolegaining if the moving CP is the GRCP for this SOR; losing if it is the LRCP.String (enum)Yes
currentStateHigh-level state: matched, ordered, partially-triggered, partially-cancelled, complete-pending-trigger, trigger-failed.String (enum)Yes
counterparty.rcpidRCPID of the CP on the opposite side of the order.StringYes
counterparty.mapRcpidRCPID of the counterparty's current MAP, looked up live at export time.StringYes
originalMatch.grcpBrandNameBrand name presented to the customer at match time.StringIf known
originalMatch.nameCustomer or business name from the match.StringYes
originalMatch.accountLRCP-side account number, if supplied at match.StringOptional
originalMatch.identity[]Identity tuples (contact, IAS, NBICS, SecureID, SIC) used to anchor the match.ArrayOptional
originalMatch.addressUPRN / address lines / post town / post code from the match.ObjectYes
services[].serviceTypeIAS, NBICS, NBICSRange (or future industry-agreed type).StringYes
services[].serviceIdentifierThe GRCP's per-service reference. Required for every service, mirroring §3.3 of the SforB Msg Spec.StringYes
services[].statusCurrent service state: matched, ordered, triggered, cancelled, failed.StringYes
services[].plannedSwitchDatePer-service planned switch date if it overrides the SOR header.String (CCYY-MM-DD)Optional
services[].fault{ faultCode, faultText } if the service is in failed state.ObjectOptional
lastInteraction.routingIDMost recent completed exchange (e.g. businessSwitchOrderConfirmation).StringYes
lastInteraction.timestampWhen that exchange completed.String (ISO 8601)Yes
lastInteraction.sourceCorrelationIDThe original sender-side correlation ID.StringYes
lastInteraction.destCorrelationIDThe destination-side correlation ID, if available.StringOptional
lastInteraction.payloadHashSHA-256 of the canonicalised message body — included so the new MAP can verify with the counterparty without exposing the body itself.String (hex)Yes
expectedNext.messageTypeThe message type expected next on this SOR (e.g. businessSwitchOrderTriggerRequest).StringOptional
expectedNext.deadlineHard SLA deadline by which the next message must be sent.String (ISO 8601)Optional
expectedNext.slaWindowThe SLA budget remaining as ISO 8601 duration.StringOptional
auditTrail[]Array of { routingID, timestamp, payloadHash } for every prior completed exchange on this SOR. Hashes only — message bodies are NOT exported.ArrayYes
recordHashSHA-256 over the canonicalised record above (excluding recordHash itself). Allows individual orders to be re-verified after import.String (hex)Yes

#7.4.4 Worked Example

A minimal export covering one SOR where the moving CP is the GRCP and the order is in the ordered state, awaiting trigger:

{
  "rcpidStatusConfirmation": {
    "exportMetadata": {
      "exportVersion": "1.0",
      "generatedAt": "2026-04-29T10:00:00Z",
      "generatedBy": "<old-MAP RCPID>",
      "movingRCPID": "809b6e65-a6e7-40f6-8b52-04dd65b6fce1",
      "newMAP": "<new-MAP RCPID>",
      "dnsVerifiedAt": "2026-04-29T09:58:42Z",
      "directoryRemovedAt": "2026-04-29T09:59:01Z",
      "specReference": "GPLB-SG SforB Msg Spec v3.0",
      "orderCount": 1,
      "aggregateHash": "5a3b...c812",
      "signature": "MEUCIQDh..."
    },
    "orders": [
      {
        "switchOrderReference": "123e4567-e89b-12d3-a456-426614174000",
        "role": "gaining",
        "currentState": "ordered",
        "counterparty": {
          "rcpid": "d8322d80-92c8-4906-a408-f1d2daf7e03d",
          "mapRcpid": "<counterparty MAP RCPID>"
        },
        "originalMatch": {
          "grcpBrandName": "Bridgend Telecoms (Telesales)",
          "name": "The Coffee Shop",
          "account": "0003316563216",
          "identity": [
            { "name": "contact", "value": "John Smith" },
            { "name": "NBICS",   "value": "01234987654" },
            { "name": "SIC",     "value": "AB1234" }
          ],
          "address": {
            "uprn": "12345",
            "addressLines": ["Unit 1", "Highlands Industrial Estate", "22 Cheshunt Mews"],
            "postTown": "Glasgow",
            "postCode": "SW2R 5AF"
          }
        },
        "services": [
          { "serviceType": "IAS",        "serviceIdentifier": "ABC123",                "status": "ordered" },
          { "serviceType": "NBICS",      "serviceIdentifier": "0101111222",            "status": "ordered" },
          { "serviceType": "NBICSRange", "serviceIdentifier": "0101112000-0101112999", "status": "ordered", "plannedSwitchDate": "2025-05-27" }
        ],
        "lastInteraction": {
          "routingID": "businessSwitchOrderConfirmation",
          "timestamp": "2026-04-12T14:21:08Z",
          "sourceCorrelationID": "ABC123",
          "destCorrelationID": "XYZ987",
          "payloadHash": "8f1a...0e44"
        },
        "expectedNext": {
          "messageType": "businessSwitchOrderTriggerRequest",
          "deadline": "2025-04-03T23:59:59Z",
          "slaWindow": "P14D"
        },
        "auditTrail": [
          { "routingID": "businessSwitchMatchRequest",      "timestamp": "2026-04-08T09:00:00Z", "payloadHash": "..." },
          { "routingID": "businessSwitchMatchConfirmation", "timestamp": "2026-04-08T09:00:18Z", "payloadHash": "..." },
          { "routingID": "businessSwitchOrderRequest",      "timestamp": "2026-04-12T14:20:55Z", "payloadHash": "..." },
          { "routingID": "businessSwitchOrderConfirmation", "timestamp": "2026-04-12T14:21:08Z", "payloadHash": "..." }
        ],
        "recordHash": "9c2d...4e10"
      }
    ]
  }
}

#7.4.5 Rules

  • Switch Orders only. The export includes Switch Orders only. Switch Matches that have not progressed to an order MUST be repeated by the CP via its new MAP.
  • Completed interactions only. Partially completed interactions MUST NOT be exported — only the last completed interaction for each exchange.
  • Correlation IDs. The new MAP mints fresh envelope correlation IDs for any subsequent messages — the originals are recorded in lastInteraction and auditTrail[] for traceability only.
  • Directory update precedence. The original MAP MUST remove the moving CP from its directory before transmitting the export response. Receivers can rely on the registry version bumping in tandem with the export.
  • Body hashes, not bodies. Only message-body hashes are exported. The body itself is opaque to the CSF and remains the property of the originating CP — the hashes are sufficient to support bilateral counterparty cross-checks without re-exposing customer data.
  • Signed end-to-end. The export's aggregateHash MUST be signed with the original MAP's _domainkey private key (the MAP-level key), giving the new MAP and any audit body cryptographic proof of provenance for the imported data.

#7.5 Conflict Resolution

When a CP appears in more than one MAP's directory simultaneously:

  1. The detecting MAP checks the CP's _mapkey DNS record
  2. DNS indicates the current MAP endpoint
  3. The MAP marks the conflicting entry as being in "conflict"
  4. The new MAP advertises the old MAP as being in conflict, signalling other MAPs to check

Over time, all MAPs will recognise the conflict and update their directories accordingly.

#Latency During Transition

A CP should plan its move to minimise the gap — typically done overnight. During the transition:

  • Messages sent by other MAPs will reflect routing from their current directories
  • PKI verification will eventually prevent delivery to the old MAP
  • Messages will be queued locally by sending MAPs for retry
  • Once the new MAP has completed DNS changes and RCPID Status exports, messages resume flowing to the correct MAP

This self-healing methodology is native to the distributed CP Registry model — PKI provides the final verification check before sending.

#7.6 RACI for In-Flight Order Handling

ActivityOriginal MAPNew MAPCP
Generate new PKI keysResponsible
Update DNS recordsAssistsResponsible
Stop processing messages (after DNS change)Automatic
Respond to RCPID Status requestResponsible
Remove CP from directoryResponsible
Add CP to directoryResponsible
Send RCPID Status requests to other MAPsResponsible
Import in-flight order dataResponsible
Notify affected CPsResponsibleInformed