Skip to content

Reference

shared.services.payment_providers.revolut.entities

RevolutAccountReference

Bases: AlanBaseEnum

Reference string of all the accounts declared on any Revolut worspace (=business account)

See: https://www.notion.so/alaninsurance/Eng-Plan-Revolut-Workspacesat-2ef1426e8be78022ba47eb4200d4ddd2?source=copy_link#2ef1426e8be780499ebaf04d28b34025 ⧉

alan_prevoyance_claims class-attribute instance-attribute

alan_prevoyance_claims = 'alan_prevoyance_claims'

cnp_claims class-attribute instance-attribute

cnp_claims = 'cnp_claims'

health_claims_be class-attribute instance-attribute

health_claims_be = 'health_claims_be'

health_claims_es class-attribute instance-attribute

health_claims_es = 'health_claims_es'

health_claims_fr class-attribute instance-attribute

health_claims_fr = 'health_claims_fr'

hps_direct_payments_fr class-attribute instance-attribute

hps_direct_payments_fr = 'hps_direct_payments_fr'

lamie_claims class-attribute instance-attribute

lamie_claims = 'lamie_claims'

RevolutBusinessAccountConfig dataclass

RevolutBusinessAccountConfig(
    base_url,
    api_credentials_secret_name,
    private_key,
    private_key_secret_name,
    client_id,
    issuer,
    webhook_signing_secret=None,
    webhook_signing_secret_name=None,
)

api_credentials_secret_name instance-attribute

api_credentials_secret_name

base_url instance-attribute

base_url

client_id instance-attribute

client_id

issuer instance-attribute

issuer

private_key instance-attribute

private_key

private_key_secret_name instance-attribute

private_key_secret_name

webhook_signing_secret class-attribute instance-attribute

webhook_signing_secret = None

webhook_signing_secret_name class-attribute instance-attribute

webhook_signing_secret_name = None

RevolutBusinessAccountName

Bases: AlanBaseEnum

alan_insurance class-attribute instance-attribute

alan_insurance = 'alan_insurance'

alan_sa class-attribute instance-attribute

alan_sa = 'alan_sa'

alan_services class-attribute instance-attribute

alan_services = 'alan_services'

marmot_be class-attribute instance-attribute

marmot_be = 'marmot_be'

RevolutBusinessAccountNamesAvailableByCountry

belgium class-attribute instance-attribute

belgium = (alan_insurance, marmot_be)

france class-attribute instance-attribute

france = (alan_insurance, alan_services)

prevoyance_france class-attribute instance-attribute

prevoyance_france = (alan_insurance,)

spain class-attribute instance-attribute

spain = (alan_insurance,)

RevolutCounterPartyInfo dataclass

RevolutCounterPartyInfo(counterparty_id, account_id)

Bases: DataClassJsonMixin

account_id instance-attribute

account_id

counterparty_id instance-attribute

counterparty_id

RevolutPrevoyancePaymentAccount dataclass

RevolutPrevoyancePaymentAccount(lamie, cnp, alan)

This dataclass is storing the account references used for Prévoyance.

alan instance-attribute

alan

cnp instance-attribute

cnp

lamie instance-attribute

lamie

RevolutTransactionCreatedEvent dataclass

RevolutTransactionCreatedEvent(
    business_account_name,
    transaction_id,
    type,
    request_id,
    created_at,
    updated_at,
    state,
    legs=list(),
    reason_code=None,
    completed_at=None,
    scheduled_for=None,
    reference=None,
    related_transaction_id=None,
    raw_event=None,
)

Event for Revolut TransactionCreated webhooks.

Emitted when a new transaction is created in Revolut. https://developer.revolut.com/docs/guides/manage-accounts/webhooks/about-webhooks#transaction-created-event ⧉

business_account_name instance-attribute

business_account_name

Revolut Business Account name passed as query string business_account_name to the webhook controller

completed_at class-attribute instance-attribute

completed_at = None

The date and time the transaction was completed, mandatory for completed state only.

created_at instance-attribute

created_at

The date and time the transaction was created.

legs class-attribute instance-attribute

legs = field(default_factory=list)

The legs of a transaction. There are 2 legs between your Revolut accounts and 1 leg in other cases.

raw_event class-attribute instance-attribute

raw_event = None

Raw webhook payload

reason_code class-attribute instance-attribute

reason_code = None

The optional reason code for declined or failed transaction state.

reference class-attribute instance-attribute

reference = None

The payment reference provided by the user.

related_transaction_id class-attribute instance-attribute

related_transaction_id = None

The ID of the related transaction. Only applicable when type=refund.

request_id instance-attribute

request_id

The request ID provided by the client.

scheduled_for class-attribute instance-attribute

scheduled_for = None

The optional date the transaction was scheduled for.

state instance-attribute

state

The transaction state: pending, completed, declined or failed.

transaction_id instance-attribute

transaction_id

The ID of transaction. (id in JSON payload)

type instance-attribute

type

The transaction type.

updated_at instance-attribute

updated_at

The date and time the transaction was last updated.

RevolutTransactionPaymentResponse

Bases: RevolutTransferResponse

The Revolut OpenAPI spec lacks the undocumented expected_processing_time field, add it here.

Counterparty of RevolutTransactionPaymentRequest

expected_processing_time class-attribute instance-attribute

expected_processing_time = None

RevolutTransactionStateChangedEvent dataclass

RevolutTransactionStateChangedEvent(
    business_account_name,
    transaction_id,
    request_id,
    old_state,
    new_state,
    updated_at,
    raw_event=None,
)

Event for Revolut TransactionStateChanged webhooks.

Emitted when an existing transaction changes state. https://developer.revolut.com/docs/guides/manage-accounts/webhooks/about-webhooks#transaction-state-changed-event ⧉

business_account_name instance-attribute

business_account_name

Revolut Business Account name passed as query string business_account_name to the webhook controller

new_state instance-attribute

new_state

The new state of the transaction.

old_state instance-attribute

old_state

The previous state of the transaction.

raw_event class-attribute instance-attribute

raw_event = None

Raw webhook payload

request_id instance-attribute

request_id

The request ID provided by the client.

transaction_id instance-attribute

transaction_id

The ID of the updated transaction. (id in JSON payload)

updated_at instance-attribute

updated_at

The date and time the transaction was last updated. (from timestamp in payload)

shared.services.payment_providers.revolut.errors

PotentialSwiftError

PotentialSwiftError(code=None, message=None)

Bases: RevolutError

A special case of RevolutError. Raised when we think it may be fixable by resetting the SWIFT. Context: many calls to /counterparty fail because we supply the wrong SWIFT number. We don't get details from the 400 response from Revolut, but we want to communicate that it's possibly caused by a SWIFT problem.

Source code in shared/services/payment_providers/revolut/errors.py
4
5
6
def __init__(self, code=None, message=None) -> None:  # type: ignore[no-untyped-def]
    self.code = code
    self.message = message

RevolutError

RevolutError(code=None, message=None)

Bases: Exception

A generic revolut exception we should handle

Source code in shared/services/payment_providers/revolut/errors.py
4
5
6
def __init__(self, code=None, message=None) -> None:  # type: ignore[no-untyped-def]
    self.code = code
    self.message = message

code instance-attribute

code = code

message instance-attribute

message = message

RevolutInternalError

RevolutInternalError(code=None, message=None)

Bases: RevolutError

Revolut issue, we can't act on it

Source code in shared/services/payment_providers/revolut/errors.py
4
5
6
def __init__(self, code=None, message=None) -> None:  # type: ignore[no-untyped-def]
    self.code = code
    self.message = message

RevolutNotAuthorizedError

RevolutNotAuthorizedError(code=None, message=None)

Bases: RevolutError

The access token is not valid.

We have a scheduled job running every 15 minutes to rotate the access token as it is valid for less than 1h. If the command failed to run for some reason, the token won't have been rotated, and will thus be expired / invalid.

See also https://www.notion.so/alaninsurance/Revolut-API-keys-Management-d536aabbf7ba4c278dc86a2da05b6b21#1cca1b9404314144bdc58f2fbd06b791 ⧉

Source code in shared/services/payment_providers/revolut/errors.py
4
5
6
def __init__(self, code=None, message=None) -> None:  # type: ignore[no-untyped-def]
    self.code = code
    self.message = message

RevolutNotFoundError

RevolutNotFoundError(code=None, message=None)

Bases: RevolutError

Resource does not exist on Revolut, it is up to us to decide whether we take this error into account or not

Source code in shared/services/payment_providers/revolut/errors.py
4
5
6
def __init__(self, code=None, message=None) -> None:  # type: ignore[no-untyped-def]
    self.code = code
    self.message = message

RevolutParallelRequestsError

RevolutParallelRequestsError(code=None, message=None)

Bases: RevolutError

See https://www.notion.so/alaninsurance/Revolut-payments-and-its-failures-68efe555a6a14610835b928ce09de883#ee51064d48de444f9763fbf66831b34d ⧉

Source code in shared/services/payment_providers/revolut/errors.py
4
5
6
def __init__(self, code=None, message=None) -> None:  # type: ignore[no-untyped-def]
    self.code = code
    self.message = message

shared.services.payment_providers.revolut.helpers

EEA_COUNTRIES module-attribute

EEA_COUNTRIES = [
    "AT",
    "AX",
    "BE",
    "BG",
    "CY",
    "CZ",
    "DE",
    "DK",
    "EA",
    "EE",
    "ES",
    "FI",
    "FR",
    "GB",
    "GF",
    "GI",
    "GP",
    "GR",
    "HR",
    "HU",
    "IC",
    "IE",
    "IT",
    "LT",
    "LU",
    "LV",
    "MF",
    "MQ",
    "MT",
    "NL",
    "PL",
    "PT",
    "RE",
    "RO",
    "SE",
    "SI",
    "SK",
    "YT",
    "NO",
    "IS",
    "LI",
    "JE",
    "GG",
    "IM",
]

PREVOYANCE_BANK_ACCOUNT_CONFIG_MAPPING module-attribute

PREVOYANCE_BANK_ACCOUNT_CONFIG_MAPPING = {
    alan_insurance: RevolutPrevoyancePaymentAccount(
        cnp=cnp_claims,
        lamie=lamie_claims,
        alan=alan_prevoyance_claims,
    )
}

get_revolut_account_id

get_revolut_account_id(reference)
Source code in shared/services/payment_providers/revolut/helpers.py
def get_revolut_account_id(reference: RevolutAccountReference) -> str:
    # Workspace key: eu_revolut_insurance
    match reference:
        case RevolutAccountReference.health_claims_fr:
            # Note: same config key in all countries
            return get_config_or_fail("REVOLUT_ALAN_INSURANCE_REIMBURSEMENT_ACCOUNT_ID")  # type: ignore[no-any-return]
        case RevolutAccountReference.health_claims_be:
            # Note: same config key in all countries
            return get_config_or_fail("REVOLUT_ALAN_INSURANCE_REIMBURSEMENT_ACCOUNT_ID")  # type: ignore[no-any-return]
        case RevolutAccountReference.health_claims_es:
            # Note: same config key in all countries
            return get_config_or_fail("REVOLUT_ALAN_INSURANCE_REIMBURSEMENT_ACCOUNT_ID")  # type: ignore[no-any-return]

        case RevolutAccountReference.cnp_claims:
            return get_config_or_fail(  # type: ignore[no-any-return]
                "REVOLUT_ALAN_INSURANCE_PREVOYANCE_CNP_PAYMENT_ACCOUNT_ID"
            )
        case RevolutAccountReference.lamie_claims:
            return get_config_or_fail(  # type: ignore[no-any-return]
                "REVOLUT_ALAN_INSURANCE_PREVOYANCE_LAMIE_PAYMENT_ACCOUNT_ID"
            )
        case RevolutAccountReference.alan_prevoyance_claims:
            return get_config_or_fail(  # type: ignore[no-any-return]
                "REVOLUT_ALAN_INSURANCE_PREVOYANCE_ALAN_PAYMENT_ACCOUNT_ID"
            )

        case RevolutAccountReference.hps_direct_payments_fr:
            return get_config_or_fail(  # type: ignore[no-any-return]
                "REVOLUT_ALAN_INSURANCE_HP_DIRECT_PAYMENTS_ACCOUNT_ID"
            )
        case _:
            assert_never(reference)  # Exhaustiveness check

get_revolut_config_from_business_account_name

get_revolut_config_from_business_account_name(account_name)
Source code in shared/services/payment_providers/revolut/helpers.py
def get_revolut_config_from_business_account_name(
    account_name: RevolutBusinessAccountName,
) -> RevolutBusinessAccountConfig:
    if RevolutBusinessAccountName[account_name] not in RevolutBusinessAccountName:
        raise ValueError(
            f"Invalid Revolut business account name {account_name}. "
            f"Valid values are: {', '.join(RevolutBusinessAccountName.get_values())}"
        )

    revolut_config = _business_account_name_mapping[account_name]

    if not revolut_config:
        raise ValueError(
            f"Could not find Revolut config for business account name {account_name}"
        )

    return revolut_config

is_transaction_on_accounts

is_transaction_on_accounts(transaction, account_ids)

Check if a Revolut transaction is linked to any of the given account IDs

Source code in shared/services/payment_providers/revolut/helpers.py
def is_transaction_on_accounts(
    transaction: RevolutTransaction, account_ids: set[str]
) -> bool:
    """
    Check if a Revolut transaction is linked to any of the given account IDs
    """
    transaction_account_ids = [str(leg.account_id) for leg in transaction.legs]

    return any(account_id in transaction_account_ids for account_id in account_ids)

shared.services.payment_providers.revolut.openapi

business_1_0

RevolutAccount

Bases: BaseModel

balance instance-attribute
balance

The current balance on the account.

created_at instance-attribute
created_at

The date and time the account was created in ISO 8601 ⧉ format.

currency instance-attribute
currency
id instance-attribute
id

The account ID.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
name class-attribute instance-attribute
name = None

The account name.

public instance-attribute
public

Indicates whether the account is visible to other businesses on Revolut.

state instance-attribute
state

Indicates the state of the account.

updated_at instance-attribute
updated_at

The date and time the account was last updated in ISO 8601 ⧉ format.

RevolutAccountBankDetailsItem

Bases: BaseModel

account_no class-attribute instance-attribute
account_no = None

The account number.

bank_country class-attribute instance-attribute
bank_country = None
beneficiary instance-attribute
beneficiary

The name of the counterparty.

beneficiary_address instance-attribute
beneficiary_address
bic class-attribute instance-attribute
bic = None

The BIC number, also known as SWIFT code.

estimated_time instance-attribute
estimated_time
iban class-attribute instance-attribute
iban = None

The IBAN number.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
pooled class-attribute instance-attribute
pooled = None

Indicates whether the account address is pooled or unique.

routing_number class-attribute instance-attribute
routing_number = None

The routing number of the account.

schemes instance-attribute
schemes

The schemes that are available for this currency account.

sort_code class-attribute instance-attribute
sort_code = None

The sort code of the account.

unique_reference class-attribute instance-attribute
unique_reference = None

The reference of the pooled account.

RevolutAccountBankDetailsItems

Bases: RootModel[list[RevolutAccountBankDetailsItem]]

root instance-attribute
root

Indicates the payment scheme used to execute transactions.

RevolutAccountCurrency

Bases: RootModel[str]

root instance-attribute
root

ISO 4217 ⧉ currency code in upper case.

RevolutAccountNameValidationReasonAU

Bases: BaseModel

code class-attribute instance-attribute
code = None

The reason code. Possible values for AU: - close_match (business accounts): The provided name is similar to the account name. The actual name is returned. Mismatched account type is corrected. - not_matched: The account details don't match the provided values. - account_does_not_exist: The account does not exist. - account_switched: The account has been switched using the Current Account Switching Service. Please contact the recipient for updated account details. - cannot_be_checked: The account cannot be checked.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
type class-attribute instance-attribute
type = None

