Code Authorisation flow
Code Authorisation is the OAuth 2.0 authorization-code grant. It suits a partner platform whose end-users should each act with their own TreasurySpring identity, so that every action is attributed to the individual user in TreasurySpring's audit trail (the impersonation pattern described in Authentication).
If a single service identity acting on behalf of all your users is sufficient, use Client Credentials instead — it is simpler to operate and needs none of the consent flow below.
Provisioning
Code Authorisation clients are provisioned by TreasurySpring, not self-served. Your account manager registers, for each client:
- the
client_id/client_secretyour platform authenticates with, and - one or more redirect URIs — the exact URLs the end-user's browser may be returned to after consent.
Redirect URIs are matched exactly, so register every callback URL your application uses (for example a separate URL per environment). Contact your account manager to add or change the redirect URIs on an existing client; this does not change your client_id or client_secret.
The flow
1. Redirect to consent
Send the end-user to the TreasurySpring consent page with your client_id and a registered redirect_uri.
2. User consents
The user approves which entities your application may access.
3. Receive a code
The user is redirected back to your redirect_uri with a short-lived authorization code.
4. Exchange for a token
Exchange the code for an access token and refresh token at the token endpoint.
1. Redirect the user to the consent page
Send the end-user's browser to the TreasurySpring consent page, passing your client_id, the redirect_uri you want the user returned to, and an optional state:
| Environment | Consent page |
|---|---|
| Production | https://app.treasuryspring.com/portal/api/authorize |
| Sandbox | https://app.sandbox.treasuryspring.com/portal/api/authorize |
https://app.treasuryspring.com/portal/api/authorize?client_id=<client_id>&redirect_uri=<redirect_uri>&state=<state>
- The consent page is served on the
app.host (the portal), whereas the token endpoint is on theapi.host — they are different hosts, so take each URL as written. redirect_urimust exactly match one of the URIs registered for your client — including scheme, host, path, trailing slash, and case. An unregistered value is rejected.stateis optional. When supplied, it is returned unchanged when the user is redirected back — use it to carry per-request context through the flow and to protect against CSRF.
2. User consents
The user signs in (if needed) and approves which entities your application may access. Consent is per user, so each of your end-users completes this once.
3. Receive the authorization code
On approval, TreasurySpring redirects the browser to your redirect_uri with a short-lived, single-use code — and your state echoed back if you supplied one:
<redirect_uri>?code=<authorization_code>&state=<state>
If you sent a state, verify it matches the value you sent before exchanging the code.
4. Exchange the code for tokens
Exchange the code for an access token at the token endpoint POST/oauth/token. Authenticate the client with HTTP Basic auth (client_id:client_secret, base64-encoded).
- cURL
- Python
curl --location 'https://api.treasuryspring.com/oauth/token' \
-H 'Authorization: Basic <base64_client_id_and_secret>' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'client_id=<client_id>' \
--data-urlencode 'code=<authorization_code>' \
--data-urlencode 'redirect_uri=<redirect_uri>'
import base64
import requests
basic = base64.b64encode(b"<client_id>:<client_secret>").decode()
resp = requests.post(
"https://api.treasuryspring.com/oauth/token",
headers={
"Authorization": f"Basic {basic}",
"Content-Type": "application/x-www-form-urlencoded",
},
data={
"grant_type": "authorization_code",
"client_id": "<client_id>",
"code": "<authorization_code>",
"redirect_uri": "<redirect_uri>",
},
)
tokens = resp.json()
access_token = tokens["access_token"]
refresh_token = tokens["refresh_token"]
The response contains an access_token, a refresh_token, expires_in (seconds), and token_type. Send the access token as Authorization: Bearer <access_token> on subsequent API calls.
redirect_uri if your client has more than one registeredInclude the same redirect_uri you used in step 1 in the token exchange. When a client has multiple registered redirect URIs, this is required: if it is omitted, the authorization server falls back to the first registered redirect URI, and the exchange fails with invalid_request whenever the user was authorised against any other one.
Clients with a single registered redirect URI may omit it, but we recommend always sending it — it matches the value used at authorization and follows the OAuth 2.0 spec. It must be identical to the authorization-request value.
Refreshing a token
Use the refresh token to obtain a new access token without prompting the user again. No redirect_uri is needed for this grant.
- cURL
- Python
curl --location 'https://api.treasuryspring.com/oauth/token' \
-H 'Authorization: Basic <base64_client_id_and_secret>' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'client_id=<client_id>' \
--data-urlencode 'refresh_token=<refresh_token>'
resp = requests.post(
"https://api.treasuryspring.com/oauth/token",
headers={
"Authorization": f"Basic {basic}",
"Content-Type": "application/x-www-form-urlencoded",
},
data={
"grant_type": "refresh_token",
"client_id": "<client_id>",
"refresh_token": refresh_token,
},
)
tokens = resp.json()
A new access token and refresh token are returned. See Token lifetime and refresh for guidance on when to refresh.
Next steps
- Authentication — grant-type reference, base URLs, and token lifetime.
- API Reference:
POST /oauth/token— the full request/response contract. - Errors & responses — what auth failures look like and how to recover.