A plain-language walkthrough of the complete consumer → broker loop. Three audiences, one flow.
Everything a remittance customer does from landing on the site to receiving their broker's final quote.
POST /api/quotes call fires. The consumer gets a confirmation email immediately. A broker rep gets notified within minutes.
FxPortal connects consumers to brokers. We don't hold funds, initiate transfers, or take custody of money. Once a consumer locks a rate, the transaction happens directly with the broker. FxPortal's role ends at introducing the two parties.
What a broker does when a quote lands in their inbox.
/broker/quotes?broker_id={id} — substitute your broker ID. You see all incoming quote requests, filterable by status: pending (not yet claimed), quoted (claimed and responded to), won, or lost. Pending quotes are shown first.
pending to quoted and records claimed_at. Only one broker can claim a quote (the consumer chose one broker when they locked the rate). Claiming signals to your team that the lead is live.
There are two automated emails in the loop, both sent via Postmark through the Polsia email proxy.
Hi,
Your rate has been locked with {broker_name}. A broker representative will be in touch within 24 hours.
Amount: {amount} {source}This rate is indicative. The final rate will be confirmed by the broker directly.
Compare this rate against others at fxportalos.polsia.app/compare
Hi{recipient_name ? ', ' + recipient_name : ''},
{broker_name} has sent their final quote for your transfer:
Your amount: {amount} {source}The broker will contact you directly to complete the transfer. Please reply to this email or use the contact details provided to fund the transfer.
New brokers are added via a direct SQL migration or by running the seed script. Broker rates are managed separately in broker_rates.
migrations/ following the naming convention {unix-timestamp}_{name}.sql. The migration runs automatically on every deploy via npm run migrate.
-- migrations/1750000000_add_new_broker.sql INSERT INTO brokers (name, rating, active, slug, fca_reg_number, years_operating, avg_response_minutes, monthly_volume_gbp, rating_count, about, supported_corridors) VALUES ( 'ClearFX UK', -- name 4.7, -- rating (1–5) 't', -- active flag 'clearfx-uk', -- slug (URL-safe, unique) '123456', -- fca_reg_number 8, -- years_operating 15, -- avg_response_minutes 2400000, -- monthly_volume_gbp 312, -- rating_count 'Specialist in GBP/NGN transfers with FCA authorisation.', 'GBP/NGN,USD/NGN' -- supported_corridors (comma-separated) );
broker_rates per corridor they operate in. Run this after inserting the broker — use the broker's id from the insert above.
INSERT INTO broker_rates (broker_id, currency_pair, rate, spread, volume_24h) VALUES ((SELECT id FROM brokers WHERE slug = 'clearfx-uk'), 'GBP/NGN', 1995.50, 0.45, 850000), ((SELECT id FROM brokers WHERE slug = 'clearfx-uk'), 'USD/NGN', 1550.00, 0.50, 420000);
npm run migrate (called automatically after each deploy). The broker will appear on /compare immediately after, and their trust profile becomes available at /broker/{slug}.
If you're adding several brokers at once, edit seed-brokers.js and run node seed-brokers.js once. The seed script uses the same INSERT pattern and can be run against a live DB. It's a one-off — don't leave it in automated deploys.