The reason type. Determines the service ⧉ used for the validation that returned the reason code. For AU, the value is: au_cop.

RevolutAccountNameValidationReasonEUR

Bases: BaseModel

code class-attribute instance-attribute
code = None

The reason code. Possible values for EUR: - close_match: The provided name is similar to the account name. The actual name is returned. - not_matched: The account details don't match the provided values. - account_does_not_exist: The account does not exist. - account_switched: The account has been switched using the Current Account Switching Service. Please contact the recipient for updated account details. - cannot_be_checked: The account cannot be checked.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
type class-attribute instance-attribute
type = None

The reason type. Determines the service ⧉ used for the validation that returned the reason code. For EUR, the value is: eu_cop.

RevolutAccountNameValidationReasonRO

Bases: BaseModel

code class-attribute instance-attribute
code = None

The reason code. Possible values for RO: - cannot_be_checked: The account cannot be checked.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
type class-attribute instance-attribute
type = None

The reason type. Determines the service ⧉ used for the validation that returned the reason code. For RO, the value is: ro_cop.

RevolutAccountNameValidationReasonUK

Bases: BaseModel

code class-attribute instance-attribute
code = None

The reason code. Possible values for UK: - close_match: The provided name is similar to the account name, the account type is correct. The actual name is returned. - individual_account_name_matched: The names match but the counterparty is an individual, not a business. - company_account_name_matched: The names match but the counterparty is a business, not an individual. - individual_account_close_match: The provided name is similar to the account name, and the account type is incorrect – the counterparty is an individual, not a business. The actual name is returned. - company_account_close_match: The provided name is similar to the account name, and the account type is incorrect - the counterparty is a business, not an individual. The actual name is returned. - not_matched: The account details don't match the provided values. - account_does_not_exist: The account does not exist. - account_switched: The account has been switched using the Current Account Switching Service. Please contact the recipient for updated account details. - cannot_be_checked: The account cannot be checked.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
type class-attribute instance-attribute
type = None

The reason type. Determines the service ⧉ used for the validation that returned the reason code. For UK, the values is: uk_cop.

RevolutAccounts

Bases: RootModel[list[RevolutAccount]]

root instance-attribute
root

RevolutAmount

Bases: RootModel[float]

root instance-attribute
root

The amount of money.

RevolutAmount1

Bases: BaseModel

amount class-attribute instance-attribute
amount = None
currency class-attribute instance-attribute
currency = None
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutAmountWithCurrency

Bases: BaseModel

amount class-attribute instance-attribute
amount = None

The amount of the transaction.

currency class-attribute instance-attribute
currency = None
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutBankCountryCode

Bases: RootModel[str]

root instance-attribute
root

The country of the bank, provided as a 2-letter ISO 3166 ⧉ code.

RevolutBeneficiaryAddress

Bases: BaseModel

city class-attribute instance-attribute
city = None

The name of the city.

country instance-attribute
country

The country of the counterparty, provided as a 2-letter ISO 3166 ⧉ code.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
postcode instance-attribute
postcode

The postcode of the counterparty address.

region class-attribute instance-attribute
region = None

The name of the region (state or province), for example, Ontario for Canada.

street_line1 class-attribute instance-attribute
street_line1 = None

Street line 1 information.

street_line2 class-attribute instance-attribute
street_line2 = None

Street line 2 information.

RevolutBusinessMerchantCategory

Bases: StrEnum

accommodation class-attribute instance-attribute
accommodation = 'accommodation'
advertising class-attribute instance-attribute
advertising = 'advertising'
airlines class-attribute instance-attribute
airlines = 'airlines'
cash class-attribute instance-attribute
cash = 'cash'
education class-attribute instance-attribute
education = 'education'
entertainment class-attribute instance-attribute
entertainment = 'entertainment'
financial class-attribute instance-attribute
financial = 'financial'
fuel class-attribute instance-attribute
fuel = 'fuel'
furniture class-attribute instance-attribute
furniture = 'furniture'
general class-attribute instance-attribute
general = 'general'
government class-attribute instance-attribute
government = 'government'
groceries class-attribute instance-attribute
groceries = 'groceries'
hardware class-attribute instance-attribute
hardware = 'hardware'
health class-attribute instance-attribute
health = 'health'
restaurants class-attribute instance-attribute
restaurants = 'restaurants'
services class-attribute instance-attribute
services = 'services'
shopping class-attribute instance-attribute
shopping = 'shopping'
software class-attribute instance-attribute
software = 'software'
transport class-attribute instance-attribute
transport = 'transport'
utilities class-attribute instance-attribute
utilities = 'utilities'

RevolutCancellationReason

Bases: Enum

too_many_name_check_attempts class-attribute instance-attribute
too_many_name_check_attempts = (
    "too_many_name_check_attempts"
)

RevolutCardCanBeUnlocked

Bases: RootModel[bool]

root instance-attribute
root

Returned for locked cards (state=locked). Indicates whether the card can be unlocked ⧉ manually (via API or in-app). If true, you'll still need the necessary scope or permission ⧉ to unlock the card.

Info

Cards can be locked for various reasons. For example, a card can be locked by the user, due to spending period settings, or automatically by the system. Only certain types of lock can be lifted manually.

RevolutCardContact

Bases: RootModel[UUID]

root instance-attribute
root

One of the card's contacts.

RevolutCardContacts

Bases: RootModel[list[RevolutCardContact]]

root instance-attribute
root

The list of contacts for a company card ⧉.

RevolutCardCreatedResponse

Bases: BaseModel

accounts instance-attribute
accounts

The list of linked accounts.

can_be_unlocked class-attribute instance-attribute
can_be_unlocked = None
categories class-attribute instance-attribute
categories = None

The list of merchant categories that are available for card spending. If this parameter is not specified, categories are not restricted.

contact_ids class-attribute instance-attribute
contact_ids = None
countries class-attribute instance-attribute
countries = None

The list of countries where the card can be used, specified as 2-letter ISO 3166 ⧉ codes.

created_at instance-attribute
created_at

The date and time the card was created in ISO 8601 ⧉ format.

expiry instance-attribute
expiry

The card expiration date.

holder_id class-attribute instance-attribute
holder_id = None

The ID of the team member who is the holder of the card. If the card belongs to the business, this will be empty.

For more information, see the guides: Manage Cards - Create a virtual card ⧉.

id instance-attribute
id

The ID of the card.

label class-attribute instance-attribute
label = None

The label of the card.

last_digits instance-attribute
last_digits

The last 4 digits of the card's PAN.

merchant_controls class-attribute instance-attribute
merchant_controls = None

The merchant-level controls for card spending.

They block or allow the card to only transact with specific merchants: - allow: permits only the specified merchants (cannot be used if the categories parameter is set) - block: blocks the specified merchants (can be used with or without categories)

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
product class-attribute instance-attribute
product = None
references class-attribute instance-attribute
references = None
spending_limits class-attribute instance-attribute
spending_limits = None
spending_period class-attribute instance-attribute
spending_period = None

The controls for the card's spending period.

They specify the dates when the card becomes available or unavailable for spending, and define what happens after the end date.

state instance-attribute
state

The state that the card is in.

updated_at instance-attribute
updated_at

The date and time the card was last updated in ISO 8601 ⧉ format.

virtual instance-attribute
virtual

Specifies whether the card is virtual (true) or physical (false).

RevolutCardInvitationAccounts

Bases: RootModel[list[UUID]]

root instance-attribute
root

The list of accounts that will be linked to the card.

RevolutCardInvitationCardId

Bases: RootModel[UUID]

root instance-attribute
root

The ID of the card issued after this invitation was claimed.

Note

Only returned for invitations in state redeemed.

RevolutCardInvitationCategories

Bases: RootModel[list[RevolutBusinessMerchantCategory]]

root instance-attribute
root

The list of merchant categories that will be available for card spending. If this parameter is not specified, categories are not restricted.

RevolutCardInvitationCountries

Bases: RootModel[list[RevolutCardInvitationCountry]]

root instance-attribute
root

The list of countries where the team member will be able to use the card. Specified as 2-letter ISO 3166 ⧉ codes.

RevolutCardInvitationCountry

Bases: RootModel[str]

root instance-attribute
root

RevolutCardInvitationCreatedAt

Bases: RootModel[AwareDatetime]

root instance-attribute
root

The date and time the card invitation was created in ISO 8601 ⧉ format.

RevolutCardInvitationCreatedHolderId

Bases: RootModel[UUID]

root instance-attribute
root

The ID of the team member to be assigned as the holder of the card after the invitation is claimed.

RevolutCardInvitationCreatedResponse

Bases: BaseModel

accounts instance-attribute
accounts
categories class-attribute instance-attribute
categories = None
countries class-attribute instance-attribute
countries = None
created_at instance-attribute
created_at
expiry_date class-attribute instance-attribute
expiry_date = None
holder_id class-attribute instance-attribute
holder_id = None
id instance-attribute
id
label class-attribute instance-attribute
label = None
merchant_controls class-attribute instance-attribute
merchant_controls = None
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
spending_limits class-attribute instance-attribute
spending_limits = None
spending_period class-attribute instance-attribute
spending_period = None
state instance-attribute
state
updated_at instance-attribute
updated_at
virtual instance-attribute
virtual

RevolutCardInvitationExpiryDate

Bases: RootModel[AwareDatetime]

root instance-attribute
root

The date and time after which this card invitation expires if not claimed or cancelled before then. Specified in ISO 8601 ⧉ format.

Note

Only returned for invitations in state created.

Tip

For other states, to find out when a card invitation transitioned to its final state ⧉, check the updated_at value.

RevolutCardInvitationHolderId

Bases: RootModel[UUID]

root instance-attribute
root

The ID of the team member to be assigned as the holder of the card after the invitation is claimed.

Note

If the team member has been deleted since the invitation was created, the holder_id is not returned.

RevolutCardInvitationId

Bases: RootModel[UUID]

root instance-attribute
root

The ID of the card invitation.

RevolutCardInvitationLabel

Bases: RootModel[str]

root instance-attribute
root

The label of the card.

RevolutCardInvitationMerchantControls

Bases: BaseModel

control_type instance-attribute
control_type

The type of control to apply.

merchant_ids instance-attribute
merchant_ids

The list of IDs of merchants to which the control applies.

Tip

To find merchant IDs, check transaction details (→ merchant.id). You can fetch transaction details for a specific transaction ⧉ or for all transactions ⧉.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutCardInvitationResponse

Bases: BaseModel

accounts instance-attribute
accounts
card_id class-attribute instance-attribute
card_id = None
categories class-attribute instance-attribute
categories = None
countries class-attribute instance-attribute
countries = None
created_at instance-attribute
created_at
expiry_date class-attribute instance-attribute
expiry_date = None
holder_id class-attribute instance-attribute
holder_id = None
id instance-attribute
id
label class-attribute instance-attribute
label = None
merchant_controls class-attribute instance-attribute
merchant_controls = None
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
spend_program class-attribute instance-attribute
spend_program = None
spending_limits class-attribute instance-attribute
spending_limits = None
spending_period class-attribute instance-attribute
spending_period = None
state instance-attribute
state
updated_at instance-attribute
updated_at
virtual instance-attribute
virtual

RevolutCardInvitationSpendProgram

Bases: RootModel[RevolutSpendProgram]

root instance-attribute
root

RevolutCardInvitationSpendingLimits

Bases: RootModel[RevolutSpendingLimits]

root instance-attribute
root

RevolutCardInvitationSpendingPeriod

Bases: RootModel[RevolutCardInvitationSpendingPeriod4 | RevolutCardInvitationSpendingPeriod5]

root instance-attribute
root

The controls for the card's spending period.

They specify the dates when the card will become available or unavailable for spending, and define what happens after the end date.

RevolutCardInvitationSpendingPeriod1

Bases: BaseModel

end_date class-attribute instance-attribute
end_date = None

The end date (inclusive) of the spending period, in ISO 8601 ⧉ format (YYYY-MM-DD). Uses the timezone set by the business ⧉, or defaults to Europe/London.

end_date_action class-attribute instance-attribute
end_date_action = None

The action to take after the end date of the spending period.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
start_date instance-attribute
start_date

The start date (inclusive) of the spending period, in ISO 8601 ⧉ format (YYYY-MM-DD). Uses the timezone set by the business ⧉, or defaults to Europe/London.

RevolutCardInvitationSpendingPeriod2

Bases: BaseModel

end_date instance-attribute
end_date

The end date (inclusive) of the spending period, in ISO 8601 ⧉ format (YYYY-MM-DD). Uses the timezone set by the business ⧉, or defaults to Europe/London.

end_date_action instance-attribute
end_date_action

The action to take after the end date of the spending period.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
start_date class-attribute instance-attribute
start_date = None

The start date (inclusive) of the spending period, in ISO 8601 ⧉ format (YYYY-MM-DD). Uses the timezone set by the business ⧉, or defaults to Europe/London.

RevolutCardInvitationSpendingPeriod3

Bases: BaseModel

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutCardInvitationSpendingPeriod4

Bases: RevolutCardInvitationSpendingPeriod1, RevolutCardInvitationSpendingPeriod3

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutCardInvitationSpendingPeriod5

Bases: RevolutCardInvitationSpendingPeriod2, RevolutCardInvitationSpendingPeriod3

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutCardInvitationState

Bases: StrEnum

created class-attribute instance-attribute
created = 'created'
expired class-attribute instance-attribute
expired = 'expired'
failed class-attribute instance-attribute
failed = 'failed'
redeemed class-attribute instance-attribute
redeemed = 'redeemed'

RevolutCardInvitationUpdatedAt

Bases: RootModel[AwareDatetime]

root instance-attribute
root

The date and time the card invitation was last updated in ISO 8601 ⧉ format.

RevolutCardInvitationUpdatedResponse

Bases: BaseModel

accounts instance-attribute
accounts
categories class-attribute instance-attribute
categories = None
countries class-attribute instance-attribute
countries = None
created_at instance-attribute
created_at
expiry_date class-attribute instance-attribute
expiry_date = None
holder_id class-attribute instance-attribute
holder_id = None
id instance-attribute
id
label class-attribute instance-attribute
label = None
merchant_controls class-attribute instance-attribute
merchant_controls = None
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
spend_program class-attribute instance-attribute
spend_program = None
spending_limits class-attribute instance-attribute
spending_limits = None
spending_period class-attribute instance-attribute
spending_period = None
state instance-attribute
state
updated_at instance-attribute
updated_at
virtual instance-attribute
virtual

RevolutCardInvitationVirtual

Bases: RootModel[bool]

root instance-attribute
root

Specifies whether the issued card will be a virtual (true) or physical (false) one.

RevolutCardInvitationsResponse

Bases: RootModel[list[RevolutCardInvitationResponse]]

root instance-attribute
root

A list of card invitations

RevolutCardProduct

Bases: BaseModel

code instance-attribute
code

The code of the card product.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutCardReference

Bases: BaseModel

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
name instance-attribute
name

The name of the card reference. Must be unique.

value instance-attribute
value

The value for this reference.

RevolutCardReferences

Bases: RootModel[list[RevolutCardReference]]

root instance-attribute
root

References for the card. Up to 5 name-value pairs assigned to the card for tracking.

Info

Each time the card is used, the references are recorded in the transaction details ⧉ (card.references), helping track transactions made with this card.

The names must be unique. The references can be amended ⧉ up to 10 times.

References are only supported for cards owned by the business (i.e. company ⧉ or auto-issued cards ⧉). They are not supported for team member cards ⧉ (i.e. with holder_id present).

Note

The references recorded on a transaction are those assigned to the card at the time the transaction took place. If the references are amended, they will only be applied to future transactions. Existing transaction are not affected.

RevolutCardResponse

Bases: BaseModel

accounts instance-attribute
accounts

The list of linked accounts.

