Skip to content

Overview of OAuth 2.0 within the Upvest Investment API

The Investment API uses opaque access tokens obtained using the OAuth 2.0 Client Credentials flow.

During the onboarding process, each client receives a client ID, a client secret, and a key ID which are required to request access tokens via our OAuth 2.0 endpoints.

All endpoints in the Upvest API are protected through authentication. This means you must authenticate before you call the API.

How to authenticate

Authentication is a three-step process:

  1. Request an access token from the token endpoint, specifying the scopes you need. Access tokens expire within 1800 seconds (30 mins). When this occurs, you must request a new token.
  2. Read the token response to extract the access token and its metadata.
  3. Call the API with that token in the Authorization header until it expires, then request a new one.

Every request to Upvest, including the request to the token endpoint, must additionally be signed using HTTP message signatures. The client credentials identify and authorise you; the message signature protects the request against tampering in transit. Make sure you have set up request signing before you attempt to fetch a token.

Step 1: Request an access token

Send a POST request to the token endpoint for your environment:

EnvironmentToken endpoint
Sandboxhttps://sandbox.upvest.co/auth/token
Livehttps://api.upvest.co/auth/token

The request body must be application/x-www-form-urlencoded and contain the following fields:

FieldRequiredDescription
client_idYesThe client ID provided during onboarding.
client_secretYesThe client secret provided during onboarding.
grant_typeYesMust always be client_credentials.
scopeYesA space-delimited list of the scopes you are requesting for this token. See Authentication scopes.

The request must also carry the upvest-client-id header and the HTTP message signature headers (signature and signature-input).

curl --request POST 'https://sandbox.upvest.co/auth/token' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --header 'upvest-client-id: <your_client_id>' \
  --header 'signature: <generated_signature>' \
  --header 'signature-input: <generated_signature_input>' \
  --data-urlencode 'client_id=<your_client_id>' \
  --data-urlencode 'client_secret=<your_client_secret>' \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode 'scope=users:read users:admin'

See the HTTP message signatures page for how to generate the signature and signature-input headers.

All tokens in both the sandbox and the live environments expire within 1800 seconds (30 minutes). When the token expires, you must request a new token.

Step 2: Read the token response

A successful request returns a JSON body containing the token and its metadata:

{
  "access_token": "<access_token>",
  "expires_in": 86400,
  "token_type": "bearer",
  "scope": "users:read users:admin"
}
FieldDescription
access_tokenThe access token to send on subsequent API requests.
expires_inThe token's lifetime in seconds (in this example, 86400 seconds / 24 hours).
token_typeAlways set equal to bearer.
scopeThe scopes granted to this token.

Step 3: Call the API with the token

Send the token in the standard Authorization header on every API request, and continue to send upvest-client-id and your message-signature headers:

upvest-client-id: <your_client_id>
Authorization: Bearer <access_token>
signature: <generated_signature>
signature-input: <generated_signature_input>

The token remains valid for the number of seconds returned in expires_in. Reuse the same token across requests until it is close to expiry, then request a new one. Avoid fetching a new token for every call.

  • Scope tokens to the task. Request only the scopes a given workflow needs. For example, use a users:read token for read-only access, and request a separate users:admin token only when you need to onboard or modify users.
  • Cache and reuse tokens. Hold a token until shortly before expires_in elapses rather than requesting one per call, to reduce latency and load.
  • Store your client secret securely. Treat the client secret like a password: keep it server-side, never embed it in client applications or commit it to source control.

Authentication scopes

The Upvest Investment API supports granular authentication scopes to improve security. By specifying scopes when you request a token, you restrict the permissions and data access that token grants.

Two distinct sets of scopes are relevant:

  • Scopes granted to your client — the full set of scopes Upvest has enabled for you. Upvest provides this list when sending your API credentials during onboarding. You can only request scopes from this set.
  • Scopes on a token — the subset of your granted scopes that you request in the scope field when fetching a particular token. A token only carries the permissions you explicitly request for it.

You must ensure that each token carries the scopes required by the endpoints you call with it. The required scopes for each endpoint are listed in the security details of that endpoint.

In the API reference: open the endpoint to view the required scopes. You can select View security details in the Security section to see scope details.

The Security window shows additional details, including optional scopes for the endpoint.

