Skip to content

Reference data

Platform → Reference data is “the tables every other screen resolves against”: the bank a settlement lands in, the network a momo prompt is sent to, the rate a conversion is priced at, the locale a notification is written in, the reason code a dispute deadline is computed from.

Nothing here is a queue and nothing here has a lifecycle. They are consulted rather than worked, which is why they are five tabs behind one door.

The two write permissions are not the same

Section titled “The two write permissions are not the same”
Permission Covers
reference.write Editing a bank, a translation, a reason code.
reference.fx.write Publishing a rate — which changes what every cross-currency payment on the platform costs, from the instant it lands until somebody notices.

The split is honoured per action rather than at the door, because an account that can read reference data and correct a bank’s GhIPSS code but may not reprice the platform is a real and useful role.

Most of this cluster is a lookup, not a register

Section titled “Most of this cluster is a lookup, not a register”

There is no list operation for countries or currencies — only lookups by code. Those tabs are resolvers, and they say so at the top, so an operator who arrives expecting to browse is not left to conclude the table is empty.

Banks and mobile money networks can be listed — GET /v1/reference/banks and GET /v1/reference/momo-networks, every row, active or not, with an optional ?country — and the console uses those lists for its pickers. FX rates, locales and dispute reason codes can be listed too.

Two lookups, because which one you use depends on where you came from.

Field Notes
Country + Bank code ISO 3166-1 alpha-2, and “the platform’s own code, not the SWIFT BIC.”
Choose a bank “Every bank, active or not. Opens the canonical record, the one an edit refreshes.” Pick from the list; no id to paste.

This is also the table behind the bank list on the merchant portal’s Add destination form, which shows the active banks we can pay out to — see the merchant side. A merchant who cannot find their bank is asking about this table.

Field Notes
Country Alpha-2.
Code “The platform’s own identifier within the country. Merchant integrations may hardcode it, so choose it once.”
Name The bank’s own name.
Short name “What the picker shows when space is short.”
Supports collection “A merchant can take money through it.”
Supports payout “A merchant can register it as a settlement destination.” Off here means no settlement destination can be added for this bank, whatever else is set.
Reachable on the GhIPSS instant rail “Needs a GhIPSS code below. Without one an instant transfer cannot be addressed.”
GhIPSS code Required in practice for any instant rail.
SWIFT / BIC For reference. Not what the platform routes on.
Picker order “Lower sorts first.” Use it to put the banks most merchants settle to at the top.
Account number pattern “Checked for validity, not for correctness.”
Why this bank is being added Goes in the audit log.

The edit form carries the same fields, plus the one that takes a bank out of circulation:

Field Notes
Offered to merchants “Off hides the bank from every merchant-facing list. Transactions in flight and merchants already settling to it are unaffected.”
Why Required, and the audit entry records the changed fields and their previous values rather than the whole row.

Withdrawing is the nearest thing to a delete here, and deliberately not one: a merchant already settling to that bank keeps being paid, and only new choices are prevented.

Import banks — “creates new banks and updates existing ones, matched on country and code. One bad row and nothing is written.”

  1. Download the template, an Excel workbook (.xlsx). The Import sheet has the header row (frozen) and example rows. The Columns sheet lists each column, whether it is required, and what it means, which is also under What each column means in the dialog. Where a column has a fixed set of values, the template offers them as a dropdown in the cell.
  2. Fill it in and choose the filled sheet, as .xlsx or .csv.
  3. Check the sheet — a dry run. Nothing is written. Every problem is listed by its line number.
  4. Import N rows, with a Reason — “at least eight characters. Recorded on every imported row’s audit entry.”

It is all or nothing: one bad row and no row is written. At most 2,000 rows.

Column Notes
country_code, code, name Required. A row whose country and code match an existing bank updates it; otherwise it creates one.
short_name, swift_bic, ghipss_code, account_number_regex, display_order Optional.
supports_collection Default no.
supports_payout Default yes.
supports_instant Default no.
is_active Default yes.

The logo is not imported; set it on the bank. From the API: POST /v1/reference/banks/import.

How a phone number becomes a rail — and the single most important thing on the tab is the pill that says the inference is not authoritative.

So the panel says “preselect this” and never “it is this”. When a merchant reports prompts that never arrive, this is the first thing to ask about their integration.

