n8n Multi-Currency Automation: Lock the Rate, Round Right, Normalize
Build n8n multi-currency automation that locks the exchange rate at issue time, rounds correctly per currency, and normalizes to a base for clean reporting.
The currency templates that rank all do the same trick: hit an exchange-rate API on a schedule, drop the latest rates into a Google Sheet, maybe email them out. Handy as a rate ticker. Useless as finance, because a rate sheet doesn't know which rate applied to last Tuesday's invoice, and that's the only rate that matters once the money's been billed.
Real n8n multi-currency automation is about money math, not rate fetching. Lock the rate at the moment a transaction happens. Round it the way that currency expects. Normalize everything to one base currency so a report that spans three currencies sums into a number you can trust. Get those three right and the API call is the trivial part.
This is the gap every ranking template leaves open. They fetch rates beautifully and apply them never.
What You Can Automate in Multi-Currency Handling
The pieces that automate correctly when you respect the math:
- Fetching the exchange rate at the instant a transaction is created
- Storing that locked rate on the record, beside the original amount
- Converting with a consistent rounding rule per currency
- Keeping both the original and converted amounts for audit
- Normalizing every record into one base reporting currency
- Validating fetched rates before they touch a single number
- Flagging a rate that moved implausibly far since the last fetch
- Summing the normalized column for a clean cross-currency report
The order matters. Fetch, lock, round, store. Convert at write time, sum at read time. Never the other way around.
The Multi-Currency Pipeline
The shape that keeps historical numbers stable:
Transaction → Fetch rate → Validate rate → Lock + round → Store both → Normalize → Report
A concrete invoice-creation run:
Webhook / Trigger: new invoice in a foreign currency
→ HTTP Request v4.2: fetch rate (ExchangeRate API) for that currency pair
→ Code v2 (jsCode): validate rate (numeric, within sane band of last)
→ Code v2 (jsCode): convert + round per currency rules
→ Google Sheets v4: store original, rate, converted, base-currency value
--- later, at report time ---
→ Schedule Trigger v1.2: read all records
→ Code v2 (jsCode): sum the pre-computed base-currency column
The conversion happens once, when the invoice is created, and never again. A month-end report just sums a column that was already correct. Re-converting at report time is exactly the bug the rate-sheet templates bake in.
The n8n.io currency templates that rank — the rates-in-invoices flow, the webhook converter, the daily-rates emailer — all store the current rate and nothing more. Re-open last quarter's report and every foreign invoice silently re-converts at today's rate. Your closed numbers move. The fix is one column: store the locked rate and the converted value on each record at creation, so history stays put no matter when you read it.
Step-by-Step Breakdown
1. Fetch the rate at transaction time
When a foreign-currency transaction is created, hit the rate API once for that currency pair. The ExchangeRate API and ExchangeRate.host both return clean JSON. This fetch is the only time you look up this transaction's rate.
2. Validate the rate before trusting it
A Code node checks the returned rate is numeric and within a sane band of the last known rate. An API hiccup that returns zero or a wildly off value will silently corrupt every amount converted with it. Catch it here, before the math.
3. Convert and round per currency
Apply the rate, then round according to the currency's actual decimal places. Two for most, zero for yen, three for a few. Store the rule in data so a Code node looks up the decimals rather than assuming two everywhere.
4. Store both amounts and the rate
Write the original amount, the currency, the locked rate, the converted value, and the base-currency value to the record. Both amounts and the rate. That triple is what makes any later question answerable without guessing.
5. Normalize and report
The base-currency value computed at write time is the normalized figure. A report just reads and sums that column. No conversion at report time means no drift, and a cross-currency total that actually ties out.
Implementation Patterns That Hold Up
Pattern 1: Lock the rate on the record. Convert at creation, store everything, and never re-fetch for that transaction.
Code v2 (jsCode):
const decimals = { USD: 2, EUR: 2, JPY: 0, BHD: 3 }[currency] ?? 2;
const converted = round(amount * lockedRate, decimals);
return [{ json: {
original: amount, currency, rate: lockedRate,
converted, base: round(amount * lockedRate, 2)
} }];
The decimals lookup is the rounding rule made explicit. Assuming two decimals everywhere quietly mangles yen amounts, and yen has a habit of showing up in totals that no longer tie out.
Pattern 2: Sanity-band the fetched rate. A rate that jumped more than a few percent since the last fetch is probably an API glitch, not a market move. Compare against the last stored rate and route an outlier to an alert instead of into the ledger. One bad rate from a flaky endpoint can poison a whole batch.
Pattern 3: Convert at write time, sum at read time. This is the discipline the rate-sheet templates miss. The conversion is a property of the transaction, fixed when it happened. The report is just addition over a column that's already in one currency. Keep those two jobs apart and historical reports stop moving under you.
n8n Nodes You'll Use Most
| Node | Purpose |
|---|---|
n8n-nodes-base.httpRequest | Fetch the rate for the currency pair at issue time |
n8n-nodes-base.code | Validate the rate, convert, round per currency |
n8n-nodes-base.scheduleTrigger | Run the normalized report on a cadence |
n8n-nodes-base.googleSheets | Store original, rate, converted, base value |
n8n-nodes-base.switch | Route an out-of-band rate to an alert |
n8n-nodes-base.if | Branch on validation pass or fail |
n8n-nodes-base.gmail | Alert when a fetched rate looks wrong |
The Code node doing the conversion is where correctness lives. It owns the rounding rule and the locked rate. Everything around it just moves data. Test it against yen and a three-decimal currency, not just dollars and euros, because that's where the rounding bugs hide.
Getting Started
- Decide your base currency and write it in the Config node. Every normalized figure converts to this. Pick it once.
- Build the fetch-and-validate pair. One node gets the rate, the next confirms it's sane against the last known value.
- Write the conversion with an explicit decimals lookup. Don't assume two. Test yen.
- Store original, rate, converted, and base on every record. All four. This is what makes history auditable.
- Build the report as a sum over the base column. No conversion at report time. Read and add.
- Add the out-of-band rate alert. A flaky endpoint shouldn't quietly corrupt a batch. Flag the outlier and hold it.
The Data Gatekeeper — Preflight & Failure Logging ships the validation layer this workflow can't skip: it checks every record for required fields, value format, and duplicates, logs a daily audit summary, and emails an instant alert when bad rows appear — exactly the guard that stops a zero or a garbage exchange rate from corrupting a converted batch. It's part of The Complete n8n Templates Bundle, a one-time lifetime license to the whole catalog (plus every template added later) if you run more than one of these finance automations.
One caveat on accuracy: free exchange-rate APIs publish mid-market rates, which won't match the rate your bank or payment processor actually applied, fees and spread included. For invoicing and internal reporting that's usually fine. For reconciling against what actually hit your account, pull the realized rate from the processor instead, and treat the API rate as an estimate.
If multi-currency feeds a wider reporting setup, the n8n bank reconciliation workflow shows how converted amounts get matched against the bank feed, and the n8n data pipeline automation guide covers the validate-transform-load pattern this conversion step plugs into.
Lock the rate at issue time and store both amounts. Everything downstream gets easier, and your closed months finally stay closed.
See more finance templates →Common questions
How do you lock an exchange rate in an n8n workflow?
Why does rounding matter in multi-currency automation?
What does base-currency normalization mean for reporting?
Get the workflow templates this guide is built on
Import-ready n8n JSON, step-by-step setup, and tested end-to-end. One-time payment, own it forever.
Get 3 tested n8n templates, free
The full customer package for three real catalog templates — workflow JSON, step-by-step setup guide, credential checklist. Built through the same live-instance release process as everything we sell. Plus new templates and automation guides in your inbox. No spam, unsubscribe anytime.
- 01Smart To-Do List ManagerPre-built n8n workflow template that automates productivity with OpenAI. Live in about 10 minutes.$14
- 02Email Follow-Up AutomatorPre-built n8n workflow template that automates crm with OpenAI. Live in about 15 minutes.$12
- 03Market Trend AnalyzerPre-built n8n workflow template that automates data processing with OpenAI. Live in about 10 minutes.$14
More automation guides

n8n Month-End Close Automation: A Checklist Workflow That Tracks Itself
Type "n8n month-end close automation" into a search bar and you get accounting-firm landing pages promising to automate your close, and finance-SaaS sites selling close software. Useful if you want to…

n8n Failed Payment Recovery: A Smart Dunning Ladder for Stripe
Your customer didn't leave. Their card did. It expired, or hit a limit, or the bank flagged a recurring charge it didn't recognize, and Stripe quietly logged a . Nobody clicked cancel. But if nothing…

n8n Accounts Payable Automation: 3-Way Match and Approval Routing
Most accounts payable content shows you the easy half. A vendor emails an invoice, an AI node reads the amount, you match it against a purchase order, you pay. The n8n.io invoice-PO matching template…