can_be_unlocked class-attribute instance-attribute
can_be_unlocked = None
categories class-attribute instance-attribute
categories = None

The list of merchant categories that are available for card spending. If not specified, categories are not restricted.

contact_ids class-attribute instance-attribute
contact_ids = None
countries class-attribute instance-attribute
countries = None

The list of countries where the card can be used, specified as 2-letter ISO 3166 ⧉ codes.

created_at instance-attribute
created_at

The date and time the card was created in ISO 8601 ⧉ format.

expiry instance-attribute
expiry

The card expiration date.

holder_id class-attribute instance-attribute
holder_id = None

The ID of the team member who is the holder of the card. If the card belongs to the business, this will be empty.

For more information, see the guides: Manage Cards - Create a virtual card ⧉.

id instance-attribute
id

The ID of the card.

label class-attribute instance-attribute
label = None

The label of the card.

last_digits instance-attribute
last_digits

The last 4 digits of the card's PAN.

merchant_controls class-attribute instance-attribute
merchant_controls = None

The merchant-level controls for card spending.

They block or allow the card to only transact with specific merchants: - allow: permits only the specified merchants (cannot be used if the categories parameter is set) - block: blocks the specified merchants (can be used with or without categories)

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
product class-attribute instance-attribute
product = None
references class-attribute instance-attribute
references = None
spend_program class-attribute instance-attribute
spend_program = None
spending_limits class-attribute instance-attribute
spending_limits = None
spending_period class-attribute instance-attribute
spending_period = None

The controls for the card's spending period.

They specify the dates when the card becomes available or unavailable for spending, and define what happens after the end date.

state instance-attribute
state
updated_at instance-attribute
updated_at

The date and time the card was last updated in ISO 8601 ⧉ format.

virtual instance-attribute
virtual

Specifies whether the card is virtual (true) or physical (false).

RevolutCardState

Bases: StrEnum

active class-attribute instance-attribute
active = 'active'
created class-attribute instance-attribute
created = 'created'
frozen class-attribute instance-attribute
frozen = 'frozen'
locked class-attribute instance-attribute
locked = 'locked'
pending class-attribute instance-attribute
pending = 'pending'

RevolutCardsResponse

Bases: RootModel[list[RevolutCardResponse]]

root instance-attribute
root

A list of cards.

RevolutCategory

Bases: BaseModel

code class-attribute instance-attribute
code = None

The code of the category.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
name instance-attribute
name

The name of the category.

RevolutChargeBearer

Bases: StrEnum

debtor class-attribute instance-attribute
debtor = 'debtor'
shared class-attribute instance-attribute
shared = 'shared'

RevolutCode

Bases: StrEnum

account_does_not_exist class-attribute instance-attribute
account_does_not_exist = 'account_does_not_exist'
account_switched class-attribute instance-attribute
account_switched = 'account_switched'
cannot_be_checked class-attribute instance-attribute
cannot_be_checked = 'cannot_be_checked'
close_match class-attribute instance-attribute
close_match = 'close_match'
company_account_close_match class-attribute instance-attribute
company_account_close_match = 'company_account_close_match'
company_account_name_matched class-attribute instance-attribute
company_account_name_matched = (
    "company_account_name_matched"
)
individual_account_close_match class-attribute instance-attribute
individual_account_close_match = (
    "individual_account_close_match"
)
individual_account_name_matched class-attribute instance-attribute
individual_account_name_matched = (
    "individual_account_name_matched"
)
not_matched class-attribute instance-attribute
not_matched = 'not_matched'

RevolutCode4

Bases: StrEnum

business_expense_and_claims class-attribute instance-attribute
business_expense_and_claims = 'business_expense_and_claims'
fees_and_charges class-attribute instance-attribute
fees_and_charges = 'fees_and_charges'
fund_transfer_and_intracompany_payment class-attribute instance-attribute
fund_transfer_and_intracompany_payment = (
    "fund_transfer_and_intracompany_payment"
)
gifts_and_donations class-attribute instance-attribute
gifts_and_donations = 'gifts_and_donations'
government_services_and_tax class-attribute instance-attribute
government_services_and_tax = 'government_services_and_tax'
insurance class-attribute instance-attribute
insurance = 'insurance'
inventory class-attribute instance-attribute
inventory = 'inventory'
investment_dividend_and_interest class-attribute instance-attribute
investment_dividend_and_interest = (
    "investment_dividend_and_interest"
)
loan_and_loan_repayment class-attribute instance-attribute
loan_and_loan_repayment = 'loan_and_loan_repayment'
marketing class-attribute instance-attribute
marketing = 'marketing'
payment_for_goods_and_services class-attribute instance-attribute
payment_for_goods_and_services = (
    "payment_for_goods_and_services"
)
payroll class-attribute instance-attribute
payroll = 'payroll'
refund class-attribute instance-attribute
refund = 'refund'
rental_and_property class-attribute instance-attribute
rental_and_property = 'rental_and_property'
sales class-attribute instance-attribute
sales = 'sales'
service_provider_and_software class-attribute instance-attribute
service_provider_and_software = (
    "service_provider_and_software"
)
travel_and_transportation class-attribute instance-attribute
travel_and_transportation = 'travel_and_transportation'
utilities class-attribute instance-attribute
utilities = 'utilities'

RevolutCode5

Bases: StrEnum

advertising class-attribute instance-attribute
advertising = 'advertising'
advisor_fees class-attribute instance-attribute
advisor_fees = 'advisor_fees'
business_insurance class-attribute instance-attribute
business_insurance = 'business_insurance'
construction class-attribute instance-attribute
construction = 'construction'
delivery class-attribute instance-attribute
delivery = 'delivery'
education class-attribute instance-attribute
education = 'education'
exports class-attribute instance-attribute
exports = 'exports'
family class-attribute instance-attribute
family = 'family'
fund_investment class-attribute instance-attribute
fund_investment = 'fund_investment'
goods class-attribute instance-attribute
goods = 'goods'
homesend class-attribute instance-attribute
homesend = 'homesend'
hotel class-attribute instance-attribute
hotel = 'hotel'
insurance_claims class-attribute instance-attribute
insurance_claims = 'insurance_claims'
insurance_premium class-attribute instance-attribute
insurance_premium = 'insurance_premium'
loan_repayment class-attribute instance-attribute
loan_repayment = 'loan_repayment'
medical class-attribute instance-attribute
medical = 'medical'
office class-attribute instance-attribute
office = 'office'
other_fees class-attribute instance-attribute
other_fees = 'other_fees'
personal_transfer class-attribute instance-attribute
personal_transfer = 'personal_transfer'
property_purchase class-attribute instance-attribute
property_purchase = 'property_purchase'
property_rental class-attribute instance-attribute
property_rental = 'property_rental'
royalties class-attribute instance-attribute
royalties = 'royalties'
services class-attribute instance-attribute
services = 'services'
share_investment class-attribute instance-attribute
share_investment = 'share_investment'
tax class-attribute instance-attribute
tax = 'tax'
transfer class-attribute instance-attribute
transfer = 'transfer'
transportation class-attribute instance-attribute
transportation = 'transportation'
travel class-attribute instance-attribute
travel = 'travel'
utilities class-attribute instance-attribute
utilities = 'utilities'

RevolutControlType

Bases: StrEnum

allow class-attribute instance-attribute
allow = 'allow'
block class-attribute instance-attribute
block = 'block'

RevolutCounterparties

Bases: RootModel[list[RevolutCounterparty]]

root instance-attribute
root

RevolutCounterparty

Bases: BaseModel

accounts class-attribute instance-attribute
accounts = None

The list of public accounts associated with this counterparty.

cards class-attribute instance-attribute
cards = None

The list of cards associated with this counterparty.

country class-attribute instance-attribute
country = None
created_at instance-attribute
created_at

The date and time the counterparty was created in ISO 8601 ⧉ format.

id instance-attribute
id

The ID of the counterparty.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
name instance-attribute
name

The name of the counterparty.

profile_type class-attribute instance-attribute
profile_type = None
revtag class-attribute instance-attribute
revtag = None

The Revtag ⧉ of the counterparty.

state instance-attribute
state

Indicates the state of the counterparty.

updated_at instance-attribute
updated_at

The date and time the counterparty was last updated in ISO 8601 ⧉ format.

RevolutCounterpartyAccount

Bases: BaseModel

account_no class-attribute instance-attribute
account_no = None

The bank account number of the counterparty.

bank_country class-attribute instance-attribute
bank_country = None
bic class-attribute instance-attribute
bic = None

The BIC number of the counterparty's account if applicable.

bsb_code class-attribute instance-attribute
bsb_code = None

The BSB number of the counterparty's account if applicable.

clabe class-attribute instance-attribute
clabe = None

The CLABE number of the counterparty's account if applicable.

currency instance-attribute
currency
iban class-attribute instance-attribute
iban = None

The IBAN number of the counterparty's account if applicable.

id instance-attribute
id

The ID of the counterparty's account.

ifsc class-attribute instance-attribute
ifsc = None

The IFSC number of the counterparty's account if applicable.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
name class-attribute instance-attribute
name = None

The name of the counterparty.

recipient_charges class-attribute instance-attribute
recipient_charges = None

Indicates the possibility of the recipient charges.

Caution

This field is deprecated and should be disregarded. It is returned for legacy purposes only.

routing_number class-attribute instance-attribute
routing_number = None

The routing number of the counterparty's account if applicable.

sort_code class-attribute instance-attribute
sort_code = None

The sort code of the counterparty's account if applicable.

type instance-attribute
type

Indicates the type of account.

RevolutCounterpartyCard

Bases: BaseModel

country instance-attribute
country
currency instance-attribute
currency
id instance-attribute
id

The ID of the counterparty's card.

last_digits instance-attribute
last_digits

The last four digits of the card number.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
name instance-attribute
name

The name of the counterparty.

scheme instance-attribute
scheme

The card brand.

RevolutCounterpartyError

Bases: BaseModel

code instance-attribute
code

The error code.

message instance-attribute
message

The description of the error.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
params class-attribute instance-attribute
params = None

RevolutCounterpartyErrorParams

Bases: BaseModel

counterparty_id class-attribute instance-attribute
counterparty_id = None

The ID of the Revolut counterparty (i.e. internal counterparty) that already exists.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutCountry

Bases: RootModel[str]

root instance-attribute
root

RevolutCountryCode

Bases: RootModel[str]

root instance-attribute
root

The counterparty's bank country, provided as a 2-letter ISO 3166 ⧉ code.

RevolutCountryCodeCard

Bases: RootModel[str]

root instance-attribute
root

The country of the card issuer, provided as a 2-letter ISO 3166 ⧉ code.

RevolutCountryCodeMerchant

Bases: RootModel[str]

root instance-attribute
root

The bank country of the merchant, provided as an ISO 3166 ⧉ code. Typically 3 letters, though in some cases, card networks might provide it as a 2-letter code.

RevolutCreateCounterpartyRequest

Bases: BaseModel

account_no class-attribute instance-attribute
account_no = None

The bank account number of the counterparty.

address class-attribute instance-attribute
address = None
bank_country class-attribute instance-attribute
bank_country = None
bic class-attribute instance-attribute
bic = None

The BIC number of the counterparty's account. This field is required for non-SEPA IBAN/SWIFT.

bsb_code class-attribute instance-attribute
bsb_code = None

The BSB number of the counterparty's account. This field is required for AUD accounts.

clabe class-attribute instance-attribute
clabe = None

The CLABE number of the counterparty's account. This field is required for SWIFT MX.

company_name class-attribute instance-attribute
company_name = None

The name of the counterparty, provided when the counterparty is a company (business account type) and is not being added via Revtag.

If specified, individual_name and name must be empty.

Caution

The company_name must contain at least 2 letters (not just characters). For example, names like 12 will fail validation because they are two characters but not two letters.

currency class-attribute instance-attribute
currency = None
iban class-attribute instance-attribute
iban = None

The IBAN number of the counterparty's account. This field is displayed for IBAN countries.

ifsc class-attribute instance-attribute
ifsc = None

The IFSC number of the counterparty's account. This field is required for INR accounts.

individual_name class-attribute instance-attribute
individual_name = None
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
name class-attribute instance-attribute
name = None

The name of the counterparty, provided when the counterparty is being added via Revtag.

If specified, individual_name and company_name must be empty.

Note

The name that you provide must match or closely match the actual name associated with the account that you're trying to add. Otherwise, the creation fails and a 404 error is returned.

profile_type class-attribute instance-attribute
profile_type = None

The type of the Revolut profile. Indicates whether the counterparty is a personal or business account. Provide it when adding a Revolut counterparty via Revtag.

revtag class-attribute instance-attribute
revtag = None

The Revtag ⧉ of the counterparty to add.

routing_number class-attribute instance-attribute
routing_number = None

The routing number of the counterparty's account. This field is required for USD accounts.

sort_code class-attribute instance-attribute
sort_code = None

The sort code of the counterparty's account. This field is required for GBP accounts.

RevolutCreatePaymentDraftRequest

Bases: BaseModel

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
payments instance-attribute
payments

The details of the payment(s) to be made.

schedule_for class-attribute instance-attribute
schedule_for = None

The scheduled date of the payment draft in ISO 8601 ⧉ format.

title class-attribute instance-attribute
title = None

The title of the payment draft.

RevolutCreatePaymentDraftResponse

Bases: BaseModel

id instance-attribute
id

The ID of the payment draft created.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutCreatePayoutLinkRequest

Bases: BaseModel

account_id instance-attribute
account_id
amount instance-attribute
amount
counterparty_name instance-attribute
counterparty_name
currency instance-attribute
currency
expiry_period class-attribute instance-attribute
expiry_period = None
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
payout_methods instance-attribute
payout_methods
reference instance-attribute
reference

The reference for the payout link. A piece of text or number you provide to help identify what the payment relates to. It can be used, for example, for reconciliation or tracking purposes.

You might include an invoice number, account or transaction ID, or any other reference meaningful to you or the recipient.

request_id instance-attribute
request_id

The ID of the request, provided by the sender.

Caution

To ensure that a link payment is not processed multiple times if there are network or system errors, the same request_id should be used for requests related to the same link.

save_counterparty class-attribute instance-attribute
save_counterparty = False

Indicates whether to save the recipient as your counterparty upon link claim. If false then the counterparty will not show up on your counterparties list, for example, when you retrieve your counterparties. However, you will still be able to retrieve this counterparty by its ID.

If you don't choose to save the counterparty on link creation, you can do it later from your transactions list in the Business app.

transfer_reason_code class-attribute instance-attribute
transfer_reason_code = None

RevolutCreateWebhookRequest

Bases: BaseModel

events class-attribute instance-attribute
events = [TransactionCreated, TransactionStateChanged]

A list of event types to subscribe to. If you don't provide it, you're automatically subscribed to the default event types ⧉.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
url instance-attribute
url

A valid webhook URL to which to send event notifications. The supported protocol is https.

RevolutCurrency

Bases: RootModel[str]

root instance-attribute
root

ISO 4217 ⧉ currency code in upper case.

RevolutCurrentChargeOptions

Bases: BaseModel

fee class-attribute instance-attribute
fee = None
from_ instance-attribute
from_
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
rate class-attribute instance-attribute
rate = None
to instance-attribute
to

RevolutEndDateAction

Bases: StrEnum

lock class-attribute instance-attribute
lock = 'lock'
terminate class-attribute instance-attribute
terminate = 'terminate'

RevolutError

Bases: BaseModel

code instance-attribute
code

The error code.

message instance-attribute
message

The description of the error.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutErrorUnauthorized

Bases: RevolutErrorWithStatus

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutErrorUnprocessableEntity

Bases: BaseModel

code class-attribute instance-attribute
code = None

The error code.

message instance-attribute
message

The description of the error.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutErrorWithStatus

Bases: BaseModel

message instance-attribute
message

The description of the error.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
status instance-attribute
status

