PRD
In-App Support — PRD
Section titled “In-App Support — PRD”Author: Ridwan Egbeyemi (PM, Swap) Status: Spec-locked · ready for engineering Surfaces: Rechaj Swap (mobile) · Atlas (web) Prototype references:
products/rechaj-swap-app/prototypes/support-cases-v3.html·products/atlas/prototypes/swap-support-resolution.htmlSupersedes:specs/2026/swap-support/— earlier iteration. Where this PRD’s issue taxonomy or officer-role model conflicts with the older doc, this PRD wins.
1. Overview
Section titled “1. Overview”Today, swap managers escalate operational issues to Rechaj support over WhatsApp. Nothing is tracked, managers sit blocked while riders queue behind them, and officers copy details by hand from WhatsApp into Atlas — which on 17 May caused an overdraft to be credited to the wrong rider. This PRD replaces WhatsApp end-to-end with structured in-app support: managers report issues from inside Rechaj Swap, tied to the active swap; officers resolve them inside Atlas against context that was captured at report time, never re-typed; both sides share the same issue record and message thread; every action is logged.
2. Goals
Section titled “2. Goals”- Eliminate copy-paste resolution actions. 100% of resolution forms in Atlas open with rider, wallet, battery, transaction, bank, and account identifiers pre-loaded from the issue payload. Zero free-text rider-ID entry on any resolution action. Closes the wrong-rider incident class structurally.
- Replace WhatsApp. ≥95% of support traffic flows through the in-app system within one month of GA. WhatsApp escalations drop to a fallback for app-down incidents only.
- Preserve swap throughput under blocking issues. A manager who parks a stuck swap can complete ≥3 subsequent swaps for other riders without losing the parked swap’s context, and resumes it on push without re-entering identifiers.
- Measurable resolution times per issue type. [Derived] Median time-to-acknowledge < 2 min; median time-to-resolve < 10 min for the highest-volume issue types (3.1 Release & Reassign, 3.2 Station Claim, 2.1 Payment not reflecting). Confirm targets with leadership (Q1).
- Officer-type routing accuracy. Issues land in the correct officer-type queue on first submission with no manual triage. Issue type → owner mapping is enforced server-side, not by UI convention.
- Auditability. Every resolution action, escalation, and message exchange writes an immutable audit entry with actor, target, before/after delta, timestamp, and case ID.
3. Non-goals
Section titled “3. Non-goals”- Penalty system for temporary station requests (issue 1.1). Data model must anticipate it; logic ships later.
- Facility BU issue resolution. Facility Officer role exists for RBAC scaffolding; no issue types route to them in this release.
- Energy BU resolution. Role model must extend cleanly; nothing is built for them now.
- WhatsApp fallback. No bridge, no integration, no parallel pathway. The system replaces WhatsApp; it does not coexist with it.
- AI-assisted resolution / smart routing / sentiment-based prioritization. Out of scope.
- Gamification of feedback ratings. Ratings inform ops; they do not score managers or unlock features.
- VCU connection scenarios (former scenarios 12 / 13 / 14). Deferred per the
swap-support/overview.mddecision; the in-app unlock redesign supersedes any officer-in-the-loop path.
4. Users & roles
Section titled “4. Users & roles”| Role | Surface | Resolves issue types | Notes |
|---|---|---|---|
| Swap manager | Rechaj Swap (mobile) | — | Reports issues. Receives clarifications, sends evidence retakes, gets push when resolved. |
| Swap Support Officer | Atlas | 1.1, 1.2, 2.1, 2.2, 2.3 (passive), 3.1, 3.2, 3.3 | Owns the bulk of the queue. Escalates 2.4 / 2.5 / 3.4. |
| Payment Officer | Atlas | (none in this release; receives wallet escalations) [Derived — confirm in Q2: which 2.x issues, if any, should route to Payment Officer directly vs flow through Swap Support first?] | Models the BU separation for future routing. |
| Engineering Support Officer | Atlas | 2.4 Phantom pending swap fee · 2.5 Invalid funding account | Receives escalations from Swap Support; loops in engineering off-platform. |
| Inventory Support Officer | Atlas | 3.4 Serial Mismatch | Receives escalation. |
| Facility Officer | Atlas | — | RBAC-only in this release. No resolution surfaces. |
| Future BU officers (Energy, etc.) | Atlas | — | Role model must extend without schema changes. |
A single human user may hold multiple officer roles simultaneously. RBAC checks at the action layer, not the page layer — a Payment + Swap Support officer sees both queues merged in their inbox with a per-row officer-type tag.
5. Current state → future state
Section titled “5. Current state → future state”| Pain point | Today (WhatsApp) | After this release |
|---|---|---|
| Where issues live | Officer WhatsApp threads, scrollback, screenshots. | Atlas queue, one record per issue, full thread visible to both sides. |
| What context the officer sees | Whatever the manager remembers to type. | Full payload: rider, wallet, battery, transaction, bank, station, manager, timestamp — captured at report time, never re-typed. |
| How a stuck rider stops blocking | Doesn’t. Manager and rider both wait. | Manager parks the swap, serves the next rider, returns to the parked swap on push. |
| How wrong-rider mistakes happen | Officer copies “Adekunle, 080…” from WhatsApp, mistypes the wallet ID, hits Issue Overdraft. | Resolution actions take only the loaded case payload as input. Rider/wallet/battery IDs are not editable on resolution. |
| How re-requests for clearer evidence work | “Please send a clearer photo” in WhatsApp; the new photo lands in a different thread or DM. | First-class re-request loop in the issue thread. New uploads attach to the same issue record. v1/v2 version chips render in Atlas. |
| How escalation between teams works | Officer forwards the WhatsApp thread or @-mentions another team. Hand-off is lossy. | One-click route handoff. Receiving officer-type queue inherits the full record + thread; originating officer can view but no longer resolve. |
| What the PM can see | Nothing aggregate. | Volume by issue type, by officer, by station; time-to-resolve distributions; rejection / clarification ratios. |
6. Issue type specs
Section titled “6. Issue type specs”Each issue type follows the same template: trigger · report-side flow · required fields & evidence · resolve-side flow · owner · escalation · edge cases · acceptance criteria.
Existing prototype screens are cited where they map.
Category 1 — Station Assignment
Section titled “Category 1 — Station Assignment”1.1 Temporary Swap Station Request
Section titled “1.1 Temporary Swap Station Request”- Trigger. Rider attempts a swap at a station that is not their primary; alert fires on the rider-profile step of Rechaj Swap (
alert-mismatchinline onscreen-tricycle-detected). - Report-side flow (Rechaj Swap). Manager taps Request authorization in the inline alert → sheet
sheet-stationopens with rider/plate/station pre-filled → manager picks a reason chip (Battery emergency · Customer en route · Station closed · Other →Tell support…textarea + voice note) → submits. Swap auto-parks on submit; manager serves next rider. - Required fields. Reason chip; if Other, free-text or voice note. All identifiers pre-populated.
- Resolve-side flow (Atlas).
screen-issue-temp-swapopens with rider context, primary-vs-current station, temp-swap history (count this week). Officer picks Approve / Deny. - Owner. Swap Support Officer.
- Escalation. None in this release.
- Edge cases. Rider’s primary is the same as the current station (issue should not be raisable). Same rider raises ≥3 temp requests in 7 days — flag visible on the resolution screen, no auto-action. [Derived] Auto-deny if rider has an unresolved overdraft (links to 2.2) — confirm in Q3.
- Acceptance criteria.
- Sheet pre-fills rider name, plate, primary station, current station without manager input.
- Submission triggers swap park; manager can start a new swap immediately.
- Atlas screen shows temp-swap history count for the rider in the last 7 days.
- Approve triggers push to manager; manager resumes the parked swap and the alert is gone.
- Deny triggers push with the officer’s reason; resumed swap shows a “cancel · rider returns to primary” state. [Derived]
- Data model captures
intent: temporaryso a future penalty rollup can compute counts per rider per period without a schema change. [Derived]
1.2 Primary Swap Station Reassignment
Section titled “1.2 Primary Swap Station Reassignment”- Trigger. Manager initiates from the Rechaj Swap support log: Quick request → Reassign primary station (
screen-perm-change-lookup). - Report-side flow. Manager scans rider’s keke QR or enters plate + phone → confirms identity → 2-step form: select new primary (auto-suggests current station) + reason chip + optional note/voice → submits →
screen-perm-change-statusshows pending state. - Required fields. New primary station; reason chip; optional rationale.
- Resolve-side flow (Atlas).
screen-issue-perm-changeshows rider, current primary, requested primary, distance from rider’s address, recent temp-swap count, manager’s reason. Officer picks Approve immediately / Approve effective next Monday / Deny + reason. - Owner. Swap Support Officer.
- Escalation. None.
- Edge cases. Same-station request must be rejected at submit. Same rider requests ≥1 reassignment / 30 days — flag in Atlas; allow override. [Derived — see Q4: cooldown policy.] Approval ≠ same as immediate state change if scheduled for later.
- Acceptance criteria.
- Manager cannot submit if requested primary equals current primary.
- Atlas resolution shows recent temp-swap pattern as decision context.
- Approve writes the new primary; manager sees confirmation push; future temp-swap alerts no longer fire for this rider at the new primary.
- Effective-date supported: Approve-immediately vs Approve-scheduled.
- Audit entry captures previous primary, new primary, scheduled-vs-immediate flag, officer ID, manager ID, rider ID.
Category 2 — Wallet & Payment
Section titled “Category 2 — Wallet & Payment”Baseline behaviour: the first swap of the day debits the daily subscription (currently ₦12,000) from the rider wallet; subsequent swaps the same day show “Paid” / ₦0. All wallet flows enter through the “How is the rider paying?” consolidated alert on the rider-profile step (
alert-paymentinsupport-cases-v3.html), withWallet historyaccessible from the wallet hero on every wallet-state screen.
2.1 Payment sent but not reflecting
Section titled “2.1 Payment sent but not reflecting”- Trigger. Rider claims to have transferred funds; wallet still shows insufficient balance.
- Report-side flow. “How is the rider paying?” → Log rider’s receipt opens
sheet-reconcile→ manager picks Single / Multiple payments → per-payment: amount (only; sent-at is dropped per design rule) + virtual account (Opay / Paystack / Monnify) + sender bank + photo of receipt (take or upload) → submits. - Required fields per payment. Amount, destination virtual account, sender bank, receipt photo. Yellow callout pre-submit reminds the manager that the transaction reference or session ID must be readable.
- Resolve-side flow (Atlas).
screen-issue-funding-pendingopens with: large receipt preview at top (480px), version chips (v1, v2 if retaken), claimed amount, destination VA, claimed sender + bank, rider wallet activity (last 7 days), and an “Action” select. Resolution form requires the officer to pick Transaction reference vs Session ID (radio), then paste the value from the receipt into a single input. Officer ticks the overdraft eligibility confirmation, then taps Issue overdraft & resolve to credit ₦12,000 against the pasted reference. Credit is reconciled later by the settlement matcher. - Owner. Swap Support Officer (with overdraft-eligibility guardrail on the wallet service, enforced server-side; UI checkbox mirrors the check).
- Escalation. None — receipt re-request loop replaces escalation. If the receipt is unclear: officer types a reason in the inline “Receipt unreadable?” block (which sits directly under the reference field) → taps Send back · request clearer receipt → manager receives a push, opens the support thread on the swap app side as a full-page sheet, taps the previously uploaded photo to view, then taps Reupload photo to retake → v2 chip lands in Atlas, preview swaps, thread captures both turns.
- Edge cases.
- Multiple payments claimed: each is a slot in the reconcile sheet; the officer can issue one overdraft per payment, all linked to the same case.
- Photo retake count is unbounded; each becomes a new version chip.
- Receipt reference duplicates an existing settlement match — wallet service must reject the overdraft as already-credited (idempotency on
referencefield). [Derived]
- Acceptance criteria.
- Manager cannot submit without at least one receipt photo per payment slot.
- Atlas resolution refuses to issue an overdraft if the eligibility checkbox is unticked OR the radio choice + reference field is empty.
- “Send back” triggers a manager push within 10s; manager’s reupload appears as v2 in Atlas within 5s of upload completion.
- Resolution writes one wallet credit per overdraft, keyed by the reference value (idempotent).
- Audit entry captures case ID, officer ID, reference value, reference kind (transaction-ref / session-id), and overdraft amount.
2.2 Pending overdraft blocking a swap
Section titled “2.2 Pending overdraft blocking a swap”- Trigger. Rider scans for a swap; rider wallet exposes
swap_eligible: falsebecause a prior overdraft (from 2.1) has not yet been reconciled by inbound settlement. Inline danger alert fires (alert-overdraft-pending). - Report-side flow. Send to support opens
sheet-overdraft-pending→ manager re-uploads the receipt of the unsettled payment (with the same clarity expectation as 2.1) → submits. - Required fields. Receipt photo. Case ID is not shown to the manager; it stays internal. (See the no-tech-codes rule.)
- Resolve-side flow (Atlas). Officer reviews the receipt and the rider’s wallet activity; if the inbound credit has now landed, the officer lifts the overdraft ban so the rider can swap. If still missing, the officer can keep the ban + send back for clarification.
- Owner. Swap Support Officer.
- Escalation. None.
- Edge cases. Same rider’s wallet activity already shows a successful settlement that the eligibility flag missed — officer can lift directly without an overdraft action. [Derived]
- Acceptance criteria.
- The rider profile shows the overdraft alert with no manager override path (
Send to supportis the only CTA). - The resolution surface shows the originating 2.1 case linked, plus current wallet activity.
- Lifting the ban writes
swap_eligible: trueimmediately; manager receives a push; the next scan succeeds. - Audit captures the lift action, prior ban reason, case linkage.
- The rider profile shows the overdraft alert with no manager override path (
2.3 Wallet not funded at all
Section titled “2.3 Wallet not funded at all”- Trigger. Rider scans; wallet is ₦0 and no prior overdraft. Manager surfaces the consolidated alert.
- Report-side flow. “How is the rider paying?” → Show funding accounts opens
sheet-paymentshowing the rider’s virtual accounts (Opay primary, Paystack, Monnify) with copy-to-clipboard. No officer interaction by default; the manager parks the swap to serve other riders. Resolves when the wallet shows ≥₦12,000 (poll/push). - Required fields. None — no support officer involved.
- Resolve-side flow (Atlas). No queue entry by default.
- Owner. Self-resolving.
- Escalation. Manager can convert to a 2.5 (Invalid funding account) report if the rider says the account number is being rejected.
- Edge cases. Wallet credit arrives while swap is parked → push resume.
- Acceptance criteria.
- Funding accounts sheet pulls the rider’s virtual accounts via the wallet/account service; no manager input.
- While parked, wallet polls every 30s for balance ≥ daily fee; on threshold, push fires and resumed swap auto-advances.
- No support case is created unless the manager escalates to 2.5.
2.4 Phantom pending swap fee
Section titled “2.4 Phantom pending swap fee”- Trigger. Rider has already completed at least one swap today (subscription debited; subsequent swaps should be “Paid · ₦0”); on a later swap the wallet hero wrongly shows a fee owed.
- Report-side flow. “How is the rider paying?” → Rider had previously paid today →
sheet-previously-paidopens with rider context and an inlined snapshot of today’s prior swap fee debit + subsequent free swap, with prose: “If you see today’s debit here, send to support to clear the duplicate fee” → manager submits. - Required fields. Rider context is auto-attached. No additional input required from the manager beyond the submission tap; the wallet history inline confirms the manager’s claim.
- Resolve-side flow (Atlas). [Derived screen — to be built. Tentative ID:
screen-issue-phantom-fee.] Officer reviews the rider’s full wallet history (read-only). Cannot resolve; Escalate to Engineering Support Officer is the primary action. Engineering loops in the platform team off-platform; once engineering reports the fix is in, Swap Support officer alerts the manager via the thread; manager re-attempts the swap. - Owner. Swap Support Officer triages; Engineering Support Officer resolves.
- Escalation. Mandatory: Swap Support → Engineering Support.
- Edge cases. Re-occurrence on the same rider within 24h after a “fix landed” — re-open the escalated case instead of creating a new one. [Derived]
- Acceptance criteria.
- The “Rider had previously paid today” button is only shown when the wallet-short consolidated alert is visible.
- Submission creates a case with type
phantom-fee; routing assigns to Swap Support’s queue first. - Atlas resolution view shows wallet history with today’s earlier swap fee debit highlighted; Resolve is disabled, Escalate to Engineering is enabled.
- Escalation moves case ownership to Engineering Support queue; original officer retains read access.
- Engineering’s
mark-fixedaction writes a thread message and a push to the manager.
2.5 Invalid funding account
Section titled “2.5 Invalid funding account”- Trigger. Rider’s virtual funding account (Opay / Paystack / Monnify) is being rejected by the rider’s bank (the destination account number does not return a beneficiary name).
- Report-side flow. “How is the rider paying?” → Funding account invalid opens
sheet-acct-invalid→ manager picks affected provider (Opay / Paystack / Monnify; no “Other”) → enters the account number shown to the rider → takes/uploads photo of the error on the rider’s screen → submits. - Required fields. Provider chip, account number shown, screen-error photo.
- Resolve-side flow (Atlas). [Derived screen — to be built. Tentative ID:
screen-issue-acct-invalid.] Officer reviews provider + shown number + screenshot. Cannot resolve; Escalate to Engineering Support Officer. - Owner. Swap Support Officer triages; Engineering Support Officer resolves.
- Escalation. Mandatory: Swap Support → Engineering Support.
- Edge cases. The same provider + account shows up in multiple manager reports within 1 hour — flag in Atlas as a likely platform-wide outage. [Derived]
- Acceptance criteria.
- Sheet refuses to submit without the screen-error photo.
- “Other” is not in the provider chip list.
- Atlas escalation routes to Engineering Support; Engineering’s
mark-fixedaction writes a thread message and a push to the manager.
Category 3 — Battery
Section titled “Category 3 — Battery”Per the battery-photo-required rule, every battery case requires the manager to upload a clear photo of the physical battery’s serial sticker. Every battery case supports the same retake loop as 2.1.
3.1 Release & Reassign
Section titled “3.1 Release & Reassign”- Trigger. Manager scans a battery to issue to a rider; app shows the battery is digitally attached to a different keke (off-app field swap left a stale record).
- Report-side flow. Inline alert on
screen-scan-new-battery→ Send to support openssheet-battery-releasewith the scanned serial, rider, plate, current digital holder (rider name + plate as a small pill, per thekeke-identityrule), this station, manager. Manager attaches a photo of the physical battery; submits. - Required fields. Battery photo. All identifiers auto-attached.
- Resolve-side flow (Atlas).
screen-issue-battery-releaseopens with photo evidence (large preview + version chips), conversation thread, calling station, current digital holder. Officer’s primary action: Release & reassign · resolve — one-click record mutation. Send-back-for-clearer-photo CTA in the resolution card. - Owner. Swap Support Officer.
- Escalation. None.
- Edge cases. Photo unreadable → send back; v2 lands in the thread and as a new version chip.
- Acceptance criteria.
- Submission attaches the battery serial as the case key; idempotent on
(case_id, action). - Resolution flips the inventory record atomically: detach from previous keke, attach to calling station.
- Resume push fires to the manager; manager’s swap app shows the resolved alert and proceeds.
- Submission attaches the battery serial as the case key; idempotent on
3.2 Station Claim
Section titled “3.2 Station Claim”- Trigger. Manager wants to digitally claim possession of one or more physical batteries that are at their station but recorded elsewhere.
- Report-side flow. From the support log: Quick request → Battery transfer (
sheet-battery-transfer) → for each battery: tap the QR-scan affordance (opens a camera-overlay scanner — reticle + scan line; locks on after ~1.6 s and commits the serial) + photo of the physical battery + optional notes; manager can add multiple battery slots in one submission → submits. Each battery becomes one issue record (parallel cases), all linked to one submission for the manager’s view. - Required fields per battery. Serial (via scanner or typed); photo.
- Resolve-side flow (Atlas).
screen-issue-battery-claimfor the single-battery scan path;screen-issue-battery-transferfor the manager-initiated multi-battery path (current location, calling station, photo evidence per battery). Per-battery one-click claim. - Owner. Swap Support Officer.
- Escalation. None.
- Edge cases. A battery in the submission is currently with another keke digitally → force-claim is allowed; the other keke’s next swap surfaces as 3.3, which self-heals (per the battery-cause framing in
swap-support/overview.md). - Acceptance criteria.
- Each battery in a multi-battery submission produces one independent case.
- Atlas displays the per-battery photos as thumbnails, each clickable to full size.
- Resolving one battery in a multi-battery submission does not affect the others.
3.3 Old Battery Claim
Section titled “3.3 Old Battery Claim”- Trigger. Rider arrives with a depleted battery whose serial doesn’t return a current record after VCU pair (the previous swap was not recorded).
- Report-side flow. Empty state on the VCU connected pane shows “Battery serial not on record” → Send to support opens
sheet-battery-attribute(sheet name preserved for code continuity; user-facing language is assignment, per the assignment-not-attribution rule) → manager attaches a photo of the physical battery’s serial sticker; submits. - Required fields. Battery photo. Rider, plate, station pre-attached.
- Resolve-side flow (Atlas).
screen-issue-battery-attributewith photo + thread (currently shows v1 rejected / v2 current as the canonical demo). Officer reads the serial from the photo, enters it in the resolution field, taps Assign serial · resolve. Send-back-for-clearer-photo CTA in the resolution card. - Owner. Swap Support Officer.
- Escalation. None.
- Edge cases. Photo unreadable → retake loop. Officer reads a serial that’s not in inventory at all → escalate as 3.4. [Derived]
- Acceptance criteria.
- The empty-state card is only shown when VCU pair returns no current battery; the inline alert blocks the unlock toggle until support assigns a serial.
- Resolution writes the assignment atomically; manager’s VCU card refreshes with the assigned serial; the unlock toggle becomes available.
- Send-back appends a support message to the thread immediately and simulates the retake loop end-to-end.
3.4 Serial Mismatch
Section titled “3.4 Serial Mismatch”- Trigger. Manager scans a new battery; the serial returns no inventory data (typo / worn label / off-by-one print).
- Report-side flow. Inline alert on
screen-scan-new-battery→ Send to support openssheet-battery-mismatchwith the scanned serial + photo of the physical battery → submits. - Required fields. Battery photo. Scanned serial auto-attached.
- Resolve-side flow (Atlas). Swap Support Officer reviews; cannot resolve — escalates to Inventory Support Officer. Inventory officer searches inventory manually (no auto-suggested closest match per the no-auto-serial-match rule), picks the correct serial, pushes the corrected serial back. The manager copies, prints, sticks the new label, then rescans (validated against the issued serial; mismatches captured for audit).
- Owner. Swap Support Officer triages; Inventory Support Officer resolves.
- Escalation. Mandatory: Swap Support → Inventory.
- Edge cases. Manager’s rescan after relabel does not match — captured separately; manager can re-request via thread.
- Acceptance criteria.
- Escalation moves the case to the Inventory Support queue; Swap Support officer retains read access.
- Inventory’s “Issue corrected serial” action pushes the serial to the manager + opens the print/stick UI in Rechaj Swap.
- Subsequent rescan compares to
__issuedSerial; mismatch shows the manager a specific error and writes an audit entry.
“Other” — Report a new issue type
Section titled ““Other” — Report a new issue type”- Trigger. Manager has an issue that doesn’t match any of 1.1–3.4.
- Report-side flow. Support page → Something else… opens
sheet-support→ manager picks an issue-category chip (Equipment fault · Rider dispute · Station / facility · Cash handling · App / connectivity · Safety / incident · Other → free text + voice note), describes what happened, optionally attaches a photo and a voice note, submits. - Required fields. Category chip; free-text description; everything else optional.
- Resolve-side flow (Atlas). Lands in a separate triage queue [Derived — tentative screen
screen-triage-queue] visible to a “Triage” lane shared by Swap Support and Engineering Support. Triage officer can:- Re-route to a typed queue if the issue maps to an existing type (
1.1–3.4). - Resolve in-thread for ad-hoc one-offs.
- Promote: a recurring pattern (≥N reports in M days) becomes a candidate for formalization. [Derived — confirm threshold in Q5.]
- Re-route to a typed queue if the issue maps to an existing type (
- Owner. Triage lane (Swap Support + Engineering Support shared) [Derived].
- Escalation. Re-routing replaces escalation.
- Edge cases. Safety / incident category should route directly into a priority lane (or trigger a paging integration). [Derived — see Q6.]
- Acceptance criteria.
- “Other” cases produce no type-specific resolution actions — only generic resolve / re-route / message.
- Atlas exposes a per-category count over the last 30 days to support promotion-to-formal-type reviews.
7. Cross-cutting features
Section titled “7. Cross-cutting features”7.1 Park-a-Swap
Section titled “7.1 Park-a-Swap”- Behaviour. When a manager submits any issue that blocks the active swap, the swap auto-parks; the manager’s queue moves on to the next rider. Manager can also park manually before submitting an issue.
- Multiple parked swaps. A manager can hold multiple parked swaps simultaneously. Busy stations routinely have several stuck riders on different issues at once. (User flagged this as [Derived] in the brief; confirming as the default — see Q7.)
- Parked queue surface. Dedicated section on the manager’s home screen lists every parked swap with rider name, plate, issue type, elapsed time, current status (Awaiting support / Needs your reupload / Resolved · tap to resume).
- Resume path. Tapping a parked entry returns the manager to the exact swap-app step they parked from, with the resolution applied (if resolved) or the latest thread message visible (if awaiting).
- Notifications. Push on resolution; push on new thread message that requires manager action (retake); soft in-app banner for thread messages that don’t require action.
- Acceptance criteria.
- Parking does not lose any captured context; resume re-renders the same screen with the same data.
- Concurrent parked swaps render in a stable order (oldest first), with a count badge on the home tab.
- Resolution push deep-links to the parked swap, not the home screen.
- Two simultaneous parked swaps with different issue types can both resume independently without state collision.
7.2 Message Thread per issue
Section titled “7.2 Message Thread per issue”- Surface. Rechaj Swap: full-page sheet (
sheet-support-thread,sheet-fullpagevariant). Atlas:.thread-miniblock inside the issue resolution screen. - Capabilities. Officer and manager exchange text messages; manager can attach photos / voice notes; officer can attach text only (no officer-side uploads in this release — confirm in Q8). [Derived]
- Retake loop (first-class). Officer’s send-back-for-clearer-photo action writes a message AND simulates a structured retake request. Manager’s reupload appends a new version chip on the Atlas evidence preview AND a new thread message with the photo attached. This is the canonical retake mechanism; ad-hoc “please send a clearer one” text-only messages are allowed but bypass the version-chip workflow.
- Photo viewing. Previously uploaded photos in the thread are tappable in Rechaj Swap; they open in a lightbox so the manager can compare against what they’re about to retake.
- Persistence. Messages and attachments are immutable. Edits are appended messages, not edits-in-place.
- Acceptance criteria.
- Both sides see the same messages in the same order in real time (≤2s lag).
- Send-back from Atlas appends the officer message immediately; the manager push fires within 10s.
- Manager’s reupload writes one message with the photo attached AND one version chip on the evidence preview.
- Thread persists indefinitely after case resolution; both sides can re-read.
7.3 Auto-Populated Resolution
Section titled “7.3 Auto-Populated Resolution”- Principle. Every resolution action takes only the case payload as input. Officers do not type identifiers that were captured at report time — no rider IDs, no wallet IDs, no battery serials, no transaction references, no station codes. Free-text fields are limited to (a) judgement notes (recorded for audit), (b) values that genuinely could not be captured automatically (e.g. the transaction reference / session ID from a photo).
- Enforcement. Server-side: every resolution endpoint accepts a
case_idand reads identifiers from the case record. Mismatched identifiers in the request body are rejected. UI-side: identifiers are non-editable display elements; the only inputs are decision dropdowns, notes, and the genuinely-uncaptured fields above. - Wrong-rider hard rule. Wallet credit endpoints (overdraft, manual top-up, refund) reject any request where the rider ID in the request does not match the rider ID on the linked case. This is mirrored by the overdraft-eligibility checkbox in the UI.
- Acceptance criteria.
- No resolution form in Atlas has a free-text input for rider ID, wallet ID, battery serial, station code, or any identifier present in the case payload.
- Server-side endpoint smoke tests confirm rejection of mismatched identifiers.
- The audit entry records
case_idand the loaded identifiers used in the resolution.
7.4 Feedback & Rating
Section titled “7.4 Feedback & Rating”- Trigger. Swap-complete screen in Rechaj Swap, after every swap (whether or not an issue occurred).
- Surface. Compact module on
screen-swap-complete: star rating (1–5), optional free-text, optional voice note, optional photo, Submit chip inline with the attach affordances. Renders above the CTAs (Start another swap · Back to dashboard) so all are in the viewport. - Resolve-side surface (Atlas).
screen-manager-feedback, reachable from the sidebar entry “Manager feedback” and from a “View all feedback →” link on the support dashboard snippet. KPI tiles (today’s average, 5-star share, ≤2-star share, attachment count), filter pills (per rating, with photo, with voice), and a full table of submissions. - No officer action by default. Feedback is read-only. [Derived] Future: low-rated feedback with an attachment could auto-create a Triage case — confirm in Q9.
- Acceptance criteria.
- Submit requires at least a rating (1–5 stars); other fields optional.
- Atlas feedback feed updates within 30s of submission.
- PM dashboard surfaces today’s averages and trends; per-station drill-down available.
7.5 Officer types & role-based access
Section titled “7.5 Officer types & role-based access”- Model. Officer types are records in a
BusinessUnit×OfficerRolematrix. Issue types are tagged with a list of officer-type owners (typically one primary + zero or more escalation targets). - Scoping. Each officer’s Atlas queue is filtered to issue types they own (primary or escalated-to). A multi-role user sees a merged queue with per-row officer-type tags.
- Escalation = routing handoff. Escalating an issue switches the
owning_officer_typefrom the originating type to the target type. The originating officer retains read access but cannot resolve. Receiving officer’s queue gains the case immediately. - Atlas surface. [Derived] Sidebar nav lists scoped queues per officer-type the user holds. A user with Swap Support + Payment roles sees “Swap Support · 14”, “Payment · 3” as separate nav entries plus a merged “All open · 17” entry. Confirm in Q10.
- Acceptance criteria.
- RBAC enforced at the action layer; UI hides actions the user cannot take.
- Escalation writes an audit entry capturing both officer types and the originating officer’s ID.
- Adding a future officer type (e.g. Energy) requires only a config row + queue route; no code changes for unrelated officer types.
8. Data model implications
Section titled “8. Data model implications”Ground entities in existing prototype state where possible (scenarioMode, RIDER_BY_MODE, CASE_META, case state machines already drafted in swap-support/cases-and-backend.md). New / extended entities:
8.1 Core entities
Section titled “8.1 Core entities”| Entity | Purpose | Notable fields |
|---|---|---|
Issue |
The case record itself. One per support report. | id, type (1.1–3.4 + ‘other’), category (‘station’ / ‘wallet’ / ‘battery’ / ‘other’), status (open / in-progress / pending-evidence / escalated / resolved / denied), owning_officer_type, originating_officer_type (nullable, for escalations), reporter_id (manager), created_at, resolved_at, parked_swap_id (nullable). |
IssueType |
Catalogue of the 11+1 types. | id, category, primary_owner_officer_type, escalation_targets[], requires_photo_evidence (bool), report_sheet_id, resolve_screen_id. |
IssuePayload |
The pre-captured context for an issue. Read at resolution time; never overwritten. | issue_id, rider_id, wallet_id, battery_serial, station_id, manager_id, transaction_reference (nullable), originating_bank, destination_va, scanned_serial, free context_json. |
IssueMessage |
One message in the per-issue thread. | id, issue_id, from_side (‘manager’ / ‘officer’), from_user_id, body, created_at. |
IssueAttachment |
Photo / voice attached to a message OR to the initial submission. | id, issue_id, message_id (nullable), kind (‘photo’ / ‘voice’), version (int, monotonic per kind per issue), storage_uri, created_at. |
EvidenceVersion |
[Derived] Computed view of attachments grouped by kind=photo; powers the Atlas version-chips strip. Materialized or query-time. |
|
ParkedSwap |
A swap that’s been set aside. | id, manager_id, rider_id, plate, parked_at_step (current Rechaj Swap screen ID + state), linked_issue_id (nullable for manual parks), current_status, last_thread_message_at. |
OfficerRole |
A role assignment on a user. | user_id, officer_type, business_unit_id, active. |
OfficerType |
A typed role (Swap Support / Payment / Engineering / Inventory / Facility / Energy). | id, name, business_unit_id, scope_default (queue filter). |
BusinessUnit |
Top-level org division. | id, name, active. |
ResolutionAction |
A typed action an officer can take. Concrete actions: issue-overdraft, lift-overdraft-ban, reassign-battery, claim-battery, assign-battery, update-primary-station, approve-temp-swap, deny-temp-swap, issue-corrected-serial, escalate-to-engineering, escalate-to-inventory, request-clarification, resolve-other. |
id, issue_type[], requires_officer_type[], idempotent_on[]. |
AuditLog |
Append-only record of every action. | id, actor_user_id, actor_officer_type, action_type, target_id, case_id, before_state, after_state, delta, timestamp, note. |
SwapFeedback |
One per completed swap. | id, swap_id, manager_id, rating (1–5), note, voice_uri, photo_uri, created_at. |
8.2 Relationships
Section titled “8.2 Relationships”Issue1–1IssuePayload· 1–NIssueMessage· 1–NIssueAttachment· 0–1ParkedSwap.IssueTypedefinesprimary_owner_officer_type(one) andescalation_targets[](zero or more).OfficerRoleis many-to-many betweenUserandOfficerType; a user with multipleOfficerRolerows sees the union of queues.ResolutionActionis gated byrequires_officer_type[]; rejecting a call where the actor’s officer type is not in the list is a server-side hard check.
8.3 State transitions
Section titled “8.3 State transitions”open→in-progresson first officer view.in-progress→pending-evidenceon request-clarification (officer sends back).pending-evidence→in-progresson manager reupload.in-progress→escalatedon escalate-to-* action;owning_officer_typechanges.in-progress→resolved/deniedon resolution action.escalated→in-progress(in the receiving queue) on first view by the receiving officer.
8.4 Data model conventions
Section titled “8.4 Data model conventions”- No technical case codes are user-facing for swap managers. Internal IDs (case ID, request ID) exist for back-end / officer reference; manager-facing surfaces hide them per the no-tech-codes rule.
- Money-moving endpoints are idempotent on
(case_id, action_type, request_key)per the existing backend spec. - Audit entries are immutable. Corrections are new entries that reference the original by
correction_of.
9. Acceptance criteria
Section titled “9. Acceptance criteria”Per-issue-type and per-feature acceptance criteria are listed inline in §6 and §7. Top-level acceptance for the release:
- No resolution form in Atlas requires the officer to type an identifier that was captured at report time. Verified by code grep on resolution screens + endpoint contract tests.
- The 17 May wrong-rider incident class is reproducibly blocked: integration test issues an overdraft against case A’s payload using case B’s rider ID in the request body → server returns 400.
- All 11 issue types + Other have a working end-to-end report → resolve flow in staging, exercised by manual test plan + automated smoke.
- A manager can park ≥3 swaps concurrently and resume each independently.
- Send-back-for-clearer-photo round-trips end-to-end on 2.1, 3.1, 3.3 (and 3.2, 3.4 where photo is required) in ≤30s wall-clock on staging.
- Every resolution action writes an immutable audit entry with the full payload.
- RBAC: a user assigned only Swap Support cannot view or take action on a 2.4 escalated to Engineering.
- Feedback submissions land in the Atlas Manager feedback module within 30s and aggregate into the support dashboard snippet.
10. Success metrics
Section titled “10. Success metrics”| Metric | Target | Source |
|---|---|---|
| % of support traffic in-app vs WhatsApp | ≥95% within 1 month of GA | Support team weekly review |
| Median time-to-acknowledge | < 2 min (in-swap-blocking issues) | AuditLog first-view event |
| Median time-to-resolve | < 10 min (3.1, 3.2, 2.1); < 24h (1.2) | AuditLog resolve event minus Issue.created_at |
| Wrong-rider-class incidents | 0 | Incident log; expected 0 by design |
| Manager throughput under park | ≥3 subsequent swaps completed per parked swap before resume | ParkedSwap + swap completion events |
| Re-request rate per issue type | Track; expect 2.1 highest (receipt clarity), declining over time | IssueMessage with kind=clarification |
| Issue-type distribution | Track; expect battery group ≈ 50% in week 1, declining as inventory-cause backlog clears | Issue.type aggregations |
| Feedback rating average | Track; expect ≥4.0; sub-station drill-down for outliers | SwapFeedback.rating |
Instrument all events via the audit log + a thin metrics layer; no separate analytics pipeline for this release. [Derived]
11. Open questions
Section titled “11. Open questions”- SLA targets. Are the [Derived] medians above (2 min ack / 10 min resolve) the right anchors, or should we anchor to p95 / p99? — Ops + Eng.
- Payment Officer scope. Which 2.x issues, if any, route to Payment Officer directly vs flow through Swap Support first? Or is Payment Officer purely an escalation target for 2.4 / 2.5 alongside Engineering Support? — Finance + Ops.
- Temp-swap auto-deny on outstanding overdraft. Should 1.1 be blocked at submit if the rider has an unresolved 2.2? Or surface as a warning to the officer? — Ops.
- Primary-station-change cooldown. Should the same rider be prevented from requesting 1.2 more than once / 30 days? — Ops + Finance (wallet-arbitrage prevention).
- “Other” promotion threshold. What recurrence count (N reports in M days) triggers a candidate-for-formalization review? — PM.
- Safety / incident routing. Should “Other → Safety / incident” trigger a paging integration immediately, or sit in the Triage queue? — Ops + Eng.
- Multiple parallel parked swaps. Confirming as the default per the brief’s [Derived] flag — any cap on concurrent parked swaps per manager? — Ops.
- Officer-side uploads in the thread. Should officers be able to attach references / screenshots in the thread, or text-only? — Support team.
- Auto-Triage on low feedback. Should rating ≤2 with an attached photo / voice auto-create a Triage case? — PM + Ops.
- Multi-role queue layout in Atlas. Per-officer-type sub-queues + merged “All open” — confirm vs single-merged queue with type filter. — Support team.
- Retiring older Atlas screens.
screen-issue-mgr-transfer(station-wallet payment failed, was scenario 7) andscreen-issue-deduction-fail(daily-fee deduction failed, was scenario 5) are not in the new 11-type taxonomy. Retire fully, or keep as engineering-debug-only surfaces? — Eng. - Phantom-fee + Acct-invalid Atlas screens. Tentative IDs
screen-issue-phantom-feeandscreen-issue-acct-invalidare [Derived] — confirm naming. - Feedback data retention. How long do we keep voice notes? — Legal + Ops.
Cross-references
Section titled “Cross-references”- Earlier (superseded) iteration:
specs/2026/swap-support/ - Rechaj Swap prototype:
products/rechaj-swap-app/prototypes/support-cases-v3.html - Atlas prototype:
products/atlas/prototypes/swap-support-resolution.html - Patterns library:
patterns/ - Design system:
DESIGN.md