UC 8.3.1
Flow 8 - Pay Raise Engine (Dual-Rail, Level 7+): recalculates compensation on every promotion to Level 7 or above
| Field | Value |
|---|---|
| BA in Charge | Huyen Dinh |
| Date Created | 2026-10-11 |
| Version | v4 |
| Document References | Zapier Integration V7.pdf (§Flow 8 — Pay Raise Engine, Dual-Rail, Tables A/B) · RFQ_ Stack Trading Prop Tech V7.pdf (§3.2 Financial & Transaction Endpoints) · client QnA thread (QnA_Level_Promotion.md §5, Round 1–5) |
1. Overview
| Field | Content |
|---|---|
| ID | UC_8.3.1 |
| Use Case | Flow 8 - Pay Raise (L7+) |
| Description | Every time a trader is promoted to Level 7 or above, the Middleware recalculates their compensation.[CHR-123] For US W-2 traders, this is a manual-assisted update via a Freshdesk ticket (Rippling has no public API for this). For International contractors, the Middleware calls Dots directly to pay a prorated top-up and update the recurring retainer. |
| Zapier Flow | Flow 8 (Pay Raise Engine, Dual-Rail) — invoked synchronously from Flow 4A (UC_8.1.1), Stage 3 Step 3 (Blocking gate — Flow 6 / Flow 8 redirect), when New_Level > 6 |
| Zapier Table | Table A (Forex Level Params —Base_Income, Base_Income_Flex, Title) · Table B (Futures Level Params — same fields) |
| 3rd Party | Rippling (US W-2 payroll — manual update only, no API) · Dots (International contractor payouts, one-off top-up + recurring retainer) · Freshdesk (operational ticket for Rippling manual update) · Slack (Ops alert) |
Trigger
UC_8.1.1 (Flow 4A), Stage 3 Step 3 (Blocking gate — Flow 6 / Flow 8 redirect) — when a trader's New_Level > 6, the Middleware synchronously invokes Flow 8 and blocks all remaining Stage 3 steps (Reset Balance, Update Risk, Re-Enable Trading) until Flow 8 returns a completed status.
Pre-conditions
- The trader is being promoted to
New_Level > 6 Users.compensation_track(Comp_Track) is set — either'Standard'or'Flex'— read as-is from the DB, never recalculated.
Post-conditions
- [If skip condition met] → no action taken.
- [US W-2, Path A] →
[CHR-123]a Freshdesk ticket + Slack alert has been generated for Ops to manually update Rippling. The trader's compensation inside Rippling itself is NOT yet updated (pending manual action) — but the promotion flow itself is complete, and the trader can trade immediately. - [International, Path B] → a one-off Dots top-up payout has been dispatched (if applicable) and
user_employment_profiles.dots_monthly_retainerhas been updated to the new rate.
Basic Flow
Step 1 — Data Lookup & Rate Selection
-
The Middleware retrieves from Table A (Forex) or Table B (Futures):
Base_Income,Base_Income_Flex, andTitle. -
The Middleware reads
Users.compensation_track(Comp_Track) as currently stored in the DB — this field is backend-only and can only be changed manually by HR/Operations inside Rippling; the trader can never change it via the dashboard. -
Rate selection:
- [If
Comp_Track == 'Standard'] →New_Pay = Base_Income. - [If
Comp_Track == 'Flex'] →New_Pay = Base_Income_Flex. - International contractors default to
Base_Income_Flex.
- [If
-
Skip condition BR_8.3.1.1: [If
New_PayandTitleforNew_Levelare both identical to the trader's current pay and title] → the Middleware bypasses the entire flow. No API call, no Freshdesk ticket, no Dots payout, no Slack alert is generated. s -
[Else] → proceed to Step 2 (Path A) or Step 3 (Path B) depending on the trader's employment type (US W-2 vs. International Contractor).
- The Middleware queries
billing_countrydirectly from the PostgreSQLUserstable to decide which path to route the trader into:
- [If
billing_country == 'US'] → Path A (US W-2) - [Else] → Path B (International)
- The Middleware queries
Step 2 — Path A (US W-2)
-
[CHR-123]Since Rippling exposes no public API to update employee compensation, the Middleware fires a webhook to Zapier to generate a Freshdesk ticket and a Slack alert for the Operations team.Freshdesk ticket payload:
Field Value Trader_NameTrader's full name User_IDInternal user ID Rippling_IDTrader's Rippling employee ID New_Job_TitleTitleforNew_Level(from Step 1.1)New_Compensation_RateNew_Pay(from Step 1.3) — Hourly or Annual, perBase_Income_Type- [If only
Titlechanged andNew_Payis unchanged] → the ticket still generates, butNew_Compensation_Rateis set to the current (unchanged) rate — Ops only needs to update the job title in Rippling.
- [If only
-
Operations uses this ticket to manually update the trader's compensation profile inside the Rippling dashboard, backdating the effective date to
Today + 1 Day(calendar-day boundary — see BR_8.3.1.4). -
UI behavior: the trading-account processing modal (triggered by Flow 4A) is not blocked by this manual Rippling update. Once the broker account for
New_Levelis successfully provisioned and the Freshdesk ticket has been successfully created, the modal closes and the trader can trade immediately — regardless of whether Ops has processed the Freshdesk ticket yet.
Step 3 — Path B (International Contractor)
-
The Middleware calls
POST /calculate-prorated-raise. -
Data retrieval:
- Query
Userstable forcompensation_trackandStandard_Hours. - Query Table A/B for
Base_Income,Base_Income_Flex, andBase_Income_TypeusingNew_Level.
- Query
-
Rate selection:
- International contractors default to
Rate = Base_Income_Flex.
- International contractors default to
-
Retainer calculation:
- [If
Base_Income_Type == 'Hourly'] →New_Adjusted_Retainer = Rate * ((Standard_Hours * 52) / 12). - [If
Base_Income_Type == 'Annual'] →New_Adjusted_Retainer = Rate / 12.
Example (Hourly):
Rate = 25(USD/hour),Standard_Hours = 40(hours/week) →New_Adjusted_Retainer = 25 * ((40 * 52) / 12) = 25 * (2080 / 12) = 25 * 173.33 = 4,333.33(USD/month).Example (Annual):
Rate = 72,000(USD/year) →New_Adjusted_Retainer = 72,000 / 12 = 6,000(USD/month). - [If
-
Proration math:
- Old value retrieval: the Middleware retrieves
Old_Adjusted_RetainerandOld_Comp_Trackfrom the values already saved at the time Flow 4A executed this promotion (the value of the trader's compensation parameters immediately before this raise) — readsuser_employment_profiles.dots_monthly_retaineras it stood right before this promotion overwrote it. - First-time Level 6→7 case BR_8.3.1.7: no special-case branch is required — see BR_8.3.1.7.
Proration_Factoris calculated using Business Days Only (US/Central) — excluding both weekends (Sat/Sun) and US Federal/Bank holidays (e.g. Independence Day, Thanksgiving, Christmas), aligned to the standard US corporate banking/payroll calendar.Prorated_Adjustment = (New_Adjusted_Retainer - Old_Adjusted_Retainer) * Proration_Factor— this is the delta/top-up between old and new rate for the remaining days in the current month, NOT the full remaining-days amount at the new rate.
Example: Trader is promoted on 2026-11-18 (US/Central). November 2026 has 30 calendar days; counting Business Days Only from 11-19 to 11-30, excluding weekends and the Thanksgiving holiday (11-26, Thu) and the day after if observed as a bank holiday — assume 8 remaining business days out of a 20 business-day month →
Proration_Factor = 8 / 20 = 0.4.Old_Adjusted_Retainer = 4,000(snapshot at last promotion),New_Adjusted_Retainer = 4,333.33(recalculated in Step 3.4 above) →Prorated_Adjustment = (4,333.33 - 4,000) * 0.4 = 333.33 * 0.4 = 133.33(USD), dispatched immediately as a one-off Dots payout. Starting 2026-12-01, the monthly cron (BR_8.3.1.6) pays the fullNew_Adjusted_Retainer = 4,333.33. - Old value retrieval: the Middleware retrieves
-
Automated action:
- Trigger a one-off Dots API payout for
Prorated_Adjustment, dispatched immediately at the moment of promotion (not deferred to the 1st of next month). [CHR-131]Updateuser_employment_profiles.dots_monthly_retainer = New_Adjusted_Retainer— this value is picked up by the monthly cron job (see BR_8.3.1.6) starting the 1st of next month.- Send a Slack alert to Admin:
"✅ Pay raise logic triggered for Trader [User_ID]."
- Trigger a one-off Dots API payout for
2. Business Rules
BR_8.3.1.1: Skip Condition — Identical Pay/Title
If Base_Income (or Base_Income_Flex, per the trader's Comp_Track) and Title for New_Level are mathematically identical to the trader's current pay and title, the entire Flow 8 pipeline is bypassed — no API call, no Freshdesk ticket, no Dots payout, and no operational alert is generated.
BR_8.3.1.4: Effective Date — Calendar Day, Not Rolling 24-Hour
The new pay rate's effective date is Today + 1 Day, based on the calendar day (US/Central), not a rolling 24-hour timestamp. Example: if a trader hits Level 7 at 9:30 PM on 20/9, the raise becomes effective at 00:00 (midnight) US/Central time on 21/9.
BR_8.3.1.6: Path B Recurring Retainer — Internal Cron, Not Dots-Managed Schedule
[CHR-131] Dots does not support a recurring payout schedule at the API level. Instead:
user_employment_profiles.dots_monthly_retainer(Decimal, PostgreSQL) stores each International contractor's current monthly rate.- An internal cron job runs on the 1st of every month, queries all users with
dots_monthly_retainer > 0, and callsPOST /v2/transfersindividually for each user.
3. Payment Timing Reference
| Track | Base Salary Raise (this flow) |
|---|---|
| US W-2 (Path A) | No one-off payment. Rippling applies the new rate natively within its own nextlagging payroll cycle (arrears). |
| International Contractor (Path B) | Monthly Dots retainer paidin advance on the 1st of the month. Prorated_Adjustment top-up dispatched immediately at promotion time (this flow). |
Changelog
| Date | Version | Updated item | Before | After | Notes |
|---|---|---|---|---|---|
| 2026-10-11 | v1 | Initial creation | N/A | Full SRS for Flow 8 (Pay Raise Engine, Dual-Rail, Level 7+), covering Data Lookup/Rate Selection, Path A (US W-2 Freshdesk degradation), Path B (International Dots cron mechanics), 6 Business Rules | Source:QnA_Level_Promotion.md §5 (Round 1–4) + QnA_init_docs.md (B-01→B-09, B-05 excluded as [BLOCKED]). Level 6→7 first-time transition case intentionally omitted pending BA decision. |
| 2026-10-16 | v2 | Step 3 (Path B) — Old value retrieval; new BR_8.3.1.7 | Level 6→7 first-time transition case intentionally omitted pending BA decision (B-05 BLOCKED) | Closed B-05: added BR_8.3.1.7 — first-time Level 6→7 reads the same Users.dots_monthly_retainer already written by Flow 6 (UC_8.1.2 Step 11 at the time; renumbered to Step 10 on 2026-10-27 after UC_8.1.2 removed its old Step 6 "Close 1099 Account") as Old_Adjusted_Retainer, no synthetic calculation, no special-case branch. | Source: client QnA thread 2026-10-16, resolves QnA_init_docs.md B-05. Applied to QnA_Level_Promotion.md §5 Round 5. |
| 2026-10-20 | v4 | Post-condition, Step 3.5/3.6 (Old value retrieval, Retainer update), BR_8.3.1.6 | Users.dots_monthly_retainer / users.dots_monthly_retainer | user_employment_profiles.dots_monthly_retainer — field moved off the Users table onto its own user_employment_profiles table; column name unchanged | BA update per client direction |
| 2026-10-20 | v3 | Overview "Zapier Flow" row, Trigger section, Step 1.4 skip-condition wording | Documented Flow 8's trigger point as "Flow 4A (UC_8.1.1), Stage 5, Path C" — a "Path C" that does not exist in Stage 5; skip condition said "Flow 4A's promotion is considered complete immediately" | Corrected trigger point to match UC_8.1.1 v1.3+ (which moved the Flow 6/Flow 8 redirect into Stage 3 Step 3, a blocking gate before Reset Balance/Update Risk/Re-Enable Trading — no "Path C" naming): Overview and Trigger now cite "Stage 3 Step 3 (Blocking gate — Flow 6 / Flow 8 redirect)". Skip-condition wording corrected: skipping Flow 8 only releases Flow 4A's Stage 3 blocking gate immediately — it does not mean the overall promotion (Stage 4-5) is complete. | Cross-UC consistency check against UC_8.1.1_v1.md (currently v1.8) while reviewing Flow 8 for related updates — found stale, never updated when UC_8.1.1 v1.3 relocated the redirect from its original Stage 5 position. |