The error code.

RevolutEstimatedTime

Bases: BaseModel

max class-attribute instance-attribute
max = None

The maximum time estimate.

min class-attribute instance-attribute
min = None

The minimum time estimate.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
unit instance-attribute
unit

The estimated time unit of the inbound transfer of the funds.

RevolutExchangePartFrom

Bases: BaseModel

account_id instance-attribute
account_id

The ID of the account to sell currency from.

amount class-attribute instance-attribute
amount = None

The amount of currency. Specify only if you want to sell currency.

currency instance-attribute
currency
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutExchangePartTo

Bases: BaseModel

account_id instance-attribute
account_id

The ID of the account to receive exchanged currency into.

amount class-attribute instance-attribute
amount = None

The amount of currency. Specify only if you want to buy currency.

currency instance-attribute
currency
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutExchangeRateResponse

Bases: BaseModel

fee instance-attribute
fee

The expected fee for the transaction.

from_ instance-attribute
from_

The money to sell.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
rate instance-attribute
rate

The proposed exchange rate.

rate_date instance-attribute
rate_date

The date of the proposed exchange rate in ISO 8601 ⧉ format.

to instance-attribute
to

The money to receive.

RevolutExchangeReason

Bases: BaseModel

code instance-attribute
code

Category code of the reason for the exchange.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
name instance-attribute
name

Category name of the reason for the exchange.

RevolutExchangeReasonCode

Bases: RootModel[str]

root instance-attribute
root

The reason code for the exchange. Depending on the country and the amount of funds to be exchanged, you might be required to provide an exchange reason. You can check available reason codes with the GET /exchange-reasons operation ⧉.

If an exchange reason is not required, this field is ignored.

RevolutExchangeReasons

Bases: RootModel[list[RevolutExchangeReason]]

root instance-attribute
root

The list of available exchange reasons.

RevolutExchangeRequest

Bases: BaseModel

exchange_reason_code class-attribute instance-attribute
exchange_reason_code = None
from_ instance-attribute
from_
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
reference class-attribute instance-attribute
reference = None

The reference for the exchange transaction, provided by you. It helps you to identify the transaction if you want to look it up later.

request_id instance-attribute
request_id

The ID of the request, provided by you. It helps you identify the transaction in your system.

Caution

To ensure that an exchange transaction is not processed multiple times if there are network or system errors, the same request_id should be used for requests related to the same transaction.

to instance-attribute
to

RevolutExchangeResponse

Bases: BaseModel

completed_at class-attribute instance-attribute
completed_at = None

The date and time the transaction was completed in ISO 8601 ⧉ format.

created_at class-attribute instance-attribute
created_at = None

The date and time the transaction was created in ISO 8601 ⧉ format.

id class-attribute instance-attribute
id = None

The ID of the created transaction.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
reason_code class-attribute instance-attribute
reason_code = None

The reason why the transaction was failed or declined.

Present only when the state parameter of the transaction is declined or failed.

state class-attribute instance-attribute
state = None
type class-attribute instance-attribute
type = None

The type of the transaction. For money exchange, it is exchange.

RevolutExpense

Bases: BaseModel

completed_at class-attribute instance-attribute
completed_at = None

The date and time the expense was completed in ISO 8601 ⧉ format.

description class-attribute instance-attribute
description = None

The description of the expense.

expense_date instance-attribute
expense_date

The expense data depends on the type of the expense and whether it has been completed.

Typically, it's the date and time of the expense transaction ⧉ in ISO 8601 ⧉ format. - If the transaction related to the expense is completed, it is the date and time of its completion (completed_at ⧉). - Otherwise, it is the date and time of its creation (created_at ⧉).

For reimbursements, it's the payment date provided in the reimbursement request.

id instance-attribute
id

The ID of the expense.

labels instance-attribute
labels

The labels added to the expense, organised in groups.

You can have up to 5 label groups, with max. 1 label per group.

The labels are provided as an object, where each key is the name of a label group, and each value is a single-element array containing the selected label from that group.

merchant class-attribute instance-attribute
merchant = None

The name of the merchant.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
payer class-attribute instance-attribute
payer = None

The name of the team member ⧉ who made the transaction, refund request, or ATM withdrawal, or the name of the business if the related transaction is of type fee.

receipt_ids instance-attribute
receipt_ids

The IDs of the receipts related to the expense.

spent_amount instance-attribute
spent_amount

The expense amount in billed currency.

splits instance-attribute
splits

The splits of the expense.

A single expense can be divided into multiple parts (splits), for example, to allocate different portions of the expense to different categories.

state instance-attribute
state
submitted_at class-attribute instance-attribute
submitted_at = None

The date and time the expense was submitted in ISO 8601 ⧉ format.

transaction_id class-attribute instance-attribute
transaction_id = None

The ID of the transaction ⧉ related to the expense. Not available for transactions of type external.

transaction_type instance-attribute
transaction_type

RevolutExpenseSplit

Bases: BaseModel

amount instance-attribute
amount

The original amount of the expense split.

category instance-attribute
category
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
tax_rate instance-attribute
tax_rate

RevolutExpenseState

Bases: StrEnum

approved class-attribute instance-attribute
approved = 'approved'
awaiting_review class-attribute instance-attribute
awaiting_review = 'awaiting_review'
missing_info class-attribute instance-attribute
missing_info = 'missing_info'
pending_reimbursement class-attribute instance-attribute
pending_reimbursement = 'pending_reimbursement'
refund_requested class-attribute instance-attribute
refund_requested = 'refund_requested'
refunded class-attribute instance-attribute
refunded = 'refunded'
rejected class-attribute instance-attribute
rejected = 'rejected'
reverted class-attribute instance-attribute
reverted = 'reverted'

RevolutExpenseTransactionType

Bases: StrEnum

atm class-attribute instance-attribute
atm = 'atm'
card_payment class-attribute instance-attribute
card_payment = 'card_payment'
external class-attribute instance-attribute
external = 'external'
fee class-attribute instance-attribute
fee = 'fee'
mileage_reimbursement class-attribute instance-attribute
mileage_reimbursement = 'mileage_reimbursement'
transfer class-attribute instance-attribute
transfer = 'transfer'

RevolutExpenses

Bases: RootModel[list[RevolutExpense]]

root instance-attribute
root

RevolutFee

Bases: BaseModel

amount class-attribute instance-attribute
amount = None

The fee amount.

currency class-attribute instance-attribute
currency = None
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutIndividualName

Bases: BaseModel

first_name class-attribute instance-attribute
first_name = None

The first name of the individual counterparty.

last_name class-attribute instance-attribute
last_name = None

The last name of the individual counterparty.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutIndividualName1

Bases: BaseModel

first_name instance-attribute
first_name

The first name of the recipient.

last_name instance-attribute
last_name

The last name of the recipient.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutIndividualName7

Bases: BaseModel

first_name instance-attribute
first_name

The first name of the recipient.

last_name instance-attribute
last_name

The initial of the last name of the recipient.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutIndividualName8

Bases: BaseModel

first_name instance-attribute
first_name

The first name of the recipient.

last_name instance-attribute
last_name

The last name of the recipient.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutLabelGroup

Bases: RootModel[list[str]]

root instance-attribute
root

A label group with the selected label from that group.

RevolutMerchantControls

Bases: BaseModel

control_type instance-attribute
control_type

The type of control to apply.

merchant_ids instance-attribute
merchant_ids

The list of IDs of merchants to which the control applies.

Tip

To find merchant IDs, check transaction details (→ merchant.id). You can fetch transaction details for a specific transaction ⧉ or for all transactions ⧉.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutMerchantControls1

Bases: BaseModel

control_type instance-attribute
control_type

The type of control to apply.

merchant_ids instance-attribute
merchant_ids

The list of IDs of merchants to which the control applies.

Tip

To find merchant IDs, check transaction details (→ merchant.id). You can fetch transaction details for a specific transaction ⧉ or for all transactions ⧉.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutMerchantControlsSchema

Bases: BaseModel

control_type instance-attribute
control_type

The type of control to apply.

merchant_ids instance-attribute
merchant_ids

The list of IDs of merchants to which the control applies.

Tip

To find merchant IDs, check transaction details (→ merchant.id). You can fetch transaction details for a specific transaction ⧉ or for all transactions ⧉.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutPaymentDraftResponse

Bases: BaseModel

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
payments instance-attribute
payments

The list of payments in the bulk.

scheduled_for class-attribute instance-attribute
scheduled_for = None

The scheduled date of the payment draft in ISO 8601 ⧉ format.

title class-attribute instance-attribute
title = None

The title of the payment draft.

RevolutPaymentDraftsResponse

Bases: BaseModel

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
payment_orders instance-attribute
payment_orders

The list of payment drafts that haven't been sent for processing.

RevolutPaymentInfo

Bases: BaseModel

account_id instance-attribute
account_id

The ID of the account to pay from.

amount instance-attribute
amount
currency class-attribute instance-attribute
currency = None
current_charge_options instance-attribute
current_charge_options

The explanation of conversion process.

error_message class-attribute instance-attribute
error_message = None

The description of the error message.

id instance-attribute
id

The ID of the payment.

Do not confuse it with the payment draft ID ⧉ or transaction ID ⧉.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
reason class-attribute instance-attribute
reason = None

The reason for the current state.

receiver instance-attribute
receiver
reference class-attribute instance-attribute
reference = None

The description of the transaction.

state instance-attribute
state

RevolutPaymentOrderInfo

Bases: BaseModel

id instance-attribute
id

The ID of the payment draft.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
payments_count instance-attribute
payments_count

The number of payments in the payment draft.

scheduled_for class-attribute instance-attribute
scheduled_for = None

The scheduled date of the payment draft in ISO 8601 ⧉ format.

title class-attribute instance-attribute
title = None

The title of the payment draft.

RevolutPaymentReceiver

Bases: BaseModel

account_id class-attribute instance-attribute
account_id = None

The ID of the receiving counterparty's account. Used for bank transfers.

If the counterparty has multiple payment methods available, use it to specify the account to which you want to send the money.

card_id class-attribute instance-attribute
card_id = None

The ID of the receiving counterparty's card. Used for card transfers.

If the counterparty has multiple payment methods available, use it to specify the card to which you want to send the money.

counterparty_id instance-attribute
counterparty_id

The ID of the receiving counterparty.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutPaymentRequest

Bases: BaseModel

account_id instance-attribute
account_id

The ID of the account to pay from.

Note

You can specify only one account ID for multiple payments in the same payment draft.

amount instance-attribute
amount

The amount of the payment.

currency instance-attribute
currency
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
receiver instance-attribute
receiver
reference instance-attribute
reference

The reference for the payment. A piece of text or number you provide to help identify what the payment relates to. It can be used, for example, for reconciliation or tracking purposes.

You might include an invoice number, account or transaction ID, or any other reference meaningful to you.

RevolutPaymentState

Bases: StrEnum

CANCELLED class-attribute instance-attribute
CANCELLED = 'CANCELLED'
COMPLETED class-attribute instance-attribute
COMPLETED = 'COMPLETED'
CREATED class-attribute instance-attribute
CREATED = 'CREATED'
DECLINED class-attribute instance-attribute
DECLINED = 'DECLINED'
DELETED class-attribute instance-attribute
DELETED = 'DELETED'
FAILED class-attribute instance-attribute
FAILED = 'FAILED'
PENDING class-attribute instance-attribute
PENDING = 'PENDING'
REVERTED class-attribute instance-attribute
REVERTED = 'REVERTED'

RevolutPaymentSystem

Bases: StrEnum

ach class-attribute instance-attribute
ach = 'ach'
bacs class-attribute instance-attribute
bacs = 'bacs'
chaps class-attribute instance-attribute
chaps = 'chaps'
elixir class-attribute instance-attribute
elixir = 'elixir'
faster_payments class-attribute instance-attribute
faster_payments = 'faster_payments'
nics class-attribute instance-attribute
nics = 'nics'
rix class-attribute instance-attribute
rix = 'rix'
sepa class-attribute instance-attribute
sepa = 'sepa'
sorbnet class-attribute instance-attribute
sorbnet = 'sorbnet'
sumclearing class-attribute instance-attribute
sumclearing = 'sumclearing'
swift class-attribute instance-attribute
swift = 'swift'

Bases: RevolutPayoutLinkInitialProps, RevolutPayoutLinkAdditionalProps

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutPayoutLinkAccountId

Bases: RootModel[UUID]

root instance-attribute
root

The ID of the sender's account.

RevolutPayoutLinkAdditionalProps

Bases: BaseModel

cancellation_reason class-attribute instance-attribute
cancellation_reason = None

The reason for which the payout link was cancelled.

counterparty_id class-attribute instance-attribute
counterparty_id = None

The ID of the counterparty created based on the recipient's details.

Note

By default, the newly created counterparty is hidden from your counterparties list.

To automatically save it when the link is claimed, pass the save_counterparty parameter set to true.

Alternatively, you can add the recipient to your counterparties later from the list of transactions in the Business app.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
transaction_id class-attribute instance-attribute
transaction_id = None

The ID of the created transaction. Returned only if the payout has been claimed.

RevolutPayoutLinkAmount

Bases: RootModel[float]

root instance-attribute
root

The amount of money to be transferred.

Note

The amount must be between £1 and £2,500, or equivalent in the selected currency.

RevolutPayoutLinkCounterpartyName

Bases: RootModel[str]

root instance-attribute
root

The name of the counterparty provided by the sender.

RevolutPayoutLinkExpiryDate

Bases: RootModel[AwareDatetime]

root instance-attribute
root

The date and time after which the payout link expires in ISO 8601 format ⧉. If the recipient doesn't claim the money before then, the payout link expires and is no longer available.

The default and maximum value is the date and time of creating the link + 7 days.

RevolutPayoutLinkExpiryPeriod

Bases: RootModel[timedelta]

root instance-attribute
root

The period after which the payout link expires if not claimed before, provided in ISO 8601 format ⧉.

The default and maximum value is 7 days from the link creation.

RevolutPayoutLinkInitialProps

Bases: BaseModel

account_id instance-attribute
account_id
amount instance-attribute
amount
counterparty_name instance-attribute
counterparty_name
created_at instance-attribute
created_at

The date and time the payout link was created in ISO 8601 format ⧉.

currency instance-attribute
currency
expiry_date class-attribute instance-attribute
expiry_date = None
id instance-attribute
id

The ID of the payout link.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
payout_methods instance-attribute
payout_methods
reference instance-attribute
reference
request_id instance-attribute
request_id
save_counterparty instance-attribute
save_counterparty

Indicates whether you chose to save the recipient as your counterparty upon link claim. If false then the counterparty will not show up on your counterparties list, for example, when you retrieve your counterparties ⧉. However, you can still retrieve this counterparty by its ID ⧉.

If you didn't choose to save the counterparty on link creation, you can still do it from your transactions list in the Business app.

state instance-attribute
state
transfer_reason_code class-attribute instance-attribute
transfer_reason_code = None
updated_at instance-attribute
updated_at

The date and time the payout link was last updated in ISO 8601 format ⧉.

url class-attribute instance-attribute
url = None

The URL of the payout link. Returned only for active payout links.

RevolutPayoutLinkPayoutMethods

Bases: RootModel[list[RevolutPayoutMethod]]

root instance-attribute
root

The list of payout methods that the recipient can use to claim the payout, where: - revolut: Revolut peer-to-peer (P2P) transfer - bank_account: External bank transfer - card: Card transfer

Note

  • This payout method is available in the UK and the EEA.
  • This payout method is not available in Sandbox.

RevolutPayoutLinkReference

Bases: RootModel[str]

root instance-attribute
root

The reference for the payout transaction, provided by the sender.

RevolutPayoutLinkRequestId

Bases: RootModel[str]

root instance-attribute
root

The ID of the request, provided by the sender.

RevolutPayoutLinkState

Bases: StrEnum

