Raxx · internal docs

internal · gated

Account Merge Procedure

Status: Active
Last updated: 2026-06-21 UTC
Owner: Customer Support
Console path: /console/merges
Related epic: #3245
Related card: #3252 (this runbook)
Design doc: docs/architecture/account-merge-2026-06-05.md


Overview

Account merging is a CS-initiated operation that consolidates two customer accounts into one. The feature is gated by FLAG_ACCOUNT_MERGE. Do not attempt this procedure if that flag is OFF on the target environment — the console paths will return 404.

Key invariants:


1. Prerequisites

Before initiating a merge, confirm each of the following. If any check fails, do not proceed — resolve the blocker first and note it in the FreeScout ticket.

1.1 Identity confirmation

1.2 Open DSR check

1.3 Open FreeScout ticket


2. Initiating a merge

  1. Navigate to /console/merges in the Console.
  2. Click New merge.
  3. Enter the primary email address (the surviving account). CS selects the primary — the customer cannot request a change after this step.
  4. Enter the secondary email address (the account to be deactivated).
  5. Enter the FreeScout ticket number for this request.
  6. Review the account summary displayed: subscription status, passkey count, strategy count, open positions flag for each account.
  7. Click Initiate merge.

Console sends a verification email to both addresses. The merge status transitions to initiated.


3. Monitoring verification

After initiation, the merge detail view at /console/merges/<merge_id> shows real-time verification status.

Field Meaning
primary_verified_at Timestamp when the primary account holder clicked verify. Null until confirmed.
secondary_verified_at Timestamp when the secondary account holder clicked verify. Null until confirmed.
status initiatedin_progresscompleted (or cancelled / reversal_pending)
expires_at Verification window deadline. After this, codes expire and must be resent.

The merge engine advances to in_progress automatically once both *_verified_at fields are set. It advances to completed once the data migration completes. No CS action is needed in the intermediate states unless an edge case arises (see Section 4).


4. Edge cases

4.1 One email fails delivery (bounce or no response)

Symptom: Customer reports they did not receive the verification email. Console detail view shows primary_verified_at or secondary_verified_at is null after expected delivery time.

Steps:

  1. Ask the customer to check spam/junk folders and any email filtering rules.
  2. If no message was received, click Resend verification next to the affected address in the Console detail view. The console resends and resets the expiry window.
  3. If resend bounces (Postmark bounce event appears in Console), confirm the email address with the customer. Possible causes: typo in the address provided, inbox over quota, domain rejecting Postmark.
  4. If the customer cannot access the email address at all, do not proceed. The account cannot be verified without access to that address. Close the merge (Cancel in Console), note the reason in the FreeScout ticket, and advise the customer to regain email access before re-requesting.

4.2 One account has an active paid subscription (billing collision — D4)

Symptom: Console detail view shows a non-free subscription status on the secondary account.

This is expected and handled during the merge flow. The customer is prompted via email to choose:

Steps:

  1. Monitor the merge detail view for billing_collision_resolved_at. The merge will not advance to in_progress until the customer has made a billing choice.
  2. If the customer has not responded to the billing choice within 24 hours, resend the billing choice email from the Console detail view (Resend billing prompt).
  3. If the customer requests a custom arrangement (e.g., partial credit, delayed refund), do not proceed unilaterally — escalate to the billing team via a FreeScout internal note before the merge completes.
  4. Founders pricing note: If either account holds Founders pricing, Founders pricing is preserved on the merged account regardless of which account was primary.

4.3 One account has pending paper positions

Symptom: Console detail view shows pending_paper_positions: true on one account.

Action: No blocking action required. Paper positions from the secondary account are summed with those of the primary account at merge time. The merge proceeds normally. Notify the customer in the FreeScout ticket that paper positions from both accounts were combined.

4.4 Verification code expired

Symptom: Console detail view shows expires_at in the past and one or both *_verified_at fields are still null. Merge status is initiated but stalled.

Steps:

  1. Click Resend verification in the Console detail view for the unverified address. This generates a new code and resets the expiry window.
  2. Notify the customer in the FreeScout ticket that a new verification message was sent.
  3. If codes expire repeatedly without the customer verifying, ask the customer to confirm they are using the correct email address and that they can access it.

4.5 Customer requests a primary swap before first verification

Symptom: After initiation, the customer contacts support saying they want the other account to be primary.

Action: The primary designation is CS-only and cannot be changed after initiation. The customer cannot request a swap.

Steps:

  1. Cancel the current merge in the Console (Cancel button on the detail view). Note the reason in the FreeScout ticket.
  2. Initiate a new merge with the desired primary and secondary reversed.
  3. Verification emails are resent to both addresses for the new merge.

Do not attempt to alter primary_user_id directly. This is not available in the Console and would violate a mandatory invariant (M3) in the merge engine.


5. Reversal procedure

Reversals are available within 14 calendar days of the completed_at timestamp on the merge record.

Prerequisites:

Steps:

  1. Navigate to /console/merges/<merge_id>.
  2. Confirm completed_at is within the last 14 days. If outside the window, reversals are not available — advise the customer accordingly and close the FreeScout ticket.
  3. Click Request reversal. Enter the reason.
  4. The reversal request enters reversal_pending status. An operator-level user (different from you) must approve in the Console approval queue (/console/merges/reversals).
  5. Upon approval, both original email addresses receive a notification. A 24-hour hold begins.
  6. After the 24-hour hold, the operator who approved must click Confirm reversal to execute. Alternatively, click Abort reversal if the customer changes their mind during the hold.
  7. When reversal executes: the primary account reverts to its pre-merge state; the secondary account is reactivated; merged data originating from the secondary account is separated back out per the merge record.
  8. Note in the FreeScout ticket: reversal completed, both accounts restored, merge ID and reversal timestamp.

Limitations: Reversal availability may be restricted if data from the secondary account has been substantially modified after the merge (e.g., new trades entered under the merged account that cannot be cleanly attributed). Console will surface a warning in this case — escalate to ops@raxx.app before proceeding.


6. Post-merge checklist

After a merge completes (status = completed):