Form Field Notes
Which network is this number on? Country Alpha-2.
Mobile number “Spaces, hyphens and a leading plus are fine. +233 24 412 3456, 0244123456 and 244123456 all normalise to the same number.” The answer is a preselection, never a decision.
Resolve a network code Country + Network code mtn, telecel, airteltigo — the code, not the display name.
Adding a network Country code ISO 3166-1 alpha-2.

A network added here appears in every merchant’s picker exactly as a bank does, and its id is what a charge has to carry.

The rates in force, a rate lookup, and the one form on this page that changes how much money moves.

A rate is quote-currency major units per one base-currency major unit, and it is a decimal string that is never parsed as a float for pricing. Every rate column renders the string exactly as it arrived, and the spread is shown in basis points, because that is the figure a treasury decision is made on.

Nothing is priced from that hint.

Field Notes
Base currency “The currency one unit of is being priced.”
Quote currency “The currency it is priced in.”
Mid “A decimal string.” Never a rounded float.
We buy at Our bid.
We sell at “At or above the mid.”
Feed Where the figure came from.
Spread (basis points) “Recorded for FX revenue reporting. It does NOT derive buy and sell — those are the figures above.”
Why this rate, and from where Required. “Name the source you checked it against — this is the only record of why the platform repriced.”

A spread typed here that disagrees with the buy and sell figures is not reconciled by the platform: those two figures price the payment, and the spread is a reporting field. If they disagree, the report is wrong and the pricing is right.

Field Notes
Base / Quote / Feed The pair, as published.
At (optional) “Leave empty for the rate in force now.” A time answers what a payment then was priced at.
Quote id “From a transaction’s fx_rate_id.”
Currency code ISO 4217 alpha, three letters — for resolving one currency.

When a merchant disputes a converted amount, resolve the quote id from the transaction, not the pair by date. A rate republished since would otherwise answer a different question from the one being asked.

The locales the platform will actually render in, with exactly one marked as the default.

The default locale’s text is the source column, which is why a missing translation falls back to it rather than to nothing.

And “translated” is a separate fact from the value: a lookup returns the source text on a miss, because the honest answer to “no translation exists” is the source text rather than an error. The value alone cannot tell you whether anything was translated, so the tab reports both.

Field Notes
Accept-Language “A header value or a bare comma-separated tag list. Quality values are parsed and the ordering is honoured.”

Paste the header out of the merchant’s complaint rather than retyping the language they say they use. The negotiation is what decides, and it is what this answers.

Field Notes
Resource type “The table the string belongs to, as translations.resource_type spells it.”
Row / Row key For banks and mobile money networks, Row is a picker of that table’s rows. For anything else, Row key is “the row’s primary key as text, such as a currency’s three-letter code.”
Field Which column of that row is being translated.
Locale “The default locale is the source text, not a translation.”
Fallback “The source-column text. Returned when there is no translation — which is the contract, not a shortcut.”
Somebody who speaks this language has read it “is_reviewed defaults to false, which is the honest default for a string typed into a form. Turning it on is a claim, not a formality.”
Tolgee key “Recorded only so the two can later be reconciled rather than silently diverging. There is no Tolgee sync; importing is a worker, not an endpoint.”
Why Required — “the realistic abuse here is not vandalism but a bank’s name quietly edited to another bank’s.”

The scheme’s published vocabulary, maintained once and consulted by every dispute. This is also where the “what usually answers this reason code” guidance that appears on a case file is written — see Disputes.

Field Notes
Scheme Whose vocabulary this is.
Code “As the scheme publishes it. Upper-cased.”
Title The short name an operator sees on a case.
What it means “Read by whoever picks the case up. Say what the scheme is actually alleging.”
Category The grouping the queue filters on.
Starting liability “Who carries it before anybody looks at the evidence.”
Response window (days) “The scheme’s own window.” Read the warning above: this computes real deadlines.
What the merchant should be asked for “These become the guidance on the dispute case file and what the portal asks for.” Only real evidence_type values are offered, because “the column has no CHECK of its own, so a typo would ask a merchant for something no upload endpoint accepts.”
Why Required, recorded with the whole row — “name the scheme bulletin or the case that revealed the window was wrong.”
Symptom Likely cause
A merchant cannot add their bank The bank is not in the table, or they have the wrong code.
Momo prompts never arrive The integration is inferring the network instead of naming it.
Cross-currency amounts are absurd An inverted or stale FX pair.
A notification reads in English under a non-English locale A missing translation falling back to source.
Dispute deadlines look wrong across a reason code The response window on that code.