active class-attribute instance-attribute
active = 'active'
awaiting class-attribute instance-attribute
awaiting = 'awaiting'
cancelled class-attribute instance-attribute
cancelled = 'cancelled'
created class-attribute instance-attribute
created = 'created'
expired class-attribute instance-attribute
expired = 'expired'
failed class-attribute instance-attribute
failed = 'failed'
processed class-attribute instance-attribute
processed = 'processed'
processing class-attribute instance-attribute
processing = 'processing'

Bases: RootModel[list[RevolutPayoutLink]]

root instance-attribute
root

A list of payout links

RevolutPayoutMethod

Bases: StrEnum

bank_account class-attribute instance-attribute
bank_account = 'bank_account'
card class-attribute instance-attribute
card = 'card'
revolut class-attribute instance-attribute
revolut = 'revolut'

RevolutProfileType

Bases: StrEnum

business class-attribute instance-attribute
business = 'business'
personal class-attribute instance-attribute
personal = 'personal'

RevolutRecipientCharges

Bases: StrEnum

expected class-attribute instance-attribute
expected = 'expected'
no class-attribute instance-attribute
no = 'no'

RevolutResultCode

Bases: StrEnum

cannot_be_checked class-attribute instance-attribute
cannot_be_checked = 'cannot_be_checked'
close_match class-attribute instance-attribute
close_match = 'close_match'
matched class-attribute instance-attribute
matched = 'matched'
not_matched class-attribute instance-attribute
not_matched = 'not_matched'
temporarily_unavailable class-attribute instance-attribute
temporarily_unavailable = 'temporarily_unavailable'

RevolutRole

Bases: BaseModel

created_at instance-attribute
created_at

The date and time the role was created in ISO 8601 ⧉ format.

id instance-attribute
id

The ID of the role. This can be a UUID or other default role such as OWNER.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
name instance-attribute
name

The name of the role.

updated_at instance-attribute
updated_at

The date and time the role was last updated in ISO 8601 ⧉ format.

RevolutScheme

Bases: StrEnum

mastercard class-attribute instance-attribute
mastercard = 'mastercard'
visa class-attribute instance-attribute
visa = 'visa'

RevolutSpendProgram

Bases: BaseModel

label instance-attribute
label

The name of the spend program.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutSpendingLimitPeriodic

Bases: BaseModel

amount instance-attribute
amount

The value of the spending limit.

currency instance-attribute
currency
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutSpendingLimitSingleTransaction

Bases: BaseModel

amount instance-attribute
amount

The value of the spending limit.

currency instance-attribute
currency
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutSpendingLimits

Bases: BaseModel

all_time class-attribute instance-attribute
all_time = None

The all-time limit for transactions.

day class-attribute instance-attribute
day = None

The daily limit for transactions.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
month class-attribute instance-attribute
month = None

The monthly limit for transactions.

quarter class-attribute instance-attribute
quarter = None

The quarterly limit for transactions.

single class-attribute instance-attribute
single = None

The limit for a single transaction.

week class-attribute instance-attribute
week = None

The weekly limit for transactions.

year class-attribute instance-attribute
year = None

The yearly limit for transactions.

RevolutSpendingLimitsSchema

Bases: BaseModel

all_time class-attribute instance-attribute
all_time = None

The all-time limit for transactions.

day class-attribute instance-attribute
day = None

The daily limit for transactions.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
month class-attribute instance-attribute
month = None

The monthly limit for transactions.

quarter class-attribute instance-attribute
quarter = None

The quarterly limit for transactions.

single class-attribute instance-attribute
single = None

The limit for a single transaction.

week class-attribute instance-attribute
week = None

The weekly limit for transactions.

year class-attribute instance-attribute
year = None

The yearly limit for transactions.

RevolutSpendingPeriodSchema

Bases: RootModel[RevolutSpendingPeriodSchema1 | RevolutSpendingPeriodSchema2]

root instance-attribute
root

RevolutSpendingPeriodSchema1

Bases: BaseModel

end_date class-attribute instance-attribute
end_date = None

The end date (inclusive) of the spending period, in ISO 8601 ⧉ format (YYYY-MM-DD). Uses the timezone set by the business ⧉, or defaults to Europe/London.

end_date_action class-attribute instance-attribute
end_date_action = None

The action to take after the end date of the spending period.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
start_date instance-attribute
start_date

The start date (inclusive) of the spending period, in ISO 8601 ⧉ format (YYYY-MM-DD). Uses the timezone set by the business ⧉, or defaults to Europe/London.

RevolutSpendingPeriodSchema2

Bases: BaseModel

end_date instance-attribute
end_date

The end date (inclusive) of the spending period, in ISO 8601 ⧉ format (YYYY-MM-DD). Uses the timezone set by the business ⧉, or defaults to Europe/London.

end_date_action instance-attribute
end_date_action

The action to take after the end date of the spending period.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
start_date class-attribute instance-attribute
start_date = None

The start date (inclusive) of the spending period, in ISO 8601 ⧉ format (YYYY-MM-DD). Uses the timezone set by the business ⧉, or defaults to Europe/London.

RevolutSpentAmount

Bases: BaseModel

amount instance-attribute
amount
currency instance-attribute
currency
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutState

Bases: StrEnum

active class-attribute instance-attribute
active = 'active'
inactive class-attribute instance-attribute
inactive = 'inactive'

RevolutState1

Bases: StrEnum

created class-attribute instance-attribute
created = 'created'
deleted class-attribute instance-attribute
deleted = 'deleted'
draft class-attribute instance-attribute
draft = 'draft'

RevolutTaxRate

Bases: BaseModel

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
name instance-attribute
name

The name of the tax.

percentage class-attribute instance-attribute
percentage = None

The tax rate percentage applied to the taxable amount. For example, 23 for 23%.

RevolutTeamMember

Bases: BaseModel

created_at instance-attribute
created_at

The date and time the team member was created in ISO 8601 ⧉ format.

email instance-attribute
email

The email of the team member.

first_name class-attribute instance-attribute
first_name = None

The team member's first name.

id instance-attribute
id

The ID of the team member.

last_name class-attribute instance-attribute
last_name = None

The team member's last name.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
role_id instance-attribute
role_id

The ID of the team member's role ⧉. This can be a UUID or other default role such as Owner.

state instance-attribute
state
updated_at instance-attribute
updated_at

The date and time the team member was last updated in ISO 8601 ⧉ format.

RevolutTeamMemberState

Bases: StrEnum

active class-attribute instance-attribute
active = 'active'
confirmed class-attribute instance-attribute
confirmed = 'confirmed'
created class-attribute instance-attribute
created = 'created'
disabled class-attribute instance-attribute
disabled = 'disabled'
locked class-attribute instance-attribute
locked = 'locked'
waiting class-attribute instance-attribute
waiting = 'waiting'

RevolutTransaction

Bases: BaseModel

card class-attribute instance-attribute
card = None
completed_at class-attribute instance-attribute
completed_at = None

The date and time the transaction was completed in ISO 8601 ⧉ format. This is required when the transaction state is completed.

created_at instance-attribute
created_at

The date and time the transaction was created in ISO 8601 ⧉ format.

id instance-attribute
id

The ID of the transaction.

legs instance-attribute
legs

The legs of the transaction: - For transactions between your Revolut accounts, there can be 2 legs, for example, an internal transfer made out of the GBP account and into the EUR account. - For transactions in other cases, there is only 1 leg.

merchant class-attribute instance-attribute
merchant = None
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
reason_code class-attribute instance-attribute
reason_code = None

The reason code when the transaction state is declined or failed.

reference class-attribute instance-attribute
reference = None

The payment reference.

related_transaction_id class-attribute instance-attribute
related_transaction_id = None

The ID of the original transaction to which this transaction is related. Returned, for example, when this transaction is a refund of the related transaction, or for transactions related to cashback.

request_id class-attribute instance-attribute
request_id = None

The request ID that you provided previously.

scheduled_for class-attribute instance-attribute
scheduled_for = None

The scheduled date of the payment, if applicable. Provided in ISO 8601 ⧉ format.

state instance-attribute
state
type instance-attribute
type
updated_at instance-attribute
updated_at

The date and time the transaction was last updated in ISO 8601 ⧉ format.

RevolutTransactionCard

Bases: BaseModel

card_number class-attribute instance-attribute
card_number = None

The masked card number.

first_name class-attribute instance-attribute
first_name = None

The first name of the cardholder.

id class-attribute instance-attribute
id = None

The ID of the card.

last_name class-attribute instance-attribute
last_name = None

The last name of the cardholder.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
phone class-attribute instance-attribute
phone = None

The phone number of the cardholder in E.164 ⧉ format.

references class-attribute instance-attribute
references = None

Card references (references ⧉).

Note

These are the references that were assigned to the card at the time the transaction was made. Any update ⧉ to card references does not affect existing transactions.

RevolutTransactionCounterparty

Bases: BaseModel

account_id class-attribute instance-attribute
account_id = None

The ID of the counterparty account.

account_type instance-attribute
account_type
id class-attribute instance-attribute
id = None

The ID of the counterparty.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutTransactionCounterpartyAccountType

Bases: StrEnum

external class-attribute instance-attribute
external = 'external'
revolut class-attribute instance-attribute
revolut = 'revolut'
self class-attribute instance-attribute
self = 'self'

RevolutTransactionLeg

Bases: BaseModel

account_id instance-attribute
account_id

The ID of the account that the transaction is associated with.

amount instance-attribute
amount

The amount of the transaction.

balance class-attribute instance-attribute
balance = None

The total balance of the account that the transaction is associated with.

bill_amount class-attribute instance-attribute
bill_amount = None

The billing amount for cross-currency payments.

bill_currency class-attribute instance-attribute
bill_currency = None
counterparty class-attribute instance-attribute
counterparty = None
currency instance-attribute
currency
description class-attribute instance-attribute
description = None

The transaction leg purpose.

fee class-attribute instance-attribute
fee = None

The amount of the transaction fee.

leg_id instance-attribute
leg_id

The ID of the leg.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutTransactionLimitCurrency

Bases: RootModel[str]

root instance-attribute
root

The currency of the spending limit, provided as ISO 4217 ⧉ code in upper case.

RevolutTransactionMerchant

Bases: BaseModel

category_code class-attribute instance-attribute
category_code = None

The category code of the merchant.

city class-attribute instance-attribute
city = None

The city of the merchant.

country class-attribute instance-attribute
country = None
id class-attribute instance-attribute
id = None

The ID of the merchant.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
name class-attribute instance-attribute
name = None

The name of the merchant.

RevolutTransactionPaymentRequest

Bases: BaseModel

account_id instance-attribute
account_id

The ID of the account that you send the funds from.

amount instance-attribute
amount

The amount to transfer.

charge_bearer class-attribute instance-attribute
charge_bearer = None
currency class-attribute instance-attribute
currency = None
exchange_reason_code class-attribute instance-attribute
exchange_reason_code = None
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
receiver instance-attribute
receiver
reference class-attribute instance-attribute
reference = None

The reference for the payment. A piece of text or number you provide to help identify what the payment relates to. It can be used, for example, for reconciliation or tracking purposes.

You might include an invoice number, account or transaction ID, or any other reference meaningful to you.

If the provided reference is longer than 140 characters, it will be truncated. Note that some recipient banks might truncate the reference even further.

request_id instance-attribute
request_id

The ID of the transaction, provided by you.

Caution

Always provide a unique request ID for each individual payment. This allows you to safely retry the payment in the event of any network issues; if the payment was successful, a second attempt with the same request ID won't be processed.

transfer_reason_code class-attribute instance-attribute
transfer_reason_code = None

RevolutTransactionState

Bases: StrEnum

completed class-attribute instance-attribute
completed = 'completed'
created class-attribute instance-attribute
created = 'created'
declined class-attribute instance-attribute
declined = 'declined'
failed class-attribute instance-attribute
failed = 'failed'
pending class-attribute instance-attribute
pending = 'pending'
reverted class-attribute instance-attribute
reverted = 'reverted'

RevolutTransactionType

Bases: StrEnum

atm class-attribute instance-attribute
atm = 'atm'
card_chargeback class-attribute instance-attribute
card_chargeback = 'card_chargeback'
card_credit class-attribute instance-attribute
card_credit = 'card_credit'
card_payment class-attribute instance-attribute
card_payment = 'card_payment'
card_refund class-attribute instance-attribute
card_refund = 'card_refund'
charge class-attribute instance-attribute
charge = 'charge'
charge_refund class-attribute instance-attribute
charge_refund = 'charge_refund'
exchange class-attribute instance-attribute
exchange = 'exchange'
fee class-attribute instance-attribute
fee = 'fee'
loan class-attribute instance-attribute
loan = 'loan'
refund class-attribute instance-attribute
refund = 'refund'
tax class-attribute instance-attribute
tax = 'tax'
tax_refund class-attribute instance-attribute
tax_refund = 'tax_refund'
topup class-attribute instance-attribute
topup = 'topup'
topup_return class-attribute instance-attribute
topup_return = 'topup_return'
transfer class-attribute instance-attribute
transfer = 'transfer'

RevolutTransactions

Bases: RootModel[list[RevolutTransaction]]

root instance-attribute
root

RevolutTransferReason

Bases: BaseModel

code instance-attribute
code

Category name of the transfer reason.

country instance-attribute
country
currency instance-attribute
currency
description instance-attribute
description

The description of the given transfer reason.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutTransferReasonCode

Bases: RootModel[str]

root instance-attribute
root

The reason code for the transaction. Transactions to certain countries and currencies might require you to provide a transfer reason. You can check available reason codes with the GET /transfer-reasons operation ⧉.

If a transfer reason is not required for the given currency and country, this field is ignored.

RevolutTransferReasons

Bases: RootModel[list[RevolutTransferReason]]

root instance-attribute
root

RevolutTransferRequest

Bases: BaseModel

amount instance-attribute
amount

The amount of the funds to be transferred.

currency instance-attribute
currency
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
reference class-attribute instance-attribute
reference = None

The reference for the funds transfer.

request_id instance-attribute
request_id

The ID of the request, provided by you. It helps you identify the transaction in your system.

Caution

To ensure that a transfer is not processed multiple times if there are network or system errors, the same request_id should be used for requests related to the same transfer.

source_account_id instance-attribute
source_account_id

The ID of the source account that you transfer the funds from.

target_account_id instance-attribute
target_account_id

The ID of the target account that you transfer the funds to.

RevolutTransferResponse

Bases: BaseModel

completed_at class-attribute instance-attribute
completed_at = None

The date and time the transaction was completed in ISO 8601 ⧉ format.

created_at instance-attribute
created_at

The date and time the transaction was created in ISO 8601 ⧉ format.

id instance-attribute
id

The ID of the transaction created.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
state instance-attribute
state

RevolutType

Bases: StrEnum

au_cop class-attribute instance-attribute
au_cop = 'au_cop'
eu_cop class-attribute instance-attribute
eu_cop = 'eu_cop'
ro_cop class-attribute instance-attribute
ro_cop = 'ro_cop'
uk_cop class-attribute instance-attribute
uk_cop = 'uk_cop'

RevolutType4

Bases: StrEnum

external class-attribute instance-attribute
external = 'external'
revolut class-attribute instance-attribute
revolut = 'revolut'

RevolutUnit

Bases: StrEnum

days class-attribute instance-attribute
days = 'days'
hours class-attribute instance-attribute
hours = 'hours'

RevolutUpdateWebhookRequest

Bases: BaseModel

events class-attribute instance-attribute
events = None

A list of event types to subscribe to.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
url class-attribute instance-attribute
url = None

A valid webhook URL to which to send event notifications. The supported protocol is https.

RevolutUrl

Bases: RootModel[AnyUrl]

root instance-attribute
root

A valid webhook URL to which to send event notifications. The supported protocol is https.

RevolutValidateAccountNameRequestAU

