Connecting a client through OAuth 2.0

JWT claims by scenario
ScenariosubcompanyId
Personal accountclient's userIdabsent
Company accountclient's userIdpresent
GET/v1/oauth/deeplink

Auth: None — public endpoint

Fetch the base authorization URL instead of hard-coding it, so the same integration works across environments.

GET/v1/oauth/deeplink
{
  "status": "SUCCESS",
  "data": { "url": "https://dev-tbank.tedr.com/oauth/" }
}

Discover the available scopes (optional)

GET/v1/oauth/scopes

Auth: None — public endpoint

Query parameters: ids[], names[], scopes[], descriptions[], categories[], pagination[limit], pagination[skip].

GET/v1/oauth/scopes
curl -G 'https://sandbox-bank.tedr.com/open-banking/v1/oauth/scopes' \
  -d 'pagination[skip]=0' \
  -d 'pagination[limit]=20'
{
  "status": "SUCCESS",
  "data": {
    "nodes": [
      {
        "id": "cc2d0454-0a68-4443-b71c-14e2b60ddd2e",
        "createdAt": "2026-01-15T10:00:00.000Z",
        "updatedAt": "2026-01-15T10:00:00.000Z",
        "name": "Read accounts",
        "description": "Grants read-only access to accounts.",
        "scope": "tb:accounts:read",
        "category": "INDIVIDUAL"
      }
    ],
    "count": 1,
    "cursor": 1
  }
}
Scopes currently used by the API
ScopeGrants
tb:accounts:readRead accounts, balances and bank details.
tb:accounts:writeCreate accounts.
tb:payments:readRead payments.
tb:payments:writeCreate and send payments.
tb:tariffs:readRead tariffs, limits and fee calculations.
tb:personal-data:readRead the client's personal data.
tb:internal-cards:readRead cards.
tb:internal-cards:writeOrder and manage cards.
tb:external-cards:readRead Stripe cards and Stripe payment events.
Authorization URL parameters
ParameterRequiredDescription
responseTypeyesMust be code.
clientIdyesThe partner client id issued during onboarding.
redirectUriyesWhere the client is sent back after consent.
scopesyesComma-separated list of the scopes being requested.
stateyesAn arbitrary string the partner generates. It is returned unchanged on the redirect — verify it to protect against CSRF.
accountCategorynoLimits what the consent page offers: INDIVIDUAL — personal accounts only, CORPORATE — corporate accounts only, omitted — the client chooses.

URL template

{deeplink}?responseType=code
  &clientId={PARTNER_CLIENT_ID}
  &redirectUri={CALLBACK_URL}
  &scopes={COMMA_SEPARATED_SCOPES}
  &state={ARBITRARY_STRING}
  &accountCategory={INDIVIDUAL|CORPORATE}   // optional

Example

https://dev-tbank.tedr.com/oauth/?responseType=code
  &clientId=<your-client-id>
  &redirectUri=https://partner-app.com/oauth/callback
  &scopes=tb:accounts:read,tb:accounts:write,tb:payments:read,tb:payments:write
  &state=random-csrf-token-xyz
  &accountCategory=CORPORATE

Successful redirect

https://partner-app.com/oauth/callback?state=random-csrf-token-xyz&code=<authorization-code>
Errors returned on the redirect
Error in the URLReason
access_deniedThe client pressed "Deny".
unsupported_response_typeresponseType was not code.
invalid_scopeA requested scope does not exist.

Error redirects

https://partner-app.com/oauth/callback?state=string&error=access_denied&error_description=The+user+refused+authorization
https://partner-app.com/oauth/callback?state=string&error=unsupported_response_type&error_description=The+response_type+is+invalid
https://partner-app.com/oauth/callback?state=string&error=invalid_scope&error_description=Invalid+scope+specified.

Exchange the code for an access token

POST/v1/oauth/access-token

Auth: None — public endpoint

The code is valid for exactly 1 minute. Perform the exchange immediately after receiving the redirect.

Body parameters
NameTypeRequiredDefaultDescription
grantTypestringRequiredAlways authorization_code.
codestringRequiredThe authorization code from the redirect.
redirectUristringRequiredMust match the one used in the authorization URL.
clientIdstringRequiredThe partner client id.
clientSecretstringRequiredThe partner client secret. Keep it server-side.
POST/v1/oauth/access-token
curl -X POST 'https://sandbox-bank.tedr.com/open-banking/v1/oauth/access-token' \
  -H 'Content-Type: application/json' \
  -d '{
    "grantType": "authorization_code",
    "code": "<authorization-code>",
    "redirectUri": "https://partner-app.com/oauth/callback",
    "clientId": "<your-client-id>",
    "clientSecret": "<your-client-secret>"
  }'
{
  "status": "SUCCESS",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "tokenType": "Bearer",
    "expiresIn": 31536000
  }
}

Decoded token payload

{
  "externalServiceId": "<your-client-id>",
  "scopes": ["tb:accounts:read", "tb:payments:write"],
  "sub": "c9788fca-ed84-49e9-a511-81af1906c393",
  "jti": "2b6df3c0-6cfb-4a57-ad61-86ab371384a3",
  "iss": "<issuer>",
  "iat": 1739981383,
  "exp": 1771517383
}

Error response

{
  "status": "ERROR",
  "errors": [{
    "title": "OAuth invalid payload for exchange code",
    "code": "OPEN-BANKING-029",
    "message": "Invalid payload was provided in exchange code for token."
  }]
}

Authorization header

Authorization: Bearer <access-token>