Authentication
How to authenticate Core API requests with an API key or an OAuth 2.0 access token.
Every request to the Core 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 Core endpoint, so you can choose the one that fits your security practices.
A credential belongs to one service. Core credentials work only on the Core API, and a Banking credential sent to a Core endpoint is rejected.
Before you start
You will need:
- A Mono organization with Core enabled, and access to its Mono Dashboard.
- 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 payouts: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.
Send the key in the Authorization header of every request:
curl https://api.cuentamono.com/v1/ledger/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.
Create a credential
In the Mono Dashboard, open your organization's Credentials section and create a credential:
- Choose the scopes the credential may request. You can change them later.
- Copy the client ID and client secret. The dashboard shows the secret only once.
- Store both in your secrets manager.
You do not choose between Core and Banking: a Core organization always gets Core credentials. From the same section you can also disable, enable, or delete a credential.
Request an access token
Call the Core 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/core/oauth/token \
-d grant_type=client_credentials \
-d client_id="$MONO_CLIENT_ID" \
-d client_secret="$MONO_CLIENT_SECRET" \
-d scope="ledger:readonly payouts"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": "ledger:readonly payouts"
}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/ledger/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/core/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 Core authentication reference.
Scopes
Request the narrowest scopes your integration needs. Two rules apply across the catalog:
- A bare scope, such as
cards, 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 |
|---|---|
ledger | All permissions for ledger resources, and reading their programs |
ledger:readonly | Read ledger resources and their programs |
payins | All permissions for payins, their templates, and their intents, and reading brands and settlement batches |
payins:readonly | Read payins, their templates, their intents, brands, and settlement batches |
payouts | All permissions for payouts, Bre-B target resolution, and the bank catalog |
payouts:readonly | Read payouts and the bank catalog |
fees | All permissions for fees and the policies that charge them |
fees:readonly | Read fees and their policies |
fx_rates | Read the exchange rates of the supported currencies |
fx_rates:readonly | Read the exchange rates of the supported currencies |
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 |
Bre-B is Colombia's instant payment system, operated by the central bank. Target resolution turns a Bre-B key, such as a phone number, into the account it points to.
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 Banking. |
403 | not_authorized | The credential is valid, but its scopes or roles do not allow this endpoint. |
403 | not_authorized | A ledger endpoint was called by an organization that is not a Core organization. |
A Banking token sent to a Core 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/core/oauth/token, not
from /v1/oauth/token.
Next steps
- Authentication reference — the token, introspect, and revoke endpoints in detail.
- Architecture — how a request flows after authentication.
- Best practices — key rotation, scope minimization, and credential storage.