How to use the dynamic AVUI to exercise Wave 2 rule services — what to select, what to validate, and the full scenario catalog.
powershell -File ~/code/ach-poc/start-all.ps15001–5004 must respond. Start it with powershell -File ~/code/ach-poc/start-all.ps1.fetch calls directly from your browser to localhost. CORS is configured on both the process-api and the mock-api.http://localhost instead.The real transfer form. The TO dropdown populates from GET /mock/accounts (60 accounts, grouped by category). The FROM dropdown has 3 mock bank accounts. You enter an amount, pick a contribution reason, and a retirement warning banner appears when an IRA account is selected. Submitting calls POST /ach/contribution with the real snake_case payload.
Shows the history of submitted transactions, fetched from the mock-api’s GET /mock/contributions endpoint. Pending transactions expose a Cancel button that fires DELETE /ach/queued/{txId} against the process-api.
Static reference panel. Shows the actual request and response shape for each of the three new endpoints (POST /ach/contribution, POST /ach/withdrawal/with-reason, DELETE /ach/queued/{txId}) so you know exactly what to send and what to expect.
Ten one-click scenarios that auto-fill the form and submit the request for you. This is the fastest way to see each Wave 2 rule service fire. Each button triggers the exact payload documented below and renders the live response in the API Log panel.
Each scenario below corresponds to one button in the Testing Scenarios tab. You can also run any of them manually from the Make a Transfer tab using the selections listed. Expected responses are the exact bytes returned by the pipeline on 2026-04-24.
What to select:
10000024 — Xander Roth IRA (RRH)20000What the UI shows: a red rejection banner citing a ContributionLimit rule failure with HTTP 422. The API Log panel shows the full request and the exact error body.
What to validate: type: "ContributionLimit", status_code: 422, message reads “Projected contributions $20,000.00 exceed IRS limit $8,000.00 for RRH 2026”. The catch-up maths matter: $7,000 base + $1,000 age-50+ catch-up = $8,000 because the fixture sets IraData.Age=65.
Why it matters: this is the headline proof that ContributionLimitService is live, reads age from mock data, and applies the correct IRS catch-up.
{
"status": "Fail",
"status_code": 422,
"data": {
"error_message": {
"type": "ContributionLimit",
"code": 422,
"errors": [{
"message": "Projected contributions $20,000.00 exceed IRS limit $8,000.00 for RRH 2026"
}]
}
}
}
What to select:
10000024 — Xander Roth IRA (RRH)3000What the UI shows: green success banner with a request ID. Transfer Activity tab gets a new Pending row.
What to validate: status: "Success", status_code: 200, scheduled_execution_date equals today + 1 business day, cm_request_id is a fresh GUID on every click.
Why it matters: proves the full pipeline (all 10 orchestrator steps, all 6 rule services) completes when inputs are valid.
{
"status": "Success",
"status_code": 200,
"data": {
"scheduled_execution_date": "2026-04-27",
"cm_request_id": "a697e969-f2ba-4426-8d76-b951b07afcde"
}
}
What to select:
10000047 — Harris HSA (HMM)1000What the UI shows: red rejection banner, HsaEligibility failure.
What to validate: type: "HsaEligibility", status_code: 422, message “HSA contribution requires HDHP coverage”.
Why it matters: confirms the HSA-specific rule fires only for H-prefixed class codes and gates on the HDHP metadata flag.
{
"status": "Fail",
"status_code": 422,
"data": {
"error_message": {
"type": "HsaEligibility",
"code": 422,
"errors": [{ "message": "HSA contribution requires HDHP coverage" }]
}
}
}
What to select:
10000045 — Hampton HSA (HBK)1000What the UI shows: green success banner; new Pending row in Transfer Activity.
What to validate: status: "Success", status_code: 200, scheduled_execution_date present.
Why it matters: proves the HSA rule doesn’t fire when the HDHP flag is set, and that HSA contributions flow through the pipeline identically to other contributions.
{
"status": "Success",
"status_code": 200,
"data": {
"scheduled_execution_date": "2026-04-27",
"cm_request_id": "..."
}
}
What to select:
10000024 — Xander Roth IRA (RRH)30002025What the UI shows: red rejection banner citing a ContributionYear rule failure.
What to validate: type: "ContributionYear", status_code: 422, message reads “Prior-year 2025 contribution window closed (after Apr 15)”. Note: this scenario is date-dependent. It only fails from Apr 16 onwards; before Apr 15 the same payload passes.
Why it matters: enforces the exact IRS Jan 1 – Apr 15 prior-year contribution window at the wire level.
{
"status": "Fail",
"status_code": 422,
"data": {
"error_message": {
"type": "ContributionYear",
"code": 422,
"errors": [{ "message": "Prior-year 2025 contribution window closed (after Apr 15)" }]
}
}
}
What to select:
10000031 — Orwell SEP IRA (QOI)50000What the UI shows: green success banner.
What to validate: status: "Success", status_code: 200. The 2026 SEP limit is $70,000 so $50,000 passes.
Why it matters: confirms ContributionLimitService picks the SEP limit table (not IRA) when metadata carries contribution_type: "SepEmployer".
{
"status": "Success",
"status_code": 200,
"data": {
"scheduled_execution_date": "2026-04-27",
"cm_request_id": "..."
}
}
What to select:
10000037 — Sherwin SIMPLE IRA (QIS)10000What the UI shows: green success banner.
What to validate: status: "Success", status_code: 200.
Why it matters: confirms the SIMPLE code path: ContributionLimitService picks the SIMPLE limit plus both catch-ups from metadata.
{
"status": "Success",
"status_code": 200,
"data": {
"scheduled_execution_date": "2026-04-27",
"cm_request_id": "..."
}
}
What to select:
10000027 — Morris Brokerage (BMS)500What the UI shows: green success banner, no retirement warning.
What to validate: status: "Success", status_code: 200. No retirement rules fire because the class code starts with B (non-retirement).
Why it matters: confirms the rule layer correctly short-circuits for non-retirement accounts and doesn’t block ordinary brokerage deposits.
{
"status": "Success",
"status_code": 200,
"data": {
"scheduled_execution_date": "2026-04-27",
"cm_request_id": "..."
}
}
What to select:
transaction_type: "ACHD")10000024 — Xander Roth IRA (RRH)5000What the UI shows: green success banner. Request is posted to POST /ach/withdrawal/with-reason, not /ach/contribution.
What to validate: status: "Success", status_code: 200. The API Log panel shows the withdrawal endpoint in the request URL.
Why it matters: proves UC-W2 (reason-coded withdrawal) is implementable; the TransferReason path is operational for RMD, ExcessReturn, and Other.
{
"status": "Success",
"status_code": 200,
"data": {
"scheduled_execution_date": "2026-04-27",
"cm_request_id": "b2326ec5-3f83-4a96-b6fb-6f2a3f50c618"
}
}
What to select:
DEMO-999 in the cancel box.What the UI shows: acknowledgement toast with the cancellation status.
What to validate: the API Log panel shows DELETE /ach/queued/DEMO-999 returned 200 with body {"transaction_id":"DEMO-999","status":"canceled-stub"}. This is a stub — no persistent queue state changes.
Why it matters: locks down the contract for UC-W4 so the BFF/UI work can proceed ahead of Wave 3 when the real queue is wired.
{
"transaction_id": "DEMO-999",
"status": "canceled-stub"
}
type field (ContributionLimit, HsaEligibility, ContributionYear) — this tells you exactly which Wave 2 service fired.cm_request_id. You can search the local process-api log at ~/.local/log/ach-poc/process-api.log by that id to trace the request through the orchestrator.powershell -File ~/code/ach-poc/start-all.ps1 and wait ~45 seconds for boot.~/.local/log/ach-poc/process-api.log for the CORS middleware registration line on startup.10000024. The house-account rule fires before the Wave 2 rules. Use 10000024 or higher.beta_item_seq_no in the payload. The UI auto-fills it to "1" for all scenarios, so this usually means a hand-crafted payload.http://localhost:5004/swagger to confirm.Run the 10 scenarios end-to-end before declaring the UI demo-ready. Tick each item as you go.
ContributionLimit 422 with the exact $20k / $8k / RRH message.cm_request_id on every click.HsaEligibility 422.ContributionYear 422 with the Apr 15 mention./ach/withdrawal/with-reason."canceled-stub" status.The 10 scenarios cover the happy and sad paths for every Wave 2 rule, but you can go beyond them by posting directly against the local stack.
{
"transaction_request": {
"account_id": 10000024,
"lpl_account_number": "10000024",
"amount": "3000.00",
"firm_id": 1,
"transaction_type": "ACHC",
"origin_id": "AccountView",
"beta_item_seq_no": "1",
"requested_execution_date": "2026-04-24T12:00:00Z",
"rep_id": "ABC1",
"client_id": 1024,
"transfer_reason": "IRA Contribution",
"transfer_type": "contributionType",
"user_context": { "advisor_user_name": "TestAdvisor" }
},
"contribution_metadata": {
"contribution_year": 2026,
"contribution_type": "Regular",
"actor_type": "Investor",
"ytd_contributions_amount": 0,
"is_hdhp_covered": false,
"rollover_type": null
}
}
| Field | Accepted values |
|---|---|
contribution_type | Regular, CatchUp, Spousal, SepEmployer, SepSelf, SimpleEmployee, SimpleEmployer, Rollover, Conversion, Recharacterization, ExcessReturn |
rollover_type | DirectTrustee, Indirect60Day, Roth401kToRothIra, IraToIra, Spousal, Inherited |
transfer_reason (on withdrawal) | RMD, ExcessReturn, Other |
transaction_type | ACHC (contribution/deposit) or ACHD (withdrawal) |
actor_type | Investor, Advisor, OPS |
curl -X POST http://localhost:5001/ach/contribution \
-H 'Content-Type: application/json' \
-d '{
"transaction_request": {
"account_id": 10000024, "lpl_account_number": "10000024",
"amount": "20000.00", "firm_id": 1, "transaction_type": "ACHC",
"origin_id": "AccountView", "beta_item_seq_no": "1",
"requested_execution_date": "2026-04-24T12:00:00Z",
"rep_id": "ABC1", "client_id": 1024,
"transfer_reason": "IRA Contribution", "transfer_type": "contributionType",
"user_context": {"advisor_user_name": "TestAdvisor"}
},
"contribution_metadata": {
"contribution_year": 2026, "contribution_type": "Regular",
"actor_type": "Investor", "ytd_contributions_amount": 0
}
}'
This is the exact payload the UI sends when you click Scenario 1. Paste it in a terminal (Git Bash, WSL, or PowerShell with curl.exe) and you’ll get the same 422 ContributionLimit response.
~/code/ach-poc/WAVE2-LIVE-RULE-EXECUTION.mdC:/Users/ksarkar/tmp/AVACHHackathon/AVUI/index.html