ON_CHAIN_COMPLETED webhook. QA never moves real bank money — fire POST /psp/alfred/__dev/emit-onramp with scenario: "success" from the Dev playground to walk the entire chain.Step-by-step actor sequence. Color-coded lanes show who does what.
__dev/seed-kyc-and-provision?country=MX → Alfred customer created + KYC text submitted.__dev/approve-kyc → pushes KYC COMPLETED webhook to Alfred. Customer transitions to statusKyc: COMPLETED.GET /eligibility → returns { eligible: true }. Deposit form renders.POST /quote-preview shows ≈ 18 USDT.POST /session → POST /initiate-deposit. Bank instructions + virtual address returned.session_id from response. Fire __dev/emit-onramp with scenario: success.FIAT_DEPOSIT_RECEIVED → TRADE_COMPLETED → ON_CHAIN_INITIATED → ON_CHAIN_COMPLETED.DepositManualCreateJob dispatched. User balance += 18 USDT. Widget stepper shows all 4 checkmarks.bank_id (e.g. 1007 Bancolombia).GET /eligibility passes. Deposit form renders.POST /initiate-deposit.201 with a real transaction + bank-payment instructions (Bug #1 fix verified 2026-05-29).__dev/emit-onramp with session_id to walk the webhook chain.to_amount USDT. Same outcome as MX / BR scenarios.The full happy path from widget mount to credited balance. Steps marked QA use dev endpoints; everything else is the production widget path.
GET /psp/alfred/eligibility. Returns { eligible: true } or throws USER_NOT_VERIFIED / ALFRED_KYC_PENDING / ALFRED_KYC_REJECTED / ALFRED_NOT_AVAILABLE_FOR_COUNTRY / ALFRED_CUSTOMER_NOT_PROVISIONED.GET /psp/alfred/supported-options returns the rail list; POST /psp/alfred/quote-preview returns live fiat→USDT preview while the user types.POST /psp/alfred/session re-runs KYC + Alfred KYC + country + customer-provisioned checks; on pass returns a session_id (24h TTL).POST /psp/alfred/initiate-deposit mints an Alfred quote, calls /onramp, returns the per-tx virtual deposit address + fiat payment instructions. For CO must include bank_id (4-digit PSE).POST /psp/alfred/__dev/emit-onramp with scenario: "success" walks the entire chain without a real bank transfer.Number.isFinite(amount) && amount > 0), runs a DB-uniqueness check on remote_txid, and enqueues DepositManualCreateJob. Widget polls GET /psp/alfred/session to read the new transactionStatus.Deposit / Onramp — MX SPEI · BR PIX · AR COELSA · CO ACH-PSE · DO ACH_DOM.
to_amount USDTto_amount USDT after ON_CHAIN_COMPLETEDbank_idbank_id=1007 (Bancolombia)redirectUrl in fiatPaymentInstructions; widget renders inline iframeto_amount USDT after ON_CHAIN_COMPLETEDreferenceIdALFRED_DEPOSIT_ALREADY_RECORDED, no double-credit (DB uniqueness on remote_txid)ALFRED_MAINTENANCE_MODEmetadata.bankId as a 4-digit PSE code on every POST /onramp. Without it Alfred returns 422 / 110002 "bankId is required in metadata for COP currency". The widget already enforces this on the deposit form.400/111301 UNKNOWN_ERROR. Alfred shipped the fix; TC-D3 / TC-D5 / TC-D6 are now PASS. Keep them as regression coverage on every retest.# 1. session POST /psp/alfred/session { "data": { "type":"deposit", "amount":500, "source_currency_id":"MXN", "payment_route_id":"<ALFRED-MXN-USDT>" } } # 2. initiate POST /psp/alfred/initiate-deposit { "session_id":"<id>" } → 200 { depositAddress, transaction, quote } # 3. QA dev-fast-forward POST /psp/alfred/__dev/emit-onramp { "session_id":"<id>", "scenario":"success" } → webhook chain → DepositManualCreateJob → credited
session_id from POST /psp/alfred/session → fire the emit-onramp runner with scenario: success and watch the balance change.