Take a payment in the portal
A borrower signs in to your branded portal, sees what they actually owe, authorizes the exact amount in their own words, and gets a receipt once the money is real.
On May 6, Maya signs in to the portal and pays the past-due $517.14 — keeping the promise she made to the voice agent five days earlier. The self-service API is borrower-scoped throughout: these calls run against the portal origin with a borrower session, not an operator token.
- The borrower signs ina session that is only ever Mayaa person
- Reads the loanevery money fact from the authoritative core
- Pays, explicitly authorizedcaller-minted key, reused on every retry
Sign in as the borrower
Borrower-scoped sessioncurl -X POST "$PORTAL_BASE/v1/auth/login" \
-H "Content-Type: application/json" \
-d '{ "email": "maya@example.test", "password": "********" }'{ "sessionToken": "st_9a3...", "expiresAt": "2027-05-06T18:40:00Z", "stepUpRequiredFor": ["ADD_PAYMENT_METHOD", "SIGN_AGREEMENT"] }The session is short-lived, signed, and rate-limited for the open internet. Sensitive operations demand one-time-code step-up on top of it.
Read the loan as the borrower sees it
Authoritative factscurl "$PORTAL_BASE/v1/loans/7204" \
-H "Authorization: Bearer st_9a3..."{
"loanRef": "7204",
"productName": "Harbor Personal Loan",
"status": "PAST_DUE",
"amountDue": { "amount": 517.14, "currency": "USD" },
"dueDate": "2027-04-12",
"paymentsMade": 7,
"paymentsTotal": 18,
"asOf": "2027-05-06T17:58:11Z"
}The portal UI derives nothing, and the progress figure counts payments made rather than re-computing dollars. Past-due renders amber in the shipped app, and no status rests on color alone.
Take the payment
Safe to retrycurl -X POST "$PORTAL_BASE/v1/loans/7204/payments" \
-H "Authorization: Bearer st_9a3..." \
-H "Idempotency-Key: portal-hbr-2027-05-06-1" \
-H "Content-Type: application/json" \
-d '{
"amount": { "amount": 517.14, "currency": "USD" },
"paymentMethodId": 704,
"authorization": { "acknowledged": true, "acknowledgedAt": "2027-05-06T18:01:40Z" }
}'{ "paymentRef": "9014", "status": "PROCESSING", "receiptRef": "rcpt-9014", "submittedAt": "2027-05-06T18:01:41Z" }A network blip replays to the original result instead of a second charge. The payment settles and posts through the same pipeline as any other, and the posting event is what marks Maya’s promise KEPT.