Skip to main content

Authentication

Every call to Retorna Gateway carries two independent layers. This page covers the first one; the second has its own guide.

LayerWhat it provesWhere
ApplicationYour access tokenthis page
IntegrityYour request signatureRequest signing

Getting a token​

POST /oauth2/token with the OAuth2 client_credentials grant. Your clientId / clientSecret pair travels in an Authorization: Basic header, and the scope in a form-encoded body:

curl -X POST https://api.gateway.sandbox.retorna.app/oauth2/token \
-H "Authorization: Basic $(printf '%s:%s' "$CLIENT_ID" "$CLIENT_SECRET" | base64)" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "scope=sandbox/full_access"

The scope is fixed per environment: sandbox/full_access in sandbox and prod/full_access in production. There is no finer-grained scope β€” the credential pair already defines what your company can do.

{
"access_token": "eyJraWQiOiJ...",
"expires_in": 3600,
"token_type": "Bearer"
}

Then send it on every subsequent call:

Authorization: Bearer eyJraWQiOiJ...

What this is not​

This is machine-to-machine authentication, not a user login. There is no email, no password, no idToken, and no refresh token β€” when the token expires you request another one the same way. Treat the credential pair as an API key: it identifies your company, not a person.

That is also why no endpoint takes an account, owner, or company parameter. Your company is resolved from the verified token, so GET /me/wallets/{currency} and GET /me/routes take no company parameter to know whose wallet and whose corridors to return. One credential, one company.

Token lifetime​

expires_in is seconds β€” 3600 in the example above, but read the field instead of hardcoding an hour. Cache the token and reuse it until it is close to expiry rather than requesting one per call.

When it fails​

A 401 on /oauth2/token covers every credential problem: missing Authorization header, a scheme other than Basic, base64 that does not decode to clientId:clientSecret, wrong secret, or a scope your credentials were not provisioned for.

A 401 on any other endpoint means the token itself was rejected β€” absent, malformed, expired, or not valid for the channel. The body distinguishes the two cases:

{ "message": "Bearer token required", "error": "Unauthorized", "statusCode": 401 }
{ "message": "Invalid api token", "error": "Unauthorized", "statusCode": 401 }

A valid token is not enough on its own: most endpoints also require a signature. If the token is fine and you still get a 401, read request signing β€” the two failures look identical from the outside.