Authentication
How to authenticate Banking API requests with an API key or an OAuth 2.0 access token.
Every request to the Banking API must prove that it comes from your company. You can prove it with a long-lived API key, or with a short-lived access token that your server requests on its own. Mono accepts both on every Banking endpoint, so you can choose the one that fits your security practices.
A credential belongs to one service. Banking credentials work only on the Banking API, and a Core credential sent to a Banking endpoint is rejected.
Before you start
You will need:
- Access to the Mono Dashboard for your organization.
- An HTTPS client. Mono refuses requests that do not use TLS.
- A safe place for secrets, such as a secrets manager. Never commit credentials to source control.
The conventions shared by every Mono product, such as the header format and environments, are in Authentication standards.
Choose a credential
| API key | OAuth 2.0 access token | |
|---|---|---|
| How long it lasts | Until you revoke it | 30 minutes, then you request a new one |
| What it can do | What its roles allow | What its scopes allow |
| How you get it | Created in the Mono Dashboard | Requested by your server with a client ID and secret |
| Where it goes | Authorization: Bearer <key> | Authorization: Bearer <access_token> |
A role is a named set of permissions, such as "Admin" or "Viewer", attached to an API key. A scope is a single permission, such as transfers:readonly, attached to an access token. Mono maps each role permission to the scopes that grant the same action, so an endpoint accepts either credential for the same operation.
Option 1: API keys
An API key is a secret string that stays valid until you revoke it. To create or revoke keys, sign in to the Mono Dashboard and open the API key management section.
When you create a key, choose its roles. The key can do exactly what those roles allow. For example, a key with the "Viewer" role can read data but not move money.
Send the key in the Authorization header of every request:
curl https://api.cuentamono.com/v1/accounts \
-H "Authorization: Bearer $MONO_API_KEY"Keys do not expire, so rotating them is your responsibility: create a new key, deploy it, then revoke the old one.
Option 2: OAuth 2.0 access tokens
OAuth 2.0 is an industry standard for issuing temporary credentials. Mono uses its client credentials flow, designed for server-to-server integrations. Your server exchanges a permanent client ID and client secret for an access token that lasts 30 minutes. Think of it as trading an employee badge for a day pass: the pass expires, the badge stays in the safe.
Get a credential
Banking OAuth credentials are not self-service yet: the Mono Dashboard cannot create them for Banking organizations. To get one, contact your Mono account team and tell them which scopes your integration needs.
Mono gives you a client ID and a client secret. Store both in your secrets manager as soon as you receive them. To change the scopes later, or to disable or delete the credential, contact the same team.
Request an access token
Call the token endpoint with grant_type=client_credentials, your client ID and secret, and the scopes you need, separated by spaces:
curl https://api.cuentamono.com/v1/oauth/token \
-d grant_type=client_credentials \
-d client_id="$MONO_CLIENT_ID" \
-d client_secret="$MONO_CLIENT_SECRET" \
-d scope="accounts:readonly transfers"The endpoint accepts application/x-www-form-urlencoded, as above, or the same fields as JSON. In sandbox, use https://api.sandbox.cuentamono.com.
{
"access_token": "<access_token>",
"token_type": "Bearer",
"expires_in": 1800,
"refresh_token": "<refresh_token>",
"scope": "accounts:readonly transfers"
}Always send scope. If you leave it out, Mono still issues a token, but with no scopes, and
every endpoint then answers 403. If you ask for a scope your credential does not have, the
whole request fails with 400 invalid_scope; Mono never returns a token with only part of
what you asked for.
Call the API
Send the access token in the Authorization header, exactly like an API key:
curl https://api.cuentamono.com/v1/accounts \
-H "Authorization: Bearer $MONO_ACCESS_TOKEN"Renew the token
expires_in is the token's remaining lifetime in seconds: 1800 when it is issued. Request a new token before it runs out. You have two ways:
- Request a new token with your client credentials, exactly as above. This is the simplest option for a server that always holds its secret.
- Use the refresh token. The response also carries a
refresh_token, valid for one day. Exchange it for a new access token:
curl https://api.cuentamono.com/v1/oauth/token \
-d grant_type=refresh_token \
-d client_id="$MONO_CLIENT_ID" \
-d client_secret="$MONO_CLIENT_SECRET" \
-d refresh_token="$MONO_REFRESH_TOKEN"Each refresh returns a new refresh token. Use the new one next time and discard the old one.
Check or revoke a token
Two more endpoints take your client ID and secret plus the token itself:
- Introspect (
grant_type=introspect) tells you whether a token is still active, when it expires, and which scopes it carries. An expired or unknown token returns{"active": false}. - Revoke (
grant_type=revoke) invalidates an access token or a refresh token at once. It answers200even if the token had already expired.
Revoke tokens you no longer need, for example when you retire a server. The endpoint contracts are in the Banking authentication reference.
Scopes
Request the narrowest scopes your integration needs. Two rules apply across the catalog:
- A bare scope, such as
transfers, grants every action on that resource, including the specific actions listed below it. - A
:readonlyscope grants reads only. It does not include the specific actions:cards:readonlydoes not grantcards:details.
| Scope | Grants |
|---|---|
accounts | All permissions for accounts |
accounts:readonly | Read accounts |
accounts:balances | Read the balance of an account |
transfers | All permissions for bank transfers, and reading the bank catalog |
transfers:readonly | Read bank transfers and the bank catalog |
transfers:prepare | Create bank transfers that wait for approval |
collection_links | All permissions for collection links and collection intents, and reading the bank catalog |
collection_links:readonly | Read collection links, collection intents, and the bank catalog |
cards | All permissions for cards |
cards:readonly | Read cards |
cards:details | Read a card's sensitive details: number, security code, expiry |
spending_controls | All permissions for card spending controls |
spending_controls:readonly | Read card spending controls |
Failures
The token, introspect, and revoke endpoints answer errors in the OAuth format, with error and error_description fields. For example, 400 invalid_scope means you asked for a scope your credential does not have.
Every other endpoint answers with the standard error envelope:
| Status | Code | What happened |
|---|---|---|
401 | expired_token | The token expired, was revoked, or belongs to another service, such as Core. |
403 | not_authorized | The credential is valid, but its scopes or roles do not allow this endpoint. |
A Core token sent to a Banking endpoint gets the same 401 expired_token as an expired token.
If a fresh token is rejected that way, check that it came from /v1/oauth/token, not from
/v1/core/oauth/token.
Next steps
- Authentication reference — the token, introspect, and revoke endpoints in detail.
- Authentication standards — header, environments, and rotation rules shared by every product.
- Errors and retries — the error envelope and when to retry.