New functionality may introduce new scopes, and the set of scopes can change over time. The security details in the API reference are always the authoritative, up-to-date source for which scopes an endpoint requires. The table below is a convenience snapshot and may lag behind the API reference.

List of authentication scopes

The following scopes are available in the Upvest Investment API. For the current required scopes of any specific endpoint, always check its security details in the API reference.

ScopePermissions
Users, accounts, and access
accounts:readRead accounts and account groups.
accounts:adminCreate, update, and delete accounts and account groups.
users:readRead users and user identifiers.
users:adminCreate, update, and delete users and user identifiers.
businesses:readRead business entities.
businesses:adminCreate, update, and delete business entities.
checks:readRead checks by user or by check ID.
checks:adminCreate checks.
roles:readRead roles.
roles:adminCreate, update, and deactivate roles.
Taxes
taxes:readRead tax residencies.
taxes:adminModify tax residencies and create or modify tax exemptions.
Instruments and prices
instruments:readRead instruments.
prices:readRead instrument prices, including latest price and open, high, low, close (OHLC) prices.
Orders
orders:readRead orders.
orders:adminCreate, update, and delete orders.
Investment solutions
portfolios:readRead portfolios.
portfolios:adminModify portfolios, including allocations, configurations, and rebalancing.
savings_plans:readRead savings plans.
savings_plans:adminCreate and read savings plans.
account_liquidations:readRead account liquidations.
account_liquidations:adminTrigger, read, and cancel account liquidations.
Payments
payments:readRead pay-in and withdrawal operations.
payments:adminPay-in and withdrawal operations, including direct debits and withdrawals.
mandates:readRead mandates.
mandates:adminCreate, update, and delete mandates.
reference_accounts:readRead reference accounts.
reference_accounts:adminCreate, update, and delete reference accounts.
topups:readRead top-up operations.
topups:adminTop-up operations.
credit_fundings:readRead credit funding details.
Balances
positions:readRead positions and cash balances.
valuations:readRead account valuations.
account_returns:readRead account returns.
virtual_cash_balances:adminIncrease and decrease virtual cash balances.
Fees
fees:readRead fee operations.
fees:adminCreate and read fee operations.
transaction_fees:readRead transaction fee operations.
transaction_fees:adminCreate and read transaction fee operations.
Reporting
files:readRead file metadata.
reports:readRead reports.
reports:adminCreate reports.
transactions:readRead cash and securities transactions.
Transfers
securities_transfers:readRead securities transfers.
securities_transfers:adminCreate and read securities transfers.
account_transfers:readRead account transfers.
account_transfers:adminAccount transfer operations.
isa_transfers:adminISA transfer operations.
Corporate actions
corporate_actions:adminCreate, update, and delete corporate action instructions.
Webhooks
webhooks:readRead webhooks.
webhooks:adminCreate, update, and delete webhooks.
Tests
tests:adminTrigger a bank transaction for testing purposes.

For more on how scopes map to permissions, see Permissions.

Troubleshooting scope errors

Scope errors include a status code to help you troubleshoot.

401 when requesting a token

If you request a scope that has not been granted to your client, the token endpoint responds with status 401 and does not issue a token.

This happens when the scope field includes a scope that is outside the set Upvest enabled for you. Because the set of scopes granted to your client is configured on the Upvest side, you cannot change it yourself.

To resolve a 401:

  • Compare the scopes in your request against the list of granted scopes Upvest sent you during onboarding, and remove any scope that is not on that list.
  • If your integration needs a scope that has not been granted, contact the Upvest team to enable the required scopes.

403 when calling an endpoint

If you call an endpoint with a token that does not include the scope that endpoint requires, the API responds with status 403.

This happens when the scope is available to your client but was not included in the scope field when you fetched the token. In other words, the permission exists for you, but this particular token does not carry it.

To resolve a 403:

  • Check the endpoint's required scopes under View security details in the API reference.
  • Request a new token whose scope field includes the required scope, then retry the request with that token.

A 401 means the scope is not available to your client and Upvest needs to enable it. A 403 means the scope is available to you but missing from the token you used, which you can simply request a token with the required scopes.

Protecting requests against tampering

Using client_credentials alone lets us identify and authorise you, but it does not protect against a man-in-the-middle attack, where a request could be maliciously modified in transit.

To mitigate this, Upvest requires an additional layer of public key infrastructure in the form of HTTP message signatures on every request, including the token request.