Accounts
In short
Section titled “In short”An account is where a customer’s money sits or moves through: a mobile money wallet, a bank account, a card, a merchant or agent account, a loan. Every account belongs to one customer. Transactions are tied to accounts, so many rules (velocity, pass-through, dormancy) work per account.
POST /api/v1/ingest/accountsAuthorization: Bearer spk_…Fields
Section titled “Fields”The body is {"items": [ … ]} with up to 500 accounts.
| Field | Type | Required | What it is | Example |
|---|---|---|---|---|
external_id |
string, 1–128 | Yes | Your reference for the account. Never changes, never reused | "WAL-0244123456" |
customer_external_id |
string, 1–128 | Yes | The external_id of the customer who owns it |
"CUST-77120" |
type |
see below | Yes | What kind of account | "wallet" |
currency |
3-letter currency code | Yes | The account’s currency | "GHS" |
version |
integer ≥ 0 | No, but strongly advised | Increases whenever the record changes. Works exactly as for customers: higher is applied, equal or lower is stale, none is always applied |
1760000000000 |
provider |
string, ≤ 64 | No | Who runs the account. Stored in lower case | "mtn_momo", "telecel_cash", "at_money", a bank code |
identifier |
string, ≤ 64 | No | The account number, wallet number (MSISDN) or card token. Encrypted | "0244123456" |
identifier_type |
msisdn · account_number · card_token · iban · other |
No (see defaults) | What kind of number identifier is. Decides how it is normalised |
"msisdn" |
status |
active · dormant · frozen · closed |
No (default active) |
"active" |
|
opened_at |
date-time | No | When it was opened. Rules for new accounts need it | "2023-06-12T10:05:00Z" |
closed_at |
date-time | No | When it was closed | |
attributes |
object, ≤ 64 keys | No | Your own extra fields | {"tier": "gold"} |
type is one of bank, wallet, card, merchant, agent, loan, virtual, other.
Example
Section titled “Example”curl https://speculapro.example.com/api/v1/ingest/accounts \ -H "Authorization: Bearer $SPECULAPRO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "items": [ { "external_id": "WAL-0244123456", "customer_external_id": "CUST-77120", "version": 1760000000000, "type": "wallet", "provider": "mtn_momo", "identifier": "0244123456", "currency": "GHS", "opened_at": "2023-06-12T10:05:00Z" }, { "external_id": "AGT-0417", "customer_external_id": "CUST-90311", "version": 1760000000000, "type": "agent", "provider": "mtn_momo", "currency": "GHS", "attributes": {"region": "Ashanti", "agent_tier": "B"} } ] }'{ "accepted": 2, "duplicate": 0, "stale": 0, "rejected": 0, "retry": 0, "results": [ {"index": 0, "external_id": "WAL-0244123456", "status": "accepted", "id": "0199c8f1-2c40-7b8d-9e13-3f5a6b7c8d90"}, {"index": 1, "external_id": "AGT-0417", "status": "accepted", "id": "0199c8f1-2c40-7b8d-9e13-40ab5c6d7e81"} ]}Updating an account
Section titled “Updating an account”Send it again with the same external_id. Versions work exactly as for customers: a higher version is applied, an equal or lower one returns stale and changes nothing, and a record with no version is always applied without raising the stored version. See Versions.
When the customer isn’t known yet
Section titled “When the customer isn’t known yet”If customer_external_id names a customer SpeculaPro hasn’t received, the account is still accepted. SpeculaPro creates a placeholder customer (a “stub”) which the real customer record replaces when it arrives. See Placeholder customers.
The same works the other way: if a transaction names an account SpeculaPro hasn’t received, a placeholder account is created with the transaction’s currency and account type (other if none was given). Your real account record replaces it.
The account identifier
Section titled “The account identifier”identifier is the number that identifies the account outside your institution: the wallet’s phone number, the bank account number, the card token.
- It is normalised (see below), then encrypted with your workspace’s key. It is never stored in readable form.
- A masked copy of the normalised number is kept for display. Phone numbers keep the
+and the whole country calling code plus the last two digits: wallet0244123456becomes+233244123456and is shown as+233•••••••56; a UK number+447700900123is shown as+44••••••••23. Other identifiers keep their first three and last two characters, so bank accountGH0123456778is shown asGH0•••••••78. - A keyed fingerprint lets SpeculaPro recognise the same number elsewhere, in particular as the counterparty of another customer’s transaction. That’s how it can see that money sent “to 0244123456” went to a wallet you monitor.
What kind of number it is
Section titled “What kind of number it is”identifier_type tells SpeculaPro how to read the number. You can usually leave it out:
If you don’t send identifier_type and… |
SpeculaPro treats the identifier as |
|---|---|
type is wallet |
msisdn (a phone number) |
provider is a mobile-money provider (list below) |
msisdn |
| anything else | account_number |
Mobile-money providers recognised automatically (matched in lower case): mtn_momo, telecel_cash, vodafone_cash, at_money, airteltigo_money, airtel_money, orange_money, moov_money, wave, free_money, mpesa, momo.
Send identifier_type explicitly when the default would be wrong, for example "card_token" for a card account, or "msisdn" for a wallet from a provider not on the list. Any value other than the five allowed fails the whole batch with 422.
How identifiers are normalised
Section titled “How identifiers are normalised”| Type | Normalisation | These all match |
|---|---|---|
msisdn |
Converted to international (E.164) format using your workspace’s country, exactly like customer phone numbers | 0241234567, 233241234567, +233 24 123 4567, 00233241234567 → +233241234567 |
account_number, card_token, iban, other |
Spaces, dashes, dots and slashes removed; letters upper-cased | gb82 west-1234, GB82WEST1234 → GB82WEST1234 |
So you don’t need to format wallet numbers the same way in every feed: a wallet sent as 0241234567 in your account feed and as +233 24 123 4567 in a transaction’s counterparty is recognised as the same number.
Common rejections
Section titled “Common rejections”| Message | Cause | Fix |
|---|---|---|
currency must be an ISO 4217 code |
currency isn’t three capital letters |
Use codes such as GHS, USD, XOF |
invalid data: violates … |
The record broke a database rule | Check the values against the table above |