Payments
Overview
Scope: tb:payments:write (create), tb:payments:read (read)
Supported payment systems: INNER, SWIFT, INTERNAL_CARDS. Each has its own endpoint and its own request shape, and each exposes four endpoints.
| Purpose | Endpoints |
|---|---|
| Create and send in one call | POST /v1/payments/inner · POST /v1/payments/swift · POST /v1/payments/internal-cards |
| Prepare without sending | POST /v1/payments/inner/prepare · POST /v1/payments/swift/prepare · POST /v1/payments/internal-cards/prepare |
| Send a prepared payment | POST /v1/payments/inner/:paymentId/send · POST /v1/payments/swift/:paymentId/send · POST /v1/payments/internal-cards/:paymentId/send |
| Calculate the fee | POST /v1/payments/inner/calculate-fee · POST /v1/payments/swift/calculate-fee · POST /v1/payments/internal-cards/calculate-fee |
prepare and calculate-fee take the same body as the create call. send takes no body.
| Status | Meaning |
|---|---|
DRAFT | Prepared but not sent yet. |
PROCESSING | Submitted and being processed. |
ON_REVIEW | Held for manual review. |
NEED_ACTION | Waiting for an action before it can continue. |
SUCCESSFUL | Completed. |
REFUNDED | Returned to the sender. |
DECLINED | Rejected. |
Payment direction is INCOMING or OUTGOING.
Base request fields (all payment types)
| Name | Type | Required | Description |
|---|---|---|---|
sourceCurrencyId | UUID | Required | Debit currency. |
targetCurrencyId | UUID | Required | Credit currency. |
sourceAmount | string (decimal) | Conditional | Amount to debit. Pass either this or targetAmount. |
targetAmount | string (decimal) | Conditional | Amount to credit. Pass either this or sourceAmount. |
quoteId | UUID | Conditional | Locked quote id, required when sourceCurrencyId ≠ targetCurrencyId. |
note | string | Optional | Free-form note. |
metadata | object | Optional | Free-form JSON returned back on the payment object. |
attachments is response-only.
It is returned on the payment object but is not accepted in creation requests.
About quoteId
For transfers between different currencies a quoteId is required — the id of a locked quote that fixes the exchange rate at the moment of sending. A quote has a limited lifetime. If the currencies match, no quoteId is needed.
Internal transfer (INNER)
/v1/payments/innerAuth: Bearer access token — how to use the access token.
Scopes: tb:payments:write
A transfer between accounts inside the system — instant, no external banks.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| senderAccountId | UUID | Required | The account to debit. | |
| recipientAccountId | UUID | Required | The account to credit. | |
| recipientClientId | UUID | Required | The recipient client. | |
| recipientClientType | INDIVIDUAL | CORPORATE | Required | The kind of recipient client. | |
| recipientEmail | string | Optional | Recipient email. | |
| recipientPhoneNumber | string | Optional | Recipient phone number. |
/v1/payments/innercurl -X POST 'https://sandbox-bank.tedr.com/open-banking/v1/payments/inner' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-d '{
"senderAccountId": "05155318-22a3-45aa-9db0-0ebfe8f21e8c",
"recipientAccountId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"recipientClientId": "c9788fca-ed84-49e9-a511-81af1906c393",
"recipientClientType": "INDIVIDUAL",
"recipientEmail": "recipient@example.com",
"recipientPhoneNumber": "+1234567890",
"sourceCurrencyId": "99d41c98-93d1-471a-a73a-4cac9e3587e7",
"targetCurrencyId": "99d41c98-93d1-471a-a73a-4cac9e3587e7",
"sourceAmount": "100.00",
"note": "Payment for services",
"metadata": { "orderId": "ORD-123" }
}'
const response = await fetch('https://sandbox-bank.tedr.com/open-banking/v1/payments/inner', {
method: 'POST',
headers: {
Authorization: 'Bearer <access-token>',
'Content-Type': 'application/json',
},
body: JSON.stringify({
senderAccountId: '05155318-22a3-45aa-9db0-0ebfe8f21e8c',
recipientAccountId: 'b2c3d4e5-f6a7-8901-bcde-f12345678901',
recipientClientId: 'c9788fca-ed84-49e9-a511-81af1906c393',
recipientClientType: 'INDIVIDUAL',
recipientEmail: 'recipient@example.com',
recipientPhoneNumber: '+1234567890',
sourceCurrencyId: '99d41c98-93d1-471a-a73a-4cac9e3587e7',
targetCurrencyId: '99d41c98-93d1-471a-a73a-4cac9e3587e7',
sourceAmount: '100.00',
note: 'Payment for services',
metadata: { orderId: 'ORD-123' },
}),
});
const { data } = await response.json();
import requests
response = requests.post(
'https://sandbox-bank.tedr.com/open-banking/v1/payments/inner',
headers={'Authorization': 'Bearer <access-token>'},
json={
'senderAccountId': '05155318-22a3-45aa-9db0-0ebfe8f21e8c',
'recipientAccountId': 'b2c3d4e5-f6a7-8901-bcde-f12345678901',
'recipientClientId': 'c9788fca-ed84-49e9-a511-81af1906c393',
'recipientClientType': 'INDIVIDUAL',
'recipientEmail': 'recipient@example.com',
'recipientPhoneNumber': '+1234567890',
'sourceCurrencyId': '99d41c98-93d1-471a-a73a-4cac9e3587e7',
'targetCurrencyId': '99d41c98-93d1-471a-a73a-4cac9e3587e7',
'sourceAmount': '100.00',
'note': 'Payment for services',
'metadata': {'orderId': 'ORD-123'},
},
)
data = response.json()['data']
The body also takes the base request fields.
The sender is taken from the token.
SWIFT transfer
/v1/payments/swiftAuth: Bearer access token — how to use the access token.
Scopes: tb:payments:write
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| purposeOfPayment | string | Required | Purpose of the payment. | |
| sourceOfFunds | enum | Required | COMMISSION_OF_SALES, DIVIDEND_INCOME_FROM_SHARE, INVESTMENT_CAPITAL, REVENUE_GENERATED_FROM_BUSINESS_ACTIVITIES, SALARY, SAVING. | |
| internal.accountId | UUID | Required | The sender's account. | |
| external | object | Required | The recipient, see below. | |
| external.accountHolderName | string | Required | Name on the recipient account. | |
| external.clientType | INDIVIDUAL | CORPORATE | Required | The kind of recipient. | |
| external.bankCode | string | Required | BIC, 8 or 11 characters. | |
| external.accountNumber | string | Required | Recipient account number or IBAN. | |
| external.branchCode | string | Optional | Derived from bankCode when omitted. | |
| external.firstName · lastName · middleName | string | Optional | Recipient name parts. | |
| external.address | object | Required | countryId (UUID), postalCode, line1, line2?. | |
| external.bank | object | Required | name, countryId (UUID), cityName?, addressLine1?, addressLine2?, postalCode?. | |
| external.intermediaryBank | object | Optional | bankCode, accountNumber?, name?, countryId?, cityName?, addressLine1?, addressLine2?. |
/v1/payments/swiftcurl -X POST 'https://sandbox-bank.tedr.com/open-banking/v1/payments/swift' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-d '{
"purposeOfPayment": "Payment for consulting services",
"sourceOfFunds": "REVENUE_GENERATED_FROM_BUSINESS_ACTIVITIES",
"sourceCurrencyId": "99d41c98-93d1-471a-a73a-4cac9e3587e7",
"targetCurrencyId": "2e5d9800-f467-4eb7-ad47-1f67a874a908",
"sourceAmount": "1000.00",
"quoteId": "7c9f2f2e-1a4b-4f1f-9a4e-58b0c2f1a3d2",
"note": "Invoice #INV-2024-001",
"metadata": { "orderId": "ORD-123" },
"internal": {
"accountId": "05155318-22a3-45aa-9db0-0ebfe8f21e8c"
},
"external": {
"accountHolderName": "Acme Corp GmbH",
"clientType": "CORPORATE",
"bankCode": "DEUTDEFFXXX",
"accountNumber": "DE89370400440532013000",
"branchCode": "XXX",
"firstName": null,
"lastName": null,
"middleName": null,
"address": {
"countryId": "6f0f2a51-1f4c-4a3e-9f4a-4b5b2d1c9a10",
"postalCode": "10117",
"line1": "Unter den Linden 1",
"line2": null
},
"bank": {
"name": "Deutsche Bank AG",
"countryId": "6f0f2a51-1f4c-4a3e-9f4a-4b5b2d1c9a10",
"cityName": "Berlin",
"addressLine1": "Taunusanlage 12",
"addressLine2": null,
"postalCode": "60325"
},
"intermediaryBank": {
"bankCode": "CHASUS33",
"accountNumber": "9876543210",
"name": "JPMorgan Chase",
"countryId": "a1c3e5f7-2b4d-4e6f-8a0b-1c2d3e4f5a6b"
}
}
}'
const response = await fetch('https://sandbox-bank.tedr.com/open-banking/v1/payments/swift', {
method: 'POST',
headers: {
Authorization: 'Bearer <access-token>',
'Content-Type': 'application/json',
},
body: JSON.stringify({
purposeOfPayment: 'Payment for consulting services',
sourceOfFunds: 'REVENUE_GENERATED_FROM_BUSINESS_ACTIVITIES',
sourceCurrencyId: '99d41c98-93d1-471a-a73a-4cac9e3587e7',
targetCurrencyId: '2e5d9800-f467-4eb7-ad47-1f67a874a908',
sourceAmount: '1000.00',
quoteId: '7c9f2f2e-1a4b-4f1f-9a4e-58b0c2f1a3d2',
note: 'Invoice #INV-2024-001',
metadata: { orderId: 'ORD-123' },
internal: {
accountId: '05155318-22a3-45aa-9db0-0ebfe8f21e8c',
},
external: {
accountHolderName: 'Acme Corp GmbH',
clientType: 'CORPORATE',
bankCode: 'DEUTDEFFXXX',
accountNumber: 'DE89370400440532013000',
branchCode: 'XXX',
firstName: null,
lastName: null,
middleName: null,
address: {
countryId: '6f0f2a51-1f4c-4a3e-9f4a-4b5b2d1c9a10',
postalCode: '10117',
line1: 'Unter den Linden 1',
line2: null,
},
bank: {
name: 'Deutsche Bank AG',
countryId: '6f0f2a51-1f4c-4a3e-9f4a-4b5b2d1c9a10',
cityName: 'Berlin',
addressLine1: 'Taunusanlage 12',
addressLine2: null,
postalCode: '60325',
},
intermediaryBank: {
bankCode: 'CHASUS33',
accountNumber: '9876543210',
name: 'JPMorgan Chase',
countryId: 'a1c3e5f7-2b4d-4e6f-8a0b-1c2d3e4f5a6b',
},
},
}),
});
const { data } = await response.json();
import requests
response = requests.post(
'https://sandbox-bank.tedr.com/open-banking/v1/payments/swift',
headers={'Authorization': 'Bearer <access-token>'},
json={
'purposeOfPayment': 'Payment for consulting services',
'sourceOfFunds': 'REVENUE_GENERATED_FROM_BUSINESS_ACTIVITIES',
'sourceCurrencyId': '99d41c98-93d1-471a-a73a-4cac9e3587e7',
'targetCurrencyId': '2e5d9800-f467-4eb7-ad47-1f67a874a908',
'sourceAmount': '1000.00',
'quoteId': '7c9f2f2e-1a4b-4f1f-9a4e-58b0c2f1a3d2',
'note': 'Invoice #INV-2024-001',
'metadata': {'orderId': 'ORD-123'},
'internal': {
'accountId': '05155318-22a3-45aa-9db0-0ebfe8f21e8c',
},
'external': {
'accountHolderName': 'Acme Corp GmbH',
'clientType': 'CORPORATE',
'bankCode': 'DEUTDEFFXXX',
'accountNumber': 'DE89370400440532013000',
'branchCode': 'XXX',
'firstName': None,
'lastName': None,
'middleName': None,
'address': {
'countryId': '6f0f2a51-1f4c-4a3e-9f4a-4b5b2d1c9a10',
'postalCode': '10117',
'line1': 'Unter den Linden 1',
'line2': None,
},
'bank': {
'name': 'Deutsche Bank AG',
'countryId': '6f0f2a51-1f4c-4a3e-9f4a-4b5b2d1c9a10',
'cityName': 'Berlin',
'addressLine1': 'Taunusanlage 12',
'addressLine2': None,
'postalCode': '60325',
},
'intermediaryBank': {
'bankCode': 'CHASUS33',
'accountNumber': '9876543210',
'name': 'JPMorgan Chase',
'countryId': 'a1c3e5f7-2b4d-4e6f-8a0b-1c2d3e4f5a6b',
},
},
},
)
data = response.json()['data']
The body also takes the base request fields.
Countries are passed as countryId (UUID).
Not as { "alpha3": "DEU" } — that shape returns 400. Resolve the id through the Dictionary. Country objects with alpha2 / alpha3 appear only in responses.
Internal cards payment (INTERNAL_CARDS)
/v1/payments/internal-cardsAuth: Bearer access token — how to use the access token.
Scopes: tb:payments:write
Moves money between the client's cards and accounts: top up a card from an account, withdraw from a card to an account, or transfer card to card.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| type | ACCOUNT_TO_CARD | CARD_TO_ACCOUNT | CARD_TO_CARD | Required | Direction of the operation. | |
| sender | object | Required | cardId?, accountId?, email?, phoneNumber? — fill the one matching type. | |
| recipient | object | Required | cardId?, accountId?, clientId (required), clientType (required), email?, phoneNumber?. |
/v1/payments/internal-cardscurl -X POST 'https://sandbox-bank.tedr.com/open-banking/v1/payments/internal-cards' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-d '{
"type": "CARD_TO_CARD",
"sourceCurrencyId": "99d41c98-93d1-471a-a73a-4cac9e3587e7",
"targetCurrencyId": "99d41c98-93d1-471a-a73a-4cac9e3587e7",
"sourceAmount": "100.00",
"note": "Card to card transfer",
"metadata": { "orderId": "ORD-123" },
"sender": {
"cardId": "0f2d6d63-4d9e-4a01-9d0f-9a3b6f1f2c77"
},
"recipient": {
"cardId": "c1b2a3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"clientId": "c9788fca-ed84-49e9-a511-81af1906c393",
"clientType": "INDIVIDUAL"
}
}'
const response = await fetch(
'https://sandbox-bank.tedr.com/open-banking/v1/payments/internal-cards',
{
method: 'POST',
headers: {
Authorization: 'Bearer <access-token>',
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'CARD_TO_CARD',
sourceCurrencyId: '99d41c98-93d1-471a-a73a-4cac9e3587e7',
targetCurrencyId: '99d41c98-93d1-471a-a73a-4cac9e3587e7',
sourceAmount: '100.00',
note: 'Card to card transfer',
metadata: { orderId: 'ORD-123' },
sender: {
cardId: '0f2d6d63-4d9e-4a01-9d0f-9a3b6f1f2c77',
},
recipient: {
cardId: 'c1b2a3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d',
clientId: 'c9788fca-ed84-49e9-a511-81af1906c393',
clientType: 'INDIVIDUAL',
},
}),
},
);
const { data } = await response.json();
import requests
response = requests.post(
'https://sandbox-bank.tedr.com/open-banking/v1/payments/internal-cards',
headers={'Authorization': 'Bearer <access-token>'},
json={
'type': 'CARD_TO_CARD',
'sourceCurrencyId': '99d41c98-93d1-471a-a73a-4cac9e3587e7',
'targetCurrencyId': '99d41c98-93d1-471a-a73a-4cac9e3587e7',
'sourceAmount': '100.00',
'note': 'Card to card transfer',
'metadata': {'orderId': 'ORD-123'},
'sender': {
'cardId': '0f2d6d63-4d9e-4a01-9d0f-9a3b6f1f2c77',
},
'recipient': {
'cardId': 'c1b2a3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d',
'clientId': 'c9788fca-ed84-49e9-a511-81af1906c393',
'clientType': 'INDIVIDUAL',
},
},
)
data = response.json()['data']
The body also takes the base request fields.
Calculate the fee
/v1/payments/swift/calculate-feeAuth: Bearer access token — how to use the access token.
The body is the full body of the future payment.
/v1/payments/swift/calculate-feecurl -X POST 'https://sandbox-bank.tedr.com/open-banking/v1/payments/swift/calculate-fee' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json'
const response = await fetch(
'https://sandbox-bank.tedr.com/open-banking/v1/payments/swift/calculate-fee',
{
method: 'POST',
headers: {
Authorization: 'Bearer <access-token>',
'Content-Type': 'application/json',
},
},
);
const { data } = await response.json();
import requests
response = requests.post(
'https://sandbox-bank.tedr.com/open-banking/v1/payments/swift/calculate-fee',
headers={
'Authorization': 'Bearer <access-token>',
'Content-Type': 'application/json',
},
)
data = response.json()['data']
{
"status": "SUCCESS",
"data": {
"totalFee": "10.00",
"totalAmount": "1010.00",
"currencyTicker": "USD",
"originalAmount": "1000.00",
"originalTotalAmount": "1010.00",
"originalTotalFee": "10.00",
"originalProviderFee": "7.00",
"originalBankFee": "3.00"
}
}
Each payment system has its own calculate-fee endpoint: /v1/payments/inner/calculate-fee, /v1/payments/swift/calculate-fee and /v1/payments/internal-cards/calculate-fee.
Browse payments
/v1/paymentsAuth: Bearer access token — how to use the access token.
Scopes: tb:payments:read
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| paymentSystems[] | — | Optional | INNER, SWIFT, INTERNAL_CARDS. | |
| statuses[] | — | Optional | DRAFT, PROCESSING, ON_REVIEW, NEED_ACTION, SUCCESSFUL, REFUNDED, DECLINED. | |
| directions[] | — | Optional | INCOMING, OUTGOING. | |
| dateRange[from] · dateRange[to] | — | Optional | Range over the creation date. | |
| settledAt[from] · settledAt[to] | — | Optional | Range over the settlement date. | |
| currencyIds[] | — | Optional | By currency on either side. | |
| sourceCurrencyIds[] | — | Optional | By debit currency. | |
| targetCurrencyIds[] | — | Optional | By credit currency. | |
| isFavorite | — | Optional | Favourites only. | |
| search | — | Optional | Full-text search. | |
| pagination[limit] · pagination[skip] | — | Optional | Pagination. |
/v1/paymentscurl -G 'https://sandbox-bank.tedr.com/open-banking/v1/payments' \
-H 'Authorization: Bearer <access-token>' \
--data-urlencode 'paymentSystems[]=SWIFT' \
--data-urlencode 'directions[]=OUTGOING' \
--data-urlencode 'dateRange[from]=2026-01-01T00:00:00Z'
const query = new URLSearchParams([
['paymentSystems[]', 'SWIFT'],
['directions[]', 'OUTGOING'],
['dateRange[from]', '2026-01-01T00:00:00Z'],
]);
const response = await fetch(`https://sandbox-bank.tedr.com/open-banking/v1/payments?${query}`, {
headers: { Authorization: 'Bearer <access-token>' },
});
const { data } = await response.json();
import requests
response = requests.get(
'https://sandbox-bank.tedr.com/open-banking/v1/payments',
headers={'Authorization': 'Bearer <access-token>'},
params=[
('paymentSystems[]', 'SWIFT'),
('directions[]', 'OUTGOING'),
('dateRange[from]', '2026-01-01T00:00:00Z'),
],
)
data = response.json()['data']
The list accepts only the filters above. GET /v1/payments/:paymentId returns a single payment; its shape depends on paymentSystem.
Payment object
Shared by all payment systems.
| Field | Type | Description |
|---|---|---|
id | UUID | Payment id. |
paymentSystem | enum | INNER, SWIFT, INTERNAL_CARDS. |
status | enum | Current status. |
previousStatus | enum | Previous status, when the payment has changed state. |
direction | enum | INCOMING, OUTGOING. |
isFavorite | boolean | Favourite flag. |
note | string | Free-form note. |
purposeOfPayment | string | Purpose of the payment. |
attachments | array | Attached files. Response-only. |
metadata | object | null | Whatever was sent on creation. |
sender · recipient | object | clientId, clientType. |
amount · fee · totalAmount | object | Each with exchangeRate, source, target. |
createdAt · updatedAt · settled | date | Lifecycle timestamps. |
Per-system additions: INNER and INTERNAL_CARDS add senderClient / recipientClient; INTERNAL_CARDS also adds type; SWIFT adds sourceOfFunds, internal and external with the full bank details.