Bases: BaseModel

account_no instance-attribute
account_no

The account number of the counterparty.

bsb instance-attribute
bsb

The BSB (Bank-State-Branch) number for the counterparty's account. Used to identify the bank and branch in Australia.

company_name class-attribute instance-attribute
company_name = None

The name of the business counterparty.

Required when the account type is business (individual_name is not specified).

individual_name class-attribute instance-attribute
individual_name = None

The name of the individual counterparty, split into first name and last name.

Required when the account type is personal (company_name isn't specified).

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutValidateAccountNameRequestEUR

Bases: BaseModel

bic class-attribute instance-attribute
bic = None

The BIC (Bank Identifier Code) for the counterparty's account. Also known as the SWIFT code. It can be provided, for example, when the automatic BIC detection fails and the check is unsuccessful.

company_name class-attribute instance-attribute
company_name = None

The name of the business counterparty.

Required when the account type is business (individual_name is not specified).

iban instance-attribute
iban

The IBAN (International Bank Account Number) for the counterparty's account.

individual_name class-attribute instance-attribute
individual_name = None

The name of the individual counterparty, split into first name and last name.

Required when the account type is personal (company_name isn't specified).

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
recipient_country instance-attribute
recipient_country

The counterparty's bank country, provided as a 2-letter ISO 3166 ⧉ code.

recipient_currency instance-attribute
recipient_currency

The counterparty account’s currency, provided as a 3-letter ISO 4217 ⧉ code.

For VoP, the possible value is EUR.

RevolutValidateAccountNameRequestRO

Bases: BaseModel

bic class-attribute instance-attribute
bic = None

The BIC (Bank Identifier Code) for the counterparty's account. Also known as the SWIFT code. This value is optional. It can be provided, for example, when the automatic BIC detection fails and the check is unsuccessful.

company_name class-attribute instance-attribute
company_name = None

The name of the business counterparty.

Required when the account type is business (individual_name is not specified).

ⓘ Note that for RO ⧉, the name you provide helps identify the request, but is not used for the actual check. The API simply returns the partially masked name associated with the IBAN for you to validate. Therefore, the returned name may differ from the one you provide.

iban instance-attribute
iban

The IBAN (International Bank Account Number) for the counterparty's account.

individual_name class-attribute instance-attribute
individual_name = None

The name of the individual counterparty, split into first name and last name.

Required when the account type is personal (company_name isn't specified).

ⓘ Note that for RO ⧉, the name you provide helps identify the request, but is not used for the actual check. The API simply returns the partially masked name associated with the IBAN for you to validate. Therefore, the returned name may differ from the one you provide.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
recipient_country instance-attribute
recipient_country

The counterparty's bank country, provided as a 2-letter ISO 3166 ⧉ code.

For RO CoP, the possible value is RO.

recipient_currency instance-attribute
recipient_currency

The counterparty account’s currency, provided as a 3-letter ISO 4217 ⧉ code.

For RO CoP, the possible value is RON.

RevolutValidateAccountNameRequestUK

Bases: BaseModel

account_no instance-attribute
account_no

The account number of the counterparty.

company_name class-attribute instance-attribute
company_name = None

The name of the business counterparty.

Required when the account type is business (individual_name is not specified).

individual_name class-attribute instance-attribute
individual_name = None

The name of the individual counterparty, split into first name and last name.

Required when the account type is personal (company_name isn't specified).

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
sort_code instance-attribute
sort_code

The sort code of the counterparty's account.

RevolutValidateAccountNameResponseAU

Bases: BaseModel

company_name class-attribute instance-attribute
company_name = None

The name of the recipient when the account type is business. Provided only if individual_name is not specified.

When the name is a close match, the actual full name is returned. Otherwise, the name you provided is returned.

Mismatched name type is corrected

The mismatched name type in the request is corrected in the response. This means that, for example, if an individual recipient's name was provided under company_name, in the response it is returned under individual_name.

individual_name class-attribute instance-attribute
individual_name = None

The name of the recipient when the account type is personal. Provided only if company_name is not specified.

When the name is a close match, the actual first and last names are returned. Otherwise, the name you provided is returned.

Mismatched name type is corrected

The mismatched name type in the request is corrected in the response. This means that, for example, if an individual recipient's name was provided under company_name, in the response it is returned under individual_name.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
reason class-attribute instance-attribute
reason = None
result_code instance-attribute
result_code

The result of the check.

Possible values for AU: - matched: The name matches the provided details. For personal accounts, this also covers close matches, in which case the actual name is returned. - close_match (business account): The name is similar to the provided value. - not_matched: The name doesn't match the provided values. - cannot_be_checked: The check cannot be performed and retries won't help. For example, the recipient's bank doesn't support CoP. - temporarily_unavailable: The check cannot be performed right now. For example, the recipient's bank didn't respond to our request. You should retry the request later.

RevolutValidateAccountNameResponseEUR

Bases: BaseModel

company_name class-attribute instance-attribute
company_name = None

The name of the recipient when the account type is business. Provided only if individual_name is not specified.

When the name is a close match, the actual name is returned. Otherwise, the name you provided is returned.

Mismatched name type is not corrected

The name type in the response is returned as it was provided in the request. For example, if an individual recipient's name was provided under company_name, in the response it's still returned under company_name (instead of individual_name).

individual_name class-attribute instance-attribute
individual_name = None

The name of the recipient when the account type is personal. Provided only if company_name is not specified.

When the name is a close match, the actual name is returned. Otherwise, the name you provided is returned.

Mismatched name type is not corrected

The name type in the response is returned as it was provided in the request. For example, if an individual recipient's name was provided under company_name, in the response it's still returned under company_name (instead of individual_name).

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
reason class-attribute instance-attribute
reason = None
result_code instance-attribute
result_code

The result of the check.

Possible values for EUR: - matched: The name matches the provided details. - close_match: The recipient's name is similar to the provided value. The actual name is returned. - not_matched: The name and account type don't match the provided values. The name provided in the request is returned. - cannot_be_checked: The check cannot be performed and retries won't help. For example, the recipient's bank doesn't support VoP. - temporarily_unavailable: The check cannot be performed right now. For example, the recipient's bank didn't respond to our request. You should retry the request later.

RevolutValidateAccountNameResponseRO

Bases: BaseModel

company_name class-attribute instance-attribute
company_name = None

The partially masked name of the recipient when the account type is business. Provided only if individual_name is not specified.

Name considerations

  • The name you provide helps identify the request, but is not used for the actual check. The API simply returns the partial name associated with the IBAN for you to validate. Therefore, the returned name may differ from the one you provided.
  • Mismatched name type is not corrected. This means that the name type in the response is returned as it was provided in the request. For example, if an individual recipient's name was provided under company_name, in the response it's still returned under company_name (instead of individual_name).
individual_name class-attribute instance-attribute
individual_name = None

The partial name of the recipient when the account type is personal, that is, the first name and the last name's initial. Provided only if company_name is not specified.

Name considerations

  • The name you provide helps identify the request, but is not used for the actual check. The API simply returns the partial name associated with the IBAN for you to validate. Therefore, the returned name may differ from the one you provided.
  • Mismatched name type is not corrected. This means that the name type in the response is returned as it was provided in the request. For example, if an individual recipient's name was provided under company_name, in the response it's still returned under company_name (instead of individual_name).
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
reason class-attribute instance-attribute
reason = None
result_code instance-attribute
result_code

The result of the check.

For RO CoP, the API checks if an account exists for the provided IBAN. Possible results are: - matched: An account with the provided IBAN was found. When this status is returned, the API also returns a partial name of the account holder. Use this returned name to validate your recipient's details. - cannot_be_checked: The check cannot be performed and retries won't help. For example, the recipient's bank doesn't support CoP. - temporarily_unavailable: The check cannot be performed right now. For example, the recipient's bank didn't respond to our request. You should retry the request later.

RevolutValidateAccountNameResponseUK

Bases: BaseModel

company_name class-attribute instance-attribute
company_name = None

The name of the recipient when the account type is business. Provided only if individual_name is not specified.

When the name is a close match, the actual full name is returned, otherwise, the name you provided is returned.

individual_name class-attribute instance-attribute
individual_name = None

The name of the recipient when the account type is personal. Provided only if company_name is not specified.

When the name is a close match, the actual first and last names are returned in full.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
reason class-attribute instance-attribute
reason = None
result_code instance-attribute
result_code

The result of the check.

Possible values for UK: - matched: The name and account type match the provided details. - close_match: The name and/or account type are similar to the provided values: - The name is a match, but the account type is incorrect. For example, an individual recipient's name was provided under company_name. The actual account type is returned. - The name is similar to the provided values. Account type is correct. The actual name is returned. - The name is similar to the provided values, and the account type is incorrect. The actual values are returned.

The actual values are returned. - not_matched: The name and account type don't match the provided values. - cannot_be_checked: The check cannot be performed and retries won't help. For example, the recipient's bank doesn't support CoP. - temporarily_unavailable: The check cannot be performed right now. For example, the recipient's bank didn't respond to our request. You should retry the request later.

RevolutWebhookEvent

Bases: BaseModel

created_at instance-attribute
created_at

The date and time the event was created in ISO 8601 ⧉ format.

id instance-attribute
id

The ID of the webhook event.

last_sent_date class-attribute instance-attribute
last_sent_date = None

The date and time the last attempt at the event delivery occurred in ISO 8601 ⧉ format.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
payload instance-attribute
payload

The details of the failed event.

updated_at instance-attribute
updated_at

The date and time the event was last updated in ISO 8601 ⧉ format.

webhook_id instance-attribute
webhook_id

The ID of the webhook for which the event failed.

webhook_url instance-attribute
webhook_url

The valid webhook URL that event notifications are sent to. The supported protocol is https.

RevolutWebhookEventType

Bases: Enum

PayoutLinkCreated class-attribute instance-attribute
PayoutLinkCreated = 'PayoutLinkCreated'
PayoutLinkStateChanged class-attribute instance-attribute
PayoutLinkStateChanged = 'PayoutLinkStateChanged'
TransactionCreated class-attribute instance-attribute
TransactionCreated = 'TransactionCreated'
TransactionStateChanged class-attribute instance-attribute
TransactionStateChanged = 'TransactionStateChanged'

RevolutWebhookEvents

Bases: RootModel[list[RevolutWebhookEvent]]

root instance-attribute
root

RevolutWebhookSigningSecretRotateRequest

Bases: BaseModel

expiration_period class-attribute instance-attribute
expiration_period = None

The expiration period for the signing secret in ISO 8601 format ⧉. If set, when you rotate the secret, it continues to be valid until the expiration period has passed. Otherwise, on rotation, the secret is invalidated immediately. The maximum value is 7 days.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')

RevolutWebhookV1

Bases: BaseModel

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
url instance-attribute
url

The valid webhook URL that event notifications are sent to. The supported protocol is https.

RevolutWebhookV2

Bases: BaseModel

events instance-attribute
events

The list of event types that you are subscribed to.

id instance-attribute
id

The ID of the webhook.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
signing_secret instance-attribute
signing_secret

The signing secret for the webhook.

url instance-attribute
url

The valid webhook URL that event notifications are sent to. The supported protocol is https.

RevolutWebhookV2Basic

Bases: BaseModel

events instance-attribute
events

The list of event types that you are subscribed to.

id instance-attribute
id

The ID of the webhook.

model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
url instance-attribute
url

The valid webhook URL that event notifications are sent to. The supported protocol is https.

RevolutWebhooks

Bases: RootModel[list[RevolutWebhookV2Basic]]

root instance-attribute
root

fix_openapi_spec

Fix invalid OpenAPI spec issues for datamodel-codegen compatibility.

Fixes: - Removes invalid minimum/maximum constraints from string-typed fields (these constraints are only valid for numeric types in JSON Schema) - Fixes invalid discriminator definitions that lack propertyName and oneOf - Converts ::: admonition syntax to MkDocs Material !!! syntax

Usage

python fix_openapi_spec.py input.yaml > output.yaml python fix_openapi_spec.py input.yaml | datamodel-codegen --input-file-type openapi ...

convert_admonitions

convert_admonitions(text)

Convert ::: admonition syntax to MkDocs Material !!! syntax.

Handles: - :::note ... ::: -> !!! note\n ... - ::::::note ... :::::: (nested containers) -> flattened !!! note - Nested admonitions are flattened to the same level

Source code in shared/services/payment_providers/revolut/openapi/fix_openapi_spec.py
def convert_admonitions(text: str) -> str:
    """
    Convert ::: admonition syntax to MkDocs Material !!! syntax.

    Handles:
    - :::note ... ::: -> !!! note\\n    ...
    - ::::::note ... :::::: (nested containers) -> flattened !!! note
    - Nested admonitions are flattened to the same level
    """
    if ":::" not in text:
        return text

    lines = text.split("\n")
    result_lines: list[str] = []
    current_admonition: str | None = None
    current_title: str | None = None
    content_buffer: list[str] = []

    # Pattern to match admonition start: :::+ followed by type and optional [title]
    # e.g., :::note, ::::::caution, :::tip[Custom title]
    admonition_start_pattern = re.compile(r"^(:+)(\w+)(?:\[([^\]]+)\])?\s*$")
    # Pattern to match admonition end: just colons (:::, ::::::, etc.)
    admonition_end_pattern = re.compile(r"^(:+)\s*$")

    def flush_admonition() -> None:
        """Flush current admonition content to result."""
        nonlocal current_admonition, current_title, content_buffer
        if current_admonition:
            if current_title:
                result_lines.append(f'!!! {current_admonition} "{current_title}"')
            else:
                result_lines.append(f"!!! {current_admonition}")
            for line in content_buffer:
                if line.strip():
                    result_lines.append(f"    {line}")
                else:
                    result_lines.append("")
            # Remove trailing empty lines from the admonition
            while result_lines and result_lines[-1] == "":
                result_lines.pop()
            result_lines.append("")  # Add one blank line after admonition
        current_admonition = None
        current_title = None
        content_buffer = []

    for line in lines:
        stripped = line.strip()

        # Check for admonition start
        start_match = admonition_start_pattern.match(stripped)
        if start_match:
            # Flush any previous admonition
            flush_admonition()
            current_admonition = start_match.group(2)
            current_title = start_match.group(3)  # May be None if no [title]
            continue

        # Check for admonition end (just colons) - always skip these lines
        end_match = admonition_end_pattern.match(stripped)
        if end_match:
            # Flush current admonition if we're inside one
            if current_admonition:
                flush_admonition()
            # Skip the end marker line entirely
            continue

        # Regular content
        if current_admonition:
            content_buffer.append(line)
        else:
            result_lines.append(line)

    # Flush any remaining admonition
    flush_admonition()

    # Clean up multiple consecutive blank lines
    cleaned_lines: list[str] = []
    prev_blank = False
    for line in result_lines:
        is_blank = line.strip() == ""
        if is_blank and prev_blank:
            continue
        cleaned_lines.append(line)
        prev_blank = is_blank

    return "\n".join(cleaned_lines).strip()

fix_invalid_discriminator

fix_invalid_discriminator(obj)

Fix invalid discriminator definitions.

OpenAPI 3.0 discriminators must have a propertyName and be used with oneOf/anyOf. If we find a discriminator with only mapping (no propertyName), convert it to oneOf.

Source code in shared/services/payment_providers/revolut/openapi/fix_openapi_spec.py
def fix_invalid_discriminator(obj: dict[str, Any]) -> None:
    """
    Fix invalid discriminator definitions.

    OpenAPI 3.0 discriminators must have a propertyName and be used with oneOf/anyOf.
    If we find a discriminator with only mapping (no propertyName), convert it to oneOf.
    """
    if "discriminator" not in obj:
        return

    discriminator = obj["discriminator"]
    if not isinstance(discriminator, dict):
        return

    # Check if discriminator has mapping but no propertyName (invalid)
    if "mapping" in discriminator and "propertyName" not in discriminator:
        mapping = discriminator["mapping"]
        if isinstance(mapping, dict):
            # Convert to oneOf pattern
            one_of_refs = [{"$ref": ref} for ref in mapping.values()]
            obj["type"] = "object"
            obj["oneOf"] = one_of_refs
            del obj["discriminator"]

fix_openapi_spec

fix_openapi_spec(spec)

Fix the entire OpenAPI spec.

Source code in shared/services/payment_providers/revolut/openapi/fix_openapi_spec.py
def fix_openapi_spec(spec: dict[str, Any]) -> dict[str, Any]:
    """Fix the entire OpenAPI spec."""
    fix_schema_object(spec)
    fix_string_values(spec)
    return spec

fix_schema_object

fix_schema_object(obj)

Recursively fix schema objects by removing invalid constraints.

Removes minimum/maximum from string-typed fields since these constraints are only valid for numeric types in JSON Schema.

Source code in shared/services/payment_providers/revolut/openapi/fix_openapi_spec.py
def fix_schema_object(obj: Any) -> None:
    """
    Recursively fix schema objects by removing invalid constraints.

    Removes `minimum`/`maximum` from string-typed fields since these
    constraints are only valid for numeric types in JSON Schema.
    """
    if not isinstance(obj, dict):
        return

    # Fix invalid discriminator definitions
    fix_invalid_discriminator(obj)

    # Check if this is a schema object with type: string
    if obj.get("type") == "string":
        # Remove minimum if present and not numeric (shouldn't be there at all for strings)
        if "minimum" in obj and not is_numeric(obj["minimum"]):
            del obj["minimum"]
        # Remove maximum if present and not numeric
        if "maximum" in obj and not is_numeric(obj["maximum"]):
            del obj["maximum"]
        # Remove invalid default values
        if "default" in obj:
            default_val = obj["default"]
            # Remove "now + X days" style defaults from date-time fields
            if (
                obj.get("format") == "date-time"
                and isinstance(default_val, str)
                and "now" in default_val.lower()
            ):
                del obj["default"]
            # Remove duration string defaults (e.g., "P7D", "P0D") - they can't be parsed as timedelta
            elif obj.get("format") == "duration" and isinstance(default_val, str):
                del obj["default"]

    # Recurse into nested structures
    for value in obj.values():
        if isinstance(value, dict):
            fix_schema_object(value)
        elif isinstance(value, list):
            for item in value:
                fix_schema_object(item)

fix_string_values

fix_string_values(obj)

Recursively fix string values in the spec.

Converts ::: admonition syntax to MkDocs Material !!! syntax in description fields.

Source code in shared/services/payment_providers/revolut/openapi/fix_openapi_spec.py
def fix_string_values(obj: Any) -> None:
    """
    Recursively fix string values in the spec.

    Converts ::: admonition syntax to MkDocs Material !!! syntax in description fields.
    """
    if isinstance(obj, dict):
        for key, value in obj.items():
            if key == "description" and isinstance(value, str):
                obj[key] = convert_admonitions(value)
            else:
                fix_string_values(value)
    elif isinstance(obj, list):
        for item in obj:
            fix_string_values(item)

is_numeric

is_numeric(value)

Check if a value is numeric (int or float).

Source code in shared/services/payment_providers/revolut/openapi/fix_openapi_spec.py
def is_numeric(value: Any) -> bool:
    """Check if a value is numeric (int or float)."""
    return isinstance(value, (int, float)) and not isinstance(value, bool)

main

main()
Source code in shared/services/payment_providers/revolut/openapi/fix_openapi_spec.py
def main() -> None:
    if len(sys.argv) != 2:
        sys.stderr.write(f"Usage: {sys.argv[0]} <input.yaml>\n")
        sys.exit(1)

    input_file = sys.argv[1]

    with open(input_file, encoding="utf-8") as f:
        spec = yaml.safe_load(f)

    fixed_spec = fix_openapi_spec(spec)

    # Output to stdout with proper YAML formatting
    yaml.dump(
        fixed_spec,
        sys.stdout,
        default_flow_style=False,
        allow_unicode=True,
        sort_keys=False,
        width=120,
    )

shared.services.payment_providers.revolut.revolut_business_api_client

AUTH_ENDPOINT module-attribute

AUTH_ENDPOINT = 'auth/token'

COUNTERPARTY_ENDPOINT module-attribute

COUNTERPARTY_ENDPOINT = 'counterparty'

ConfiguredResponse module-attribute

ConfiguredResponse = tuple[
    int, RevolutResponse | RevolutErrorResponse
]

PAY_ENDPOINT module-attribute

PAY_ENDPOINT = 'pay'

RevolutArray module-attribute

RevolutArray = list[RevolutObject]

RevolutAuth

RevolutAuth(token)

Bases: AuthBase

Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def __init__(self, token: str) -> None:
    self.token = token

__call__

__call__(r)
Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def __call__(self, r: requests.PreparedRequest) -> requests.PreparedRequest:
    r.headers["Authorization"] = f"Bearer {self.token}"
    return r

token instance-attribute

token = token

RevolutBusinessApiClient

RevolutBusinessApiClient(api)

This class wraps the Revolut Business API and provides a single point of entry for all Revolut Business API calls.

It also provides better typing over the raw API using the OpenAPI generated models, which rely on Pydantic for validation.

Implements the following Nullable patterns: - Nullables: https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks#nullables ⧉ - Parameterless instantiation: https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks#instantiation ⧉ - Thin wrapper: https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks#thin-wrapper ⧉ - Embedded stub: https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks#embedded-stub ⧉

See also
Tags
Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def __init__(
    self,
    api: RevolutBusinessApiProtocol,
) -> None:
    self.api = api

RealApi

RealApi(revolut_config)

Bases: RevolutBusinessApiProtocol

API client that calls the real API.

Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def __init__(
    self,
    revolut_config: RevolutBusinessAccountConfig,
) -> None:
    private_key = raw_secret_from_config(
        config_key=revolut_config.private_key_secret_name,
        default_secret_value=current_config.get(revolut_config.private_key),
    )
    if private_key is None:
        raise ValueError("Revolut private key is not set/configured")
    self._client_id = get_config_or_fail(revolut_config.client_id)
    self._private_key = private_key
    self._base_url = get_config_or_fail(revolut_config.base_url)

    self._revolut_issuer = get_config_or_fail(revolut_config.issuer)

    secret_name = get_config_or_fail(revolut_config.api_credentials_secret_name)
    if not secret_name:
        raise RuntimeError(
            f"{revolut_config.api_credentials_secret_name} is not set"
        )

    self._secrets_manager_client = SecretsManager(secret_name)
add_counterparty
add_counterparty(request)

https://developer.revolut.com/docs/business/add-counterparty ⧉

Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def add_counterparty(
    self,
    request: RevolutRequest,
) -> RevolutObject:
    """
    https://developer.revolut.com/docs/business/add-counterparty
    """

    prepared_request = requests.Request(
        method="POST",
        url=f"{self._base_url}{COUNTERPARTY_ENDPOINT}",
        json=request,
    ).prepare()

    return self._authenticate_and_execute_request(  # type: ignore[no-any-return]
        request=prepared_request,
        endpoint=COUNTERPARTY_ENDPOINT,
    )
create_payment
create_payment(request)

https://developer.revolut.com/docs/business/create-payment ⧉

Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def create_payment(
    self,
    request: RevolutRequest,
) -> RevolutObject:
    """
    https://developer.revolut.com/docs/business/create-payment
    """

    prepared_request = requests.Request(
        method="POST",
        url=f"{self._base_url}{PAY_ENDPOINT}",
        json=request,
    ).prepare()

    # Revolut forbids issuing concurrent payment requests
    with AdvisoryLock("revolut_create_payment"):
        return self._authenticate_and_execute_request(  # type: ignore[no-any-return]
            request=prepared_request,
            endpoint=PAY_ENDPOINT,
            log_params={
                "request_id": request["request_id"],
                "counterparty_id": request["receiver"]["counterparty_id"],
                "counterparty_account_id": request["receiver"]["account_id"],
            },
        )
get_transaction
get_transaction(*, transaction_id=None, request_id=None)

https://developer.revolut.com/docs/business/get-transaction ⧉

Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def get_transaction(
    self,
    *,
    transaction_id: str | None = None,
    request_id: str | None = None,
) -> RevolutObject:
    """
    https://developer.revolut.com/docs/business/get-transaction
    """

    if transaction_id is None and request_id is None:
        raise ValueError("Either transaction_id or request_id must be provided")
    if transaction_id and request_id:
        raise ValueError(
            "Only one of transaction_id or request_id must be provided"
        )

    prepared_request = requests.Request(
        method="GET",
        url=f"{self._base_url}{TRANSACTION_ENDPOINT.format(transaction_id or request_id)}",
        params={"id_type": "request_id"} if request_id else None,
        headers={"Cache-Control": "no-cache"},
    ).prepare()

    return self._authenticate_and_execute_request(  # type: ignore[no-any-return]
        request=prepared_request,
        endpoint=TRANSACTION_ENDPOINT,
        log_params={
            "transaction_id": transaction_id,
            "request_id": request_id,
        },
    )
get_transactions
get_transactions(request)

https://developer.revolut.com/docs/business/get-transactions ⧉

Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def get_transactions(
    self,
    request: RevolutRequest,
) -> RevolutArray:  # Note: in case of error the response is an object but we raise an exception in this case
    """
    https://developer.revolut.com/docs/business/get-transactions
    """

    count = request.get("count", 100)
    if count > 1000:
        # https://developer.revolut.com/docs/api-reference/business/#operation/getTransactions
        raise ValueError(
            "Revolut doesn't support returning more than 1000 transactions"
        )

    prepared_request = requests.Request(
        method="GET",
        url=f"{self._base_url}{TRANSACTIONS_ENDPOINT}",
        params=request,
        headers={"Cache-Control": "no-cache"},
    ).prepare()

    return self._authenticate_and_execute_request(  # type: ignore[no-any-return]
        request=prepared_request,
        endpoint=TRANSACTIONS_ENDPOINT,
    )
rotate_access_token
rotate_access_token()
Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def rotate_access_token(self) -> None:
    data = {
        "grant_type": "refresh_token",
        "refresh_token": self._secrets_manager_client.get("refresh_token"),
        "client_id": self._client_id,
        "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
        "client_assertion": jwt.encode(
            {
                "iss": self._revolut_issuer,
                "sub": self._client_id,
                "aud": "https://revolut.com",
                "exp": datetime.utcnow() + timedelta(seconds=30),
            },
            self._private_key,
            algorithm="RS256",
            headers={"alg": "RS256", "typ": "JWT"},
        ),
    }

    request = requests.Request(
        method="POST",
        url=self._base_url + AUTH_ENDPOINT,
        data=data,
        headers={"Content-Type": "application/x-www-form-urlencoded"},
    ).prepare()

    current_logger.debug("Calling Revolut API: POST %s", request.url)

    # Note: request does not need to be authenticated
    response = requests.Session().send(request)
    self._raise_for_status(response, endpoint=AUTH_ENDPOINT)

    self._secrets_manager_client.set(
        "access_token", response.json()["access_token"]
    )

StubbedApi

StubbedApi(*, track_requests=None, responses=None)

Bases: RevolutBusinessApiProtocol

Embedded stub for Revolut Business API, replicates the same interface as the Revolut Business API.

Can be configured with test data for get_transaction.

https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks#embedded-stub ⧉

Implements the following Nullable patterns: - Output tracking: https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks#output-tracking ⧉ - Configurable responses: https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks#configurable-responses ⧉

Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def __init__(
    self,
    *,
    track_requests: list[TrackedRequest] | None = None,
    responses: list[ConfiguredResponse] | None = None,
) -> None:
    self.track_requests = track_requests
    self.responses = responses
add_counterparty
add_counterparty(request)
Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def add_counterparty(
    self,
    request: RevolutRequest,
) -> RevolutObject:
    if self.responses is not None and len(self.responses) > 0:
        status_code, data = self.responses.pop(0)
    else:
        # Simulate a counterparty creation from request data
        status_code = 200
        counterparty_id = uuid.uuid4()
        account_id = uuid.uuid4()
        if individual_name := request.get("individual_name"):
            name = f"{individual_name['first_name']} {individual_name['last_name']}"
        else:
            name = request["company_name"]
        now = datetime.now(UTC).isoformat()
        data = {
            "id": counterparty_id,
            "name": name,
            "profile_type": request.get("profile_type"),
            "state": "created",
            "created_at": now,
            "updated_at": now,
            "accounts": [
                {
                    "id": account_id,
                    "account_no": request.get("account_no"),
                    "iban": request.get("iban"),
                    "bic": request.get("bic"),
                    "name": name,
                    "bank_country": request.get("bank_country"),
                    "currency": "EUR",
                    "type": "external",
                }
            ],
        }

    return self._simulate_request(
        "add_counterparty", request, data, status_code, dict
    )
create_payment
create_payment(request)
Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def create_payment(
    self,
    request: RevolutRequest,
) -> RevolutObject:
    if self.responses is not None and len(self.responses) > 0:
        status_code, data = self.responses.pop(0)
    else:
        # Simulate a payment
        status_code = 200
        data = {
            "id": str(uuid.uuid4()),
            "state": "pending",
            "created_at": datetime.now(UTC).isoformat(),
            "expected_processing_time": "instant",
        }

    return self._simulate_request(
        "create_payment", request, data, status_code, dict
    )
get_transaction
get_transaction(transaction_id=None, request_id=None)
Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def get_transaction(
    self,
    transaction_id: str | None = None,
    request_id: str | None = None,
) -> RevolutObject:
    if self.responses is not None and len(self.responses) > 0:
        status_code, data = self.responses.pop(0)
    else:
        # Simulate a not found error by default
        status_code = 404
        data = {"message": "Resource not found", "code": 3006}

    return self._simulate_request(
        "get_transaction",
        {"transaction_id": transaction_id, "request_id": request_id},
        data,
        status_code,
        dict,
    )
get_transactions
get_transactions(request)
Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def get_transactions(
    self,
    request: RevolutRequest,
) -> RevolutArray:
    if self.responses is not None and len(self.responses) > 0:
        status_code, data = self.responses.pop(0)
    else:
        # Simulate an empty list by default
        status_code = 200
        data = []

    return self._simulate_request(
        "get_transactions", request, data, status_code, list
    )
responses instance-attribute
responses = responses
rotate_access_token
rotate_access_token()
Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def rotate_access_token(self) -> None:
    # No-op
    pass
track_requests instance-attribute
track_requests = track_requests

add_counterparty

add_counterparty(
    *,
    company_name=None,
    first_name=None,
    last_name=None,
    iban,
    bic,
    bank_country=None,
    address
)

Create a new counterparty in Revolut.

Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def add_counterparty(
    self,
    *,
    # This field must be specified if the counterparty is a company or a hospital
    company_name: str | None = None,
    # These two fields must be specified if the counterparty is an individual
    first_name: str | None = None,
    last_name: str | None = None,
    iban: str,
    bic: str | None,
    bank_country: str | None = None,
    address: BillingAddress | None,
) -> "RevolutCounterparty":
    """Create a new counterparty in Revolut."""
    from shared.services.payment_providers.revolut.openapi.business_1_0 import (
        RevolutBankCountryCode,
        RevolutBeneficiaryAddress,
        RevolutCounterparty,
        RevolutCreateCounterpartyRequest,
        RevolutCurrency,
        RevolutIndividualName,
        RevolutProfileType,
    )

    current_logger.info("Creating Revolut Counterparty...")

    bank_country = bank_country or iban[:2]
    # Revolut counterparty API will fail if company name contains more than 80 characters
    normalized_company_name = self._normalize_name_for_revolut(
        company_name, max_length=80
    )
    # Revolut counterparty API will fail if user names contains more than 40 characters
    normalized_first_name = self._normalize_name_for_revolut(
        first_name, max_length=40
    )
    normalized_last_name = self._normalize_name_for_revolut(
        last_name, max_length=40
    )

    request = RevolutCreateCounterpartyRequest(
        profile_type=RevolutProfileType("business" if company_name else "personal"),
        company_name=normalized_company_name,
        individual_name=RevolutIndividualName(
            first_name=normalized_first_name,
            last_name=normalized_last_name,
        )
        if normalized_first_name and normalized_last_name
        else None,
        bank_country=RevolutBankCountryCode(bank_country),
        currency=RevolutCurrency(self.get_supported_currency()),
    )

    # Revolut does not handle properly new IBAN format from Senegal (at least), so they shared a
    # workaround to avoid rejections: https://alanhealth.slack.com/archives/CPN8N22LU/p1723818151846399
    # For French polynesia we need to send the account_no and bic without the IBAN
    # https://alanhealth.slack.com/archives/CPN8N22LU/p1709893605379069?thread_ts=1709813063.688019&cid=CPN8N22LU
    if bank_country in {"SN", "PF", "NC"}:
        request.account_no = iban
        request.bic = mandatory(
            bic, f"Swift is mandatory for IBAN from {bank_country}"
        )
    else:
        request.iban = iban
        if bic:
            # swift/bic is optional for some countries like SEPA countries
            request.bic = bic

    if address and address.country:
        # We don't have validation on address.country. UK is not a valid country code, it should be GB.
        # This breaks payments to Revolut, so correcting here.
        country = "GB" if address.country == "UK" else address.country

        request.address = RevolutBeneficiaryAddress(
            street_line1=address.street,
            postcode=address.postal_code,
            city=address.city,
            country=country,
        )

    try:
        response = self.api.add_counterparty(
            request.model_dump(mode="json", exclude_none=True)
        )
    except PotentialSwiftError as e:
        if bic:
            raise e
        current_logger.info(
            "Retry to create Revolut Counterparty without BIC",
        )
        request.bic = None
        response = self.api.add_counterparty(
            request.model_dump(mode="json", exclude_none=True)
        )

    counterparty = RevolutCounterparty.model_validate(response)
    current_logger.info(f"Created Revolut Counterparty {counterparty.id}")
    return counterparty

add_counterparty_legacy

add_counterparty_legacy(
    *,
    company_name=None,
    first_name=None,
    last_name=None,
    iban,
    bic,
    bank_country=None,
    address
)
Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def add_counterparty_legacy(
    self,
    *,
    # This field must be specified if the counterparty is a company or a hospital
    company_name: str | None = None,
    # These two fields must be specified if the counterparty is an individual
    first_name: str | None = None,
    last_name: str | None = None,
    iban: str,
    bic: str | None,
    bank_country: str | None = None,
    address: BillingAddress | None,
) -> RevolutCounterPartyInfo:
    counterparty = self.add_counterparty(
        company_name=company_name,
        first_name=first_name,
        last_name=last_name,
        iban=iban,
        bic=bic,
        bank_country=bank_country,
        address=address,
    )
    assert counterparty.accounts is not None, "Counterparty should have accounts"
    assert len(counterparty.accounts) == 1, (
        "Counterparty should have exactly one account"
    )
    return RevolutCounterPartyInfo(
        counterparty_id=str(counterparty.id),
        account_id=str(counterparty.accounts[0].id),
    )

api instance-attribute

api = api

create classmethod

create(*, business_account_name=None)

Normal factory

Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
@classmethod
def create(
    cls,
    *,
    business_account_name: RevolutBusinessAccountName | None = None,
) -> "RevolutBusinessApiClient":
    """Normal factory"""
    from shared.helpers.env import is_development_mode, is_test_mode

    if is_test_mode() or is_development_mode():
        return cls.create_null()

    if business_account_name is None:
        business_account_name = current_config.get("REVOLUT_MAIN_ACCOUNT_NAME")

    if business_account_name is None:
        raise ValueError("Revolut Business account name is not set configured")

    revolut_config = get_revolut_config_from_business_account_name(
        business_account_name
    )

    return cls(
        api=cls.RealApi(
            revolut_config=revolut_config,
        ),
    )

create_null classmethod

create_null(*, track_requests=None, responses=None)
Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
@classmethod
def create_null(
    cls,
    *,
    track_requests: list[TrackedRequest] | None = None,
    responses: list[ConfiguredResponse] | None = None,
) -> "RevolutBusinessApiClient":
    return cls(
        api=cls.StubbedApi(
            track_requests=track_requests,
            responses=responses,
        ),
    )

create_payment

create_payment(
    *,
    request_id,
    account_id,
    receiver_counterparty_id,
    receiver_account_id,
    amount,
    reference=None
)

Trigger a payment using Revolut.

The payment will be sent to the recipient described by receiver_counterparty_id.

Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def create_payment(
    self,
    *,
    # The request_id is controlled by us, and is a string of 40 chars max
    request_id: str,
    account_id: str,
    receiver_counterparty_id: str,
    receiver_account_id: str,
    amount: float,
    reference: str | None = None,
) -> RevolutTransactionPaymentResponse:
    """
    Trigger a payment using Revolut.

    The payment will be sent to the recipient described by receiver_counterparty_id.
    """
    from shared.services.payment_providers.revolut.openapi.business_1_0 import (
        RevolutCurrency,
        RevolutPaymentReceiver,
        RevolutTransactionPaymentRequest,
    )

    if not request_id:
        raise ValueError("request_id is not set")

    payment_currency = self.get_supported_currency()
    request = RevolutTransactionPaymentRequest(
        request_id=request_id,
        account_id=uuid.UUID(account_id),
        receiver=RevolutPaymentReceiver(
            counterparty_id=uuid.UUID(receiver_counterparty_id),
            account_id=uuid.UUID(receiver_account_id),
        ),
        amount=amount,
        currency=RevolutCurrency(payment_currency),
        reference=reference,
    )

    return RevolutTransactionPaymentResponse.model_validate(
        self.api.create_payment(request.model_dump(mode="json", exclude_none=True))
    )

get_supported_currency

get_supported_currency()
Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def get_supported_currency(self) -> str:
    return current_config.get("REVOLUT_CURRENCY", "EUR")  # type: ignore[no-any-return]

get_transaction

get_transaction(*, transaction_id=None, request_id=None)

Get transactions from Revolut.

Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
@retry(
    stop=stop_after_attempt(4),
    retry=retry_if_exception_type(RevolutInternalError),
    wait=wait_fixed(timedelta(milliseconds=5000)),
    reraise=True,
    before_sleep=before_sleep_log(current_logger, logging.INFO),  # type: ignore[arg-type]
)
def get_transaction(
    self,
    *,
    transaction_id: str | None = None,
    request_id: str | None = None,
) -> "RevolutTransaction":
    """Get transactions from Revolut."""
    from shared.services.payment_providers.revolut.openapi.business_1_0 import (
        RevolutTransaction,
    )

    if transaction_id is None and request_id is None:
        raise ValueError("Either transaction_id or request_id must be provided")
    if transaction_id and request_id:
        raise ValueError(
            "Only one of transaction_id or request_id must be provided"
        )

    return RevolutTransaction.model_validate(
        self.api.get_transaction(
            transaction_id=transaction_id,
            request_id=request_id,
        )
    )

get_transactions

get_transactions(
    *,
    transaction_type,
    since,
    until=None,
    count=1000,
    account_id=None
)

Get transactions from Revolut.

Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def get_transactions(
    self,
    *,
    transaction_type: "RevolutTransactionType",  # `type` is reserved
    since: date,
    until: date | None = None,  # included
    count: int = 1000,
    account_id: str | None = None,
) -> list["RevolutTransaction"]:
    """Get transactions from Revolut."""
    from shared.services.payment_providers.revolut.openapi.business_1_0 import (
        RevolutTransaction,
    )

    if until is None:
        until = date.today()

    transactions = []
    for start_date, end_date in months_in_interval(
        start_date=since,
        end_date=until,
    ):
        response = self.api.get_transactions(
            {
                "from": start_date.isoformat(),
                "to": end_date.isoformat(),
                "account": account_id,
                "count": count,
                "type": str(transaction_type),
            }
        )
        current_logger.debug(
            "Call to Revolut API returned %d transactions", len(response)
        )
        for info in response:
            transactions.append(RevolutTransaction.model_validate(info))

    return transactions

parse_transfer_type staticmethod

parse_transfer_type(transaction_id, transfer_type)
Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
@staticmethod
def parse_transfer_type(
    transaction_id: str, transfer_type: str | None
) -> PaymentTransferType | None:
    if transfer_type is None:
        current_logger.debug(
            f"No Revolut transfer type for payment {transaction_id}",
            transaction_id=transaction_id,
        )
        return None
    elif transfer_type == "instant":
        return PaymentTransferType.instant
    elif transfer_type == "standard":
        return PaymentTransferType.standard
    else:
        current_logger.warning(
            f"Unknown Revolut transfer type [{transfer_type}] for payment {transaction_id}",
            transaction_id=transaction_id,
        )
        return None

rotate_access_token

rotate_access_token()
Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def rotate_access_token(self) -> None:
    self.api.rotate_access_token()

RevolutBusinessApiProtocol

Bases: Protocol

add_counterparty

add_counterparty(request)
Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def add_counterparty(
    self,
    request: RevolutRequest,
) -> RevolutObject: ...

create_payment

create_payment(request)
Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def create_payment(
    self,
    request: RevolutRequest,
) -> RevolutObject: ...

get_transaction

get_transaction(*, transaction_id=None, request_id=None)
Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def get_transaction(
    self,
    *,
    transaction_id: str | None = None,
    request_id: str | None = None,
) -> RevolutObject: ...

get_transactions

get_transactions(request)
Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def get_transactions(
    self,
    request: RevolutRequest,
) -> RevolutArray: ...

rotate_access_token

rotate_access_token()
Source code in shared/services/payment_providers/revolut/revolut_business_api_client.py
def rotate_access_token(self) -> None: ...

RevolutErrorResponse module-attribute

RevolutErrorResponse = RevolutObject

RevolutObject module-attribute

RevolutObject = dict[str, Any]

RevolutRequest module-attribute

RevolutRequest = RevolutObject

RevolutResponse module-attribute

RevolutResponse = RevolutObject | RevolutArray

TRANSACTIONS_ENDPOINT module-attribute

TRANSACTIONS_ENDPOINT = 'transactions'

TRANSACTION_ENDPOINT module-attribute

TRANSACTION_ENDPOINT = 'transaction/{}'

TrackedRequest module-attribute

TrackedRequest = tuple[
    str,
    RevolutRequest,
    RevolutResponse | RevolutErrorResponse | None,
]

shared.services.payment_providers.revolut.revolut_webhooks

Decorator for validating Revolut webhook signatures.

Revolut webhooks are signed using HMAC-SHA256. The signature is computed as: 1. Concatenate: version.timestamp.raw_payload 2. Compute HMAC-SHA256 of this string using the signing secret 3. Compare with signature from Revolut-Signature header

See: https://developer.revolut.com/docs/guides/manage-accounts/webhooks/verify-the-payload-signature ⧉

RevolutWebhooks

Decorator for incoming Revolut webhooks.

Validates webhook signatures to ensure requests are authentic.

Usage:

@app.route("/my_revolut_webhook", methods=["POST"])
@RevolutWebhooks.webhook_handler()
def revolut_webhook_controller():
    ...

Tags

is_signature_valid staticmethod

is_signature_valid(signing_secret)

Validate Revolut webhook signature using HMAC-SHA256.

The signature is computed as follows: 1. payload_to_sign = v1.{timestamp}.{raw_payload} 2. hmac_signature = HMAC-SHA256(signing_secret, payload_to_sign) 3. expected_signature = v1={hmac_signature}

Parameters:

Name Type Description Default
signing_secret str

The webhook signing secret from Revolut

required

Returns:

Type Description
bool

True if signature is valid, False otherwise

Source code in shared/services/payment_providers/revolut/revolut_webhooks.py
@staticmethod
def is_signature_valid(signing_secret: str) -> bool:
    """
    Validate Revolut webhook signature using HMAC-SHA256.

    The signature is computed as follows:
    1. payload_to_sign = v1.{timestamp}.{raw_payload}
    2. hmac_signature = HMAC-SHA256(signing_secret, payload_to_sign)
    3. expected_signature = v1={hmac_signature}

    Args:
        signing_secret: The webhook signing secret from Revolut

    Returns:
        True if signature is valid, False otherwise
    """
    signature_header = request.headers.get("Revolut-Signature")
    timestamp_header = request.headers.get("Revolut-Request-Timestamp")

    if not signature_header or not timestamp_header:
        current_logger.warning(
            "Missing signature or timestamp header",
            has_signature=bool(signature_header),
            has_timestamp=bool(timestamp_header),
        )
        return False

    raw_payload = request.get_data(as_text=True)

    # Parse JSON and re-serialize with compact format (no spaces)
    import json

    payload_dict = json.loads(raw_payload)
    compact_payload = json.dumps(payload_dict, separators=(",", ":"))

    version = "v1"
    payload_to_sign = f"{version}.{timestamp_header}.{compact_payload}"

    # Compute HMAC-SHA256
    computed_hmac = hmac.new(
        signing_secret.encode("utf-8"),
        payload_to_sign.encode("utf-8"),
        sha256,
    ).hexdigest()

    # Construct expected signature: v1=hmac_value
    expected_signature = f"{version}={computed_hmac}"

    # Extract signature(s) from header
    # Header format: "v1=signature" or "v1=sig1,v1=sig2" (multiple signatures)
    signatures = [sig.strip() for sig in signature_header.split(",")]

    # Check if expected signature matches any of the provided signatures
    for sig in signatures:
        if hmac.compare_digest(sig, expected_signature):
            return True

    current_logger.warning(
        "Signature mismatch",
        expected_signature=expected_signature,
        received_signatures=signatures,
        payload_to_sign=payload_to_sign,
    )
    return False

webhook_handler staticmethod

webhook_handler()

Handle Revolut webhook requests and validate their signature.

The business account name is extracted from the query parameter to fetch the appropriate signing secret for that account.

Returns:

Type Description
callable

Decorated function that validates webhook signatures

Source code in shared/services/payment_providers/revolut/revolut_webhooks.py
@staticmethod
def webhook_handler() -> callable:  # type: ignore[valid-type]
    """
    Handle Revolut webhook requests and validate their signature.

    The business account name is extracted from the query parameter to fetch
    the appropriate signing secret for that account.

    Returns:
        Decorated function that validates webhook signatures
    """

    def decorator(handler: callable) -> callable:  # type: ignore[valid-type]
        @wraps(handler)
        def decorated_function(*args, **kwargs) -> Response:  # type: ignore[no-untyped-def]
            business_account_name = request.args.get("business_account_name")

            if not business_account_name:
                current_logger.error(
                    "Missing business_account_name query parameter for signature verification"
                )
                return make_response("Missing business account name", 400)

            # Get signing secret for this specific business account
            signing_secret = _get_signing_secret_for_account(business_account_name)

            if not signing_secret:
                current_logger.error(
                    "Revolut signing secret not configured",
                    business_account_name=business_account_name,
                )
                return make_response("Configuration error", 500)

            if not RevolutWebhooks.is_signature_valid(signing_secret):
                current_logger.warning(
                    "Invalid Revolut webhook signature",
                    business_account_name=business_account_name,
                    headers=dict(request.headers),
                )
                return make_response("Invalid signature", 401)

            try:
                response = handler(*args, **kwargs)  # type: ignore[misc]
            except Exception as e:
                current_logger.error(
                    "Revolut webhook handler failed",
                    error=e,
                    error_message=str(e),
                    business_account_name=business_account_name,
                )
                response = make_response("Internal error", 500)

            return response  # type: ignore[no-any-return]

        return decorated_function

    return decorator