Welcome to the Wise for Banks API documentation.
Base URL Sandbox
https://api.sandbox.transferwise.tech
Base URL LIVE
https://api.transferwise.com
What is it?
Building a bank integration with Wise allows a bank to offer Wise transfers directly to their users through their online banking or mobile apps.
What are the benefits for my bank?
How does it work?
See what some of our bank partners have to say
An example native user experience based on the Wise Platform API
Please also see our Design Guide for more examples and information on how we recommend to design your integration. This resource is useful to see how the flow should work from a user point of view, along with "dos" and "don'ts" for each screen in the flow.
Please visit our Postman collection for more details and examples of how to use our API, including how to implement payment initiation, user linking, creation and others.
Please contact banks@wise.com to get started.
This guide describes in detail the technical implementation of a Wise integration for a bank. If you need any help please reach out to the Wise team here.
Please visit our Postman collection for more details and examples of how to use our API, including how to implement payment initiation, user linking, creation and others.
Wise uses standard OAuth 2.0 protocol for authentication and authorization.
Once our partnership begins, we’ll send you API access credentials for the sandbox environment consisting of a Client ID and a Client Secret. The credentials are needed to either create users over API or complete the authorization_code OAuth 2.0 grant type through which the customer will allow your application access to their account.
The Client secret is a very sensitive piece of data as it could be used to impersonate your institution on the Wise Platform API. It should be handled and stored with the upmost care, seen by as few people as possible and stored in a secure secret storage solution, preferably away from any other Wise data such as user API access tokens.
We also need redirect_url from your technical team which is used to forward users to after successfully granting your application access to their Wise account. Specifying this explicitly makes the integration more secure. This article about OAuth 2.0 framework is a great way to refresh your knowledge about the protocol itself.
You will build your Wise user experience directly into your mobile and desktop applications, and will build a backend service to support the features it offers. Your user interface should never directly call any authenticated Wise endpoint which requires an API token, this should always be done by your backend system.
Depending on the features and settlement model you will build, there are some different components you will need to build. Please ask your Wise implementation team for advice based on the requirements of your integration.
There are different ways to build your user experience, especially when it comes to the sequence of steps in the payment flow, but we have a recommended order that is described in our Integration Design Guide. We strongly recommend you follow this flow as it has been tuned by the Wise team to be the simplest to understand for the customer and the easiest to build using the APIs defined below.
We are happy to help you to design and build a great experience for your customers using our experience so please don't hesitate to get in touch if you need advice, especially if you want to deviate from the recommended flow.
You should expose an API internally for your web and mobile clients to call to provide the required Wise features. Your backend system will manage both communication to the Wise Platform API and internal operations such as querying user KYC data to send to Wise, checking a user has sufficient funds to make the requested transfer and triggering the payment of funds to Wise when a user confirms a transfer.
You should also store a copy of certain data relating to Wise to decrease latency and increase resiliency when users review previous transfers they have made or recipients they sent funds to. The extent of what you store will depend on your integration, but we recommend mend to store at a minimum:
The goal is to store locally in your platform all the data you need to drive your UI, such that calling our API is not required when reviewing historic data.
You should subscribe to our webhooks to keep this data up to date.
We have a dedicated team focusing on bank partnerships who will help you along the way, sharing knowledge and experience from previous integrations to help you build a robust and highly available system.
There are two main user flows that must be built in order to integrate with Wise.
There are two ways to access the Wise Platform API depending on if you customer already has a Wise account or not. You should handle both cases in your integration.
This flowchart describes the different scenarios you will encounter and how you should handle them.
You need to go through this flow only once for each customer before they can set up their first transfer.
Upon linking to an existing Wise account you need to ensure that you have connected to an account that represents the same natural person or business of the bank account in your platform. To do this for personal profiles please check the date of birth of the connected Wise profile matched the date of birth you hold for that customer in your platform. For businesses the comparison required changes with the region you are servicing, please discuss the best approach with your implementation team.
To create transfers on behalf of users you need these building blocks:
If a user doesn't already have a Wise account then you can create one for them. The signup with registration code feature lets you create new users directly via an API call. You will send via API all of the data Wise needs to serve these users in your region, meaning users have their accounts created without ever leaving your banking app, making a very streamlined flow.
You will define a registration_code
for the user that act similarly to a password, although is limited in scope to be used only by your integration over the API. You exchange this value for user tokens, as described in the detailed documentation above. This code can be used to regenerate tokens should they become invalid so you should save it in your database to allow this. Due to the password-like nature of this data we recommend to store it encrypted at rest for security.
We can provide this option to banks where we can create a trusted reliance or outsourcing model on your KYC processes.
Below is a sequence diagram showing this flow.
If you attempt to create a user that already has a Wise account they will always need to be redirected to the account linking flow, you can detect this at the point you attempt to create the user based on the API response of 409 conflict. See the detailed guide under the endpoint documentation for more details.
Note that these new users have to accept the Wise Terms and Conditions as part of their sign up process, which should be available for them to read as a link to our website or using the endpoint Terms and conditions..
At a high level there are two steps to gaining access to an existing Wise account.
There are two possible ways to get an authorization code — by opening the Wise website, having the user login and agree to connect accounts and then redirect back to your app, or by us sending an email to the end user with a link to login to Wise and the user then manually typing a code in to your app. The redirection flow is always preferred as it requires no manual user actions. In some cases it is not possible to securely open the Wise website within your app, in which case the email flow can be used. Before using the email flow please discuss the implications with your implementation teams.
The standard website redirection flow is as follows:
https://www.yourbank.com/transferwise-link-page/?code=[CODE]&profileID=123
These steps are explained in more detail below.
Your website or app opens the following url in the user's browser.
Sandbox:
https://sandbox.transferwise.tech/oauth/authorize/?client_id={clientId}&redirect_uri={redirectUri}
Production:
https://wise.com/oauth/authorize/?client_id={clientId}&redirect_uri={redirectUri}
List of available parameters:
Parameter | Description |
---|---|
client_id (required) | The client ID you received from us for sandbox or production. |
redirect_uri (required) | The pre-configured URL in your application where users will be sent after authorization. This can itself include query string parameters in order to pass through data required by your system to create the Wise account link, such as user ID. |
state | An opaque value, used for security purposes. If this parameter is set in the request, then it is returned to the application as part of the redirect_uri. More about state parameter. |
On mobiles apps you should not use legacy WebView
components to show the authorization page to the users because they are not secure and will not allow users to log in to Wise with Google, which is an option used by many of our users. Your app should instead open the device's full browser app or use the newer iOS and Android features to show isolated web pages within apps in a secure way, namely a SafariViewController or a ChromeCustomTab, respectively.
Please also note that provided [CODE]
expires within 30 minutes and is one time use only.
Our usual log in screens are presented to the user if they are not already logged in on the browser being used. If enabled for a user they will also be prompted to go through our two-factor authentication procedure.
Once a user gives your banking app authorization to connect to Wise and access their data, the user is redirected back to your redirect_url with a generated code query string value. For example
https://www.yourbank.com/transferwise-link-page/?code=[CODE]&profileID=123
Your website or service can then use this code to obtain the access token to act on behalf of the user account described in the Exchange an authorization code for API tokens section below.
If you are building your Wise integration as a native mobile phone app then the redirect URL should be able to handle returning the user to the correct place in the app, using a "deep link" based on a custom URL scheme defined by your mobile app.
If the website based flow is not possible for you then you can request for Wise to email the customer with a link for them to get an authorization code. This enables the user linking flow but avoids having to redirect the user to an external website. The flow is more cumbersome for a user so should only be used as a last resort. To enable this flow, you must send the sendAuthorizationCodeAsEmail
parameter as true when calling the POST /v2/users
endpoint. Where this is true, and a user already exists for the email you try to create, then we will send the user an email with a link to access the Wise authorization page, which will show them the authorization code. The user would then type this code in to your app, rather than you receive it via a redirect.
In the event of a 409 response the flow should be as follows:
These steps are explained in more detail below.
Once triggered, your UI should instruct the user to find the email and follow the flow.
You should also give the user a text field to type in the authorization code, it will be six characters, alphanumeric.
As an example, below are examples of the links that will be included in the email to the user. Note the forwardingMode=showCode
parameter included in the URL. This ensures the
Sandbox:
https://sandbox.transferwise.tech/oauth/authorize/?client_id={clientId}&redirect_uri={redirectUri}&forwardingMode=showCode
Production:
https://wise.com/oauth/authorize/?client_id={clientId}&redirect_uri={redirectUri}&forwardingMode=showCode
Our usual log in screens are presented to the user if they are not already logged in on the browser being used. If enabled for a user they will also be prompted to go through our two-factor authentication procedure.
The user will then be shown the authorization screen, where they will see your logo and a list of permissions they are granting to your application. They will be asked to approve.
Once approved, the user will then be presented with the six character authorization code, with instructions that they must type in the code to your app directly above the code.
Please also note that provided code expires within 30 minutes and is one time use only.
The user then manually types the code in to your app, and submit it to your backend platform, which can then use this code to obtain the access token to act on behalf of the user account, as described in the next section.
Example Request:
curl \
-u '[your-api-client-id]:[your-api-client-secret]' \
-d 'grant_type=authorization_code' \
-d 'client_id=[your-api-client-id]' \
-d 'code=[code-from-redirect-uri]' \
-d 'redirect_uri=https://www.yourbank.com' \
'https://api.sandbox.transferwise.tech/oauth/token'
You will be returned an access token and a refresh token.
Example Response:
{
"access_token":"ba8k9935-62f2-475a-60d8-6g45377b4062",
"token_type":"bearer",
"refresh_token":"a235uu9c-9azu-4o28-a1kn-e15500o151cx",
"expires_in": 43199,
"scope":"transfers"
}
The final step is to use the authorization code and exchange it for customer tokens.
See the example request to the right to exchange an access code for API tokens.
Upon linking to an existing Wise account you need to ensure that you have connected to an account that represents the same natural person or business of the bank account in your platform. To do this for personal profiles please check the date of birth of the connected wise profile matched the date of birth you hold for that customer in your platform. For businesses the comparison required changes with the region you are servicing, please discuss the best approach with your implementation team.
When calling API endpoints you need to provide the user's access_token
in the request's HTTP header in the format Authorization: Bearer <access_token>
.
Access tokens are short lived for security reasons, they are only valid for 12 hours by default. When they expire you need to use the refresh_token
to generate a new access_token.
This means you have to securely store the user's refresh_token
in order to generate a new access_token
each time they use your Wise integration. We recommend storing all tokens encrypted when at rest in tour database, to reduce the impact of any data breach.
Use the refresh token to generate a new access token using the following API call.
POST https://api.sandbox.transferwise.tech/oauth/token
Use Basic Authentication with your api-client-id/api-client-secret as the username/password. The body of the request must be sent as x-www-form-urlencoded.
Field | Description | Format |
---|---|---|
grant_type | "authorization_code" | Text |
client_id | your api_client_id | Text |
code | Code provided to you upon redirect back from authorization flow. See Get user authorization. | Text |
redirect_uri | Redirect page associated with your api client credentials | Text |
Field | Description | Format |
---|---|---|
access_token | Access token to be used when calling API endpoints on behalf of user. Valid for 12 hours. | Text |
token_type | "bearer" | Text |
refresh_token | Refresh token which you need to use in order to request new access_token. The lifetime of refresh tokens is 20 years. | Text |
expires_in | Expiry time in seconds | Integer |
scope | "transfers" | Text |
Below is an end-to-end sequence diagram for connecting to an existing Wise user.
Example Request:
curl \
'https://api.sandbox.transferwise.tech/oauth/token' \
-u '[your-api-client-id]:[your-api-client-secret]' \
-d 'grant_type=refresh_token' \
-d 'refresh_token=[user refresh token]'
Example Response:
{
"access_token":"be69d566-971e-4e15-9648-85a486195863",
"token_type":"bearer",
"refresh_token":"1d0ec7b9-b569-426d-a18d-8dead5b6a3cc",
"expires_in":43199,
"scope":"transfers",
"created_at": "2022-08-11T19:07:46.421141182Z"
}
Access tokens are designed to expire after a shot period of time, representing a login session to the Wise Platform API. This increases security of the user's account in case the token is leaked.
In order to maintain an uninterrupted connection, you can request a new access token whenever the previous one is close to expiring. There is no need to wait for the actual expiration to happen first.
POST https://api.sandbox.transferwise.tech/oauth/token
Use Basic Authentication with your api-client-id/api-client-secret as the username/password. The body of the request must be sent as x-www-form-urlencoded.
Field | Description | Format |
---|---|---|
grant_type | "refresh_token" | Text |
refresh_token | User's refresh_token obtained from creating or linking to a TW user. | Text |
Field | Description | Format |
---|---|---|
access_token | Access token to be used when calling API endpoints on behalf of user. Valid for 12 hours. | Text |
token_type | "bearer" | Text |
refresh_token | Refresh token which you need to use in order to request new access_token once the existing one expires | Text |
expires_in | Expiry time in seconds | Integer |
scope | "transfers" | Text |
It is also possible that a user's refresh token will become invalid. This could happen for a number of reasons, for example:
Due to this possibility your application should handle the scenario where tou fail to generate a new access token from the refresh token. Correctly handling this depends on how you originally gained access to the user.
If you were granted access by an existing user then you should send the user through the same flow as you initially did to generate tokens described in linking to an existing Wise account. You will then have new access and refresh tokens generated which you can now store and use as before.
In the case you created the user using the user creation over API flow then the mechanism for regenerating tokens is dependent on whether the user you created has "reclaimed" their Wise account and used our website or apps directly, which they may have done by following a secure process on our website or apps.
If the user has not reclaimed their account then the original registration_code
you generated should still be able to generate new tokens for the user. Because of this you should store this code alongside the created user ID in your database at the point of user generation, encrypted for security purposes.
If the previously stored token fails with an error code 400 and error:
{
"error": "invalid_grant",
"error_description": "Invalid user credentials."
}
then you can assume the user has reclaimed the account and push them through the linking to an existing Wise account flow.
The next step is to ensure Wise is sent the user's profile data, which identifies that person or business based on data such as name, date of birth, business registration number, etc.
When you first get access to a user's Wise account you cannot predict if they already have submitted their profile data to Wise or not.
The User Profiles.List endpoint will give you data for both personal and business profiles, if they exist. This makes it easy to figure out if a user has already set up this data with Wise or not. If the user already has a personal profile set up, then you can skip this creation step.
If you created the user over API then you always need to create a personal profile for the user, however it is possible you will also need to do it when getting access to an existing user account should we have incomplete data, as noted above.
There are several steps to creating a new personal user profile:
Create personal user profile – general data. This includes customer name, date of birth, and phone number.
Create personal user profile – upload verification ID data. If required for your integration (dependent on jurisdiction) then send ID document information for your user. The Wise integration team will work on this with you to decide if it is required.
Open update window. Open the update window for the profile updates.
Create personal user profile – address data. Add address information to the personal user profile.
Close update window. Close the update window for the profile updates.
A personal profile has to be created first. You can’t create a business user profile without a personal profile.
Creating a business profile is similar to how you created a personal profile. There are six steps:
Create business user profile – general data. This includes business name, type and other information.
Open update window. Open the update window for the profile updates.
Create business user profile – address data. Add address information to the business user profile.
Create business user profile - director data. Add business director information to the business user profile.
Create business user profile - UBO data. Add ultimate business owner information to the business user profile.
Close update window. Close the update window for the profile updates.
Some regions demand more profile data due to local regulations, in order to be compliant the extra data has to be provided. This is required based on jurisdiction of operation and customer demographics of your bank or other financial institution, please discuss with your implementation team to see if it is necessary to include the following behaviour. To collect this data please use our User Profile Extensions dynamic form endpoint.
Once you have access to a user and their profile is set up, you can then allow them to continue to the transfer flow in order to start making international transfers.
The next time the user comes to use Wise, you do not need to go through the user onboarding flow again. You can use the refresh tokens you have stored in your database in order to generate an access token and allow the user to access the transfer flow immediately.
A quote represents how much money a user wants to send, plus the real exchange rate and fees offered to them by Wise.
Please look at Create quote under Full API Reference.
You must include the profile ID when creating the quote using the profile
parameter.
The next thing is to choose who will receive the funds, either by selecting an existing recipient bank account the user previously created by or the user entering data to create a new one.
Please look at Create recipient account under Full API Reference for information on the calls to create recipients, using our dynamic from solution. Currently we return an email
recipient type for all currencies but this is not appropriate for the bank/FI use case, please filter it out in your client to remove it.
Use the GET accounts
endpoint to load the user's previously used recipients and allow them to select from them in your user interface. This allows them to only have to create a recipient once and then re-use it in future, plus it allows existing Wise users to use their familiar recipients from our platform.
You should store a cached copy of the recipients that are used by users of your app such that you can load that data again quickly to show in your UI, for example a transfer tracking screen might show recipient data. You could either store the entire data or just the name
and accountSummary
of the recipient.
Please note, when creating a new transfer always reload the full list of recipients over our API rather than use the ones you have saved because you cannot be certain the recipients that you store a copy of have not been deleted on Wise in the meantime.
Now that a recipient has been selected you need to update the quote with that recipient in order for us to calculate if the price has changed based on that selection. For example the fees and delivery estimate are different in the case of sending USD to a country other than the USA - we call this Global USD.
You need to do this in order to show the customer the correct final price and delivery estimate time before they commit to creating and funding the transfer.
Please look at Update quote under the full API Reference to learn how to do this.
The final API step is to create the transfer, using the quote and selected recipient account ID
Please look at Create transfer under the full API Reference for information on creating a transfer using your quote and recipient.
There are several settlement mechanisms offered by Wise. The most simple is to trigger a bank transfer or wire directly from the customer's account to our bank account in your currency. We also offer bulk settlement models, which are documented separately. Below describes the direct transfer by transfer funding mechanism.
Once you have successfully created a transfer via the Wise Platform API you should debit the exact source amount from your customer's bank account and send the funds to Wise’s local bank account via a domestic payment. You should send the amount provided in the final quote object after any recipient updates for global currencies using PATCH quote. The details of this bank account will be shared with you by the Wise team helping your integration.
In order for us to link this incoming domestic payment with a corresponding transfer order, we need you to use specific text in the "payment reference" field. Get the transfer id
for the transfer you're trying to fund and append the letter T
as a prefix. So for a hypothetical transfer with id 80106743
, the correct "payment reference" would be T80106743
. Include this string in the reference when sending the funds to Wise's bank account.
Alternatively, we offer a "prefunded" model to some banks who want to speed up transfers for their customers in regions where the domestic payment networks are slow. If you are interested in this model please get in touch with the Wise team to discuss the option and for technical documentation on how it works.
You should use a subscription to application webhooks to keep the statuses of your local transfer data storage up to date. The transfers will move through the following statuses in the happy path.
Incoming Payment Waiting ⇒ Processing ⇒ Funds Converted ⇒ Outgoing Payment Sent
Outgoing Payment Sent is the final state of the normal flow. If the payment fails, the following problematic flow will occur. An example would be if the recipient bank account doesn’t exist or the data is entered incorrectly and the payment is returned to Wise. The problematic state flow of transfers is:
Outgoing Payment Sent ⇒ Bounced Back ⇒ Processing ⇒ Cancelled ⇒ Funds Refunded
Most bounce backs occur within 2-3 business days, however this could happen up to several weeks after a transfer is sent.
Transfer state flow
NB: Transfers support rollback transitions, it allows return transfer back to one of previous states.
See below for the full list of transfer statuses and what they mean in the order of occurrence:
incoming_payment_waiting – The transfer has been submitted and it’s waiting for the funds to arrive with Wise.
incoming_payment_initiated - The funding has been initiated but the money has not yet arrived to Wise's account.
processing – We have received the funds and are processing the transfer. Processing is a generic term and means we’re doing behind-the-scene activities before the money gets sent to the recipient, such as AML, compliance and fraud checks.
funds_converted – All compliance checks have been completed for the transfer and funds have been converted from the source currency to the target currency.
outgoing_payment_sent – Wise has paid out funds to the recipient. This is the final state of the transfer, assuming funds will not be returned. When a transfer has this state, it doesn’t mean the money has arrived in the recipient’s bank account, just that we have sent it from ours. Note: Payment systems in different countries operate in different speeds and frequency. For example, in the UK, the payment will reach the recipient bank account within few minutes after we send the outgoing payment. However, in the US it usually takes a day until funds are received.
cancelled – This status is used when the transfer was never funded and therefore never processed. This is a final state of the transfer.
funds_refunded – Transfer has been refunded. This is a final state of the transfer.
bounced_back – Transfer has bounced back but has not been cancelled nor refunded yet. This is not a final transfer state, it means the transfer will either be delivered with delay or it will turn to funds_refunded state.
charged_back - This status is used when we have problem to debit payer's account or payer requested money back. Chargeback can happen from any other state.
unknown - This status is used when we don’t have enough information to move the transfer into a final state. We send out an email for more information. e.g. Sender account details to refund money back.
Keep in mind the transfer statuses in our API have different names than what you’ll see on our website or app. That’s because we use more consumer friendly language in the front end of our products.
For example "Completed" on our website means outgoing_payment_sent
in the API.
You should use the following descriptions in your website or app for the potential statuses we return:
Status | Description |
---|---|
incoming_payment_waiting | "On its way to Wise" |
incoming_payment_initiated | "On its way to Wise" |
processing | "Processing" |
funds_converted | "Processing" |
outgoing_payment_sent | "Sent" |
charged_back | "Charged back" |
cancelled | "Cancelled" |
cancelled_refund_processing | "Refund in progress" |
funds_refunded | "Refunded" |
bounced_back | "Bounced back" |
unknown | "Unknown" |
Sandbox limitations
We don't send out email notifications to customers about payment status changes in sandbox.
We don't process payments in sandbox, which means that created payments remain in their first state. You can use Simulation endpoints to change transfer statuses in sandbox.
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v1/delivery-estimates/{transferId} \
-H "Authorization: Bearer <your api token>"
Example Response:
{
"estimatedDeliveryDate" : "2018-01-10T12:15:00.000+0000"
}
Get the live delivery estimate for a transfer by the transfer ID. The delivery estimate is the time at which we currently expect the transfer to arrive in the beneficiary's bank account. This is not a guaranteed time, but we’re working hard to make these estimates as accurate as possible.
GET https://api.sandbox.transferwise.tech/v1/delivery-estimates/{transferId}
You need to save the transfer id to track its status later.
Field | Description | Format |
---|---|---|
estimatedDeliveryDate | Estimated time when funds will arrive to recipient's bank account | Timestamp |
We require yearly updates on the personal or business profiles, which can be sent when a user accesses the Wise integration and you generate an access token. If any user data has changed within the previous year then the profile information at Wise must be updated, or you must let us know nothing has changed. Change of address, name change due to marriage, etc. are some common scenarios when this data may change. If easier, you can send this data with every transfer rather than yearly.
The way this needs to be implemented is the following:
Open update window. Open the update window for profile updates.
Update personal profile - general data. Update the personal profile data.
Update personal profile - address data. Update address information of the personal profile.
Close update window. Close the update window for profile updates.
Open update window. Open the update window for profile updates.
Update business profile - general data. Update the business profile data.
Update business profile - address data. Update address information of the business profile.
Update business profile - directors data. Update directors information in the business user profile.
Update business profile - UBO data. Update ultimate business owners information in the business user profile.
Close update window. Close the update window for profile updates.
There are several valid approaches on how to implement this from the bank side.
We recommend storing the most recent date that you updated the data for each personal/business profile and each time you get an access token for that user you can check against this date whether it's been more than one year since the profile was last updated. If it has been over a year, you should open the update window, perform the update profile steps and then close the update window.
Do keep in mind that we require the update window to be opened and closed even if there were no changes to the profiles. This will essentially let us know that nothing has changed for the profile and that the information is up to date.
This section discusses some edge cases that you should test and handle before going live with your integration.
Due to how getting access to user accounts works the Wise platform relies on user email addresses matching between the bank and ourselves. At the point the bank attempts to create a user we check and see if an account already exists for that email address, if so we return a 409 response and the client application forwards the user to login to Wise to do the OAuth grant flow.
This works well when the email addresses match in the first place and aren't updated on either side after the link is established. Of course, this is not always going to be the case so we must consider what happens in either eventuality.
If a user already has a Wise account and you create a user for the same person under a different email address they could end up with a duplicate user account under the second email address. Currently we monitor this behaviour for abuse but we are working on a more robust user creation solution to prevent this from occurring.
It is possible to change a user’s email address both at Wise and potentially also on the bank platform. These flows can cause complications with the integration.
If a user changes their email address, all tokens to the user account are revoked. In this case the bank will receive a 400 when attempting to generate an access_token and as such should follow the same process as described in the token expiry section above and start the sign up flow from the beginning.
In this case, if the user has changed their email address at Wise, it is possible the user will end up with a new Wise account using their old email address still held by the bank, or they might link their bank account to a different already existing Wise account under the old email address.
In this case the tokens will remain valid for the Wise account, however, depending on how the user originally linked the account, different things can happen when/if that token expires.
If the bank created the account originally, they will be unable to generate tokens using the registration_code
they have, as the endpoint requires the email address which will now no longer match. To mitigate this it is recommended that the bank store the email that was originally used for signup alongside the registration code and use this rather than the most up to date email address they store for the user.
If the token expires for a user not created by the bank and the user has a new email address at the bank then they can be pushed through the signup flow with this new email address and either have a new account created or link an existing against the new email, as described in token expiry.
The result of many of these flows is that the user may end up with more than one Wise account, which is undesirable. Currently we monitor this behaviour for abuse but we are working on a more robust user creation scenario to prevent this from occurring.
The result of these eventualities are that over time a user of the bank could be linked to more than one Wise account and so therefore you will need to be defensive when requesting older user data as the request may fail because we forbid one user to access other user's data. We recommend to keep a local copy of your user's transfer data and update it asynchronously such that older transfers remain accessible to the user in the case where it can no longer be accessed. You should also make sure to handle these failing calls gracefully and continue to process transfers that can be accessed over the API.
In the event a user is not happy at losing access to their older data or having two accounts is confusing then we can manually update the email addresses to match for the two accounts they want.
refreshRequirementsOnChange
.refreshRequirementsOnChange
.Global Currencies is a Wise product that allows customers to send currencies to countries other than the one they are domestic to. For example, sending US dollars to a person or business in Hong Kong, rather than the USA.
It is quite common, especially for businesses, to operate in USD, GBP or EUR despite being based outside of the respective country of these currencies. In some countries, these bank accounts do not use the standard local account details format that a domestic account would, for example an account number and sort code in UK for GBP. Instead, these accounts are more likely to be addressed using an IBAN or an account number plus a BIC/SWIFT code.
The Wise Platform API supports this product in version two (v2) of the quotes API. The key difference this API provides against version one is the ability to update a quote with a recipient before a transfer is created, using the update quote endpoint. If you set the quote's targetAccount
to be the ID of, for example, a USD recipient with an account number and SWIFT code (i.e. an account with "type": "swift_code"
) then the returned quote will be updated to represent the fact we will not be sending these funds domestically in the USA and as such charge different fees for the transfer. You should highlight this change in fees from the original quote to your user, after calling the update quote endpoint.
In addition to USD, EUR also has a swift_code
recipient type, labelled "Outside Europe". This recipient type allows customers to send EUR to recipients with an account number and BIC/SWIFT code, however it is also possible to send EUR outside of the eurozone using the iban
recipient type by entering a non-SEPA IBAN.
If you do not wish to offer these recipient types to your customers please discuss with your implementation team the best way to prevent them appearing.
In addition, if you offer this product, you must be sure to display a message to customers that there are potentially additional extra fees for this type of transfer. For example:
To send this transfer, we need to use the SWIFT network, which is more expensive and slower than normal transfers. There may also be extra fess taken from the money by intermediary banks. We cannot guarantee the exact amount that will be received by your recipient.
There are three cases you need to cover where we will use the SWIFT network and the message needs to be displayed:
swift_code
, to any currency (currently only USD and EUR).iban
where the IBAN does not start with GB
iban
where the IBAN does not start with one of the SEPA country codes (i.e. the transfer is to a non-SEPA country).The process is as follows:
Create a quote for the currencies you wish to send between, e.g. GBP to USD.
Create a swift_code
recipient for using the create account endpoint, based on the requirements set out in account requirements.
Update the quote created in step 1 with the ID of the recipient created in step two.
You will now see the payOut
field of the quote to have updated to be of type SWIFT
, and the paymentOptions
array to have all options updated to also have a payOut
of type SWIFT
and different fees. The payOut
field informs you which paymentOption
to select, in this case you should show the user the fees to pay using the option that has a pay out of SWIFT
. You should use the payIn
option that has previously been agreed up, by default this is BANK_TRANSFER
unless you are using a prefunded or bulk settlement model.
The Wise Platform API is continuously evolving as we offer new features and coverage to our API customers. Here we explain how our API versioning is maintained so you know what to expect.
It's important to us that third-party integrations are not adversely affected by changes and we endeavor to uphold these standards as part of making convenience and transparency part of our company's mission. We are regularly reviewing our policies to make sure we're delivering the best possible API developer experience.
These policies apply to both our REST API and our webhooks (push-based event API).
A breaking change refers to any change that would require a client to update their application in order to continue working with the API as originally intended. If an API field or resource is removed or renamed, then a breaking change has taken place. In this case we will increment the version of the affected API endpoints to prevent breaking existing customer integrations.
Under our current policy API endpoints are not all versioned together, if API endpoint compatibility has changed in the new version as a result of a breaking change we will provide clear instructions in our documentation on which API calls must be used together.
Wise reserves the right to make additive changes to our API without incrementing the version number or notifying clients. We may add new resources, fields, and relationships to an existing version of the API and these will not be considered breaking changes.
For example, if we add a new relationship to the Transfer resource for a “parent_order”, we will neither bump the API version nor notify our customers before releasing the update. We will, however, update our API documentation explaining the purpose of the changes.
As such, clients should design their applications to be flexible enough to not break when new fields are added to resources.
It is not standard policy for Wise to remove or disable API versions, and we will not take this action lightly. However in some cases it may be necessary; for example if the affected API does not meet new regulatory requirements and there is no alternative to making a breaking change and disabling an old API.
In the extremely rare case that this is necessary we will not remove API endpoints without notice to clients who may be affected, and formal warning of at least 6 months as long as this complies with our regulatory obligations.
Having issues while calling our API endpoints?
Check our API status at https://status.transferwise.com/
If you would like to receive status change notifications, feel free to signup here.
Have a technical question about API?
Send email to api@wise.com
Have a question about how Wise works?
Search Wise Help Centre. https://wise.com/help
Talk to the bank integrations team
Please get in touch with our team at banks@wise.com
Tips and advice for designing the international payments experience with the Wise API
We've created a Bank Integrations Design Guide to help you design a great experience for your customers that's similar to what we offer on our own website and apps. These guidelines go into detail to explain the user journey, the purpose of each screen, why we suggest you include a certain screen or feature and the customer interaction on each screen.
For technical documentation for building the integration, and more information about how our API works, see our Bank Integrations Technical Guide.
Before getting started with designing your international payments experience using the Wise API, it's important to know that there are multiple ways to do it, depending on your users' needs. This documentation should be used as a reference to decide the best approach.
It's important to think about your users and Wise, there are three possible types:
These users will need to create a Wise account in order to use the international payments integration. You can create a Wise account for them behind the scenes automatically, without any user interaction.
These users will need to connect their existing Wise account to your platform. They will be prompted and guided through how to do it and be taken to Wise to log in and grant you access to allow the connection.
These users formerly were of type one or two, but are now returning users. They are already set up to make international payments using Wise from your platform and do not have to go through the account creation or connecting flows again.
This is a step-by-step view of the different screens a user may see throughout their journey. We've detailed each step further down the page.
These are examples of screens to include in your flow. Each example includes a design and information about the screen's purpose. We also included tips for what to do and not do when designing each screen.
This screen will be what your user first sees when they want to send a payment using your app or platform. The screen should have an option for them to make an international payment. It should be accessible from your app's payment or account screen.
After the customer chooses to make an international payment, they will first see a screen that lets them choose their currency. Once they make that choice, they will be taken to a screen that displays what we call the calculator.
The calculator allows a user to enter an amount they want to send in their currency, which reveals a quote from our API — the quote includes the amount the recipient will receive in their currency based on the live mid-market exchange rate, the included sending fee and the estimated delivery time.
Additionally, the calculator should allow the user to type in a specific amount for the recipient to receive, the Wise quote API will return the calculation of the exact amount to send.
The screen also is an easy way for a user to switch between different currencies to see different exchange rates and costs.
Jump to Step 5 if the user already connected their Wise account
This screen is to check to see if the user has an existing Wise account. You should use the email address stored on the user's bank profile to determine if they have an existing Wise account using an endpoint provided by our API.
There are two screens that need to be designed — one for customers that do have a Wise account, and one those that do not.
This will be what the user sees on our platform when they choose to connect their accounts.
This step looks identical to step 2, except now the user is logged in to Wise. All details previously entered in step 2 will be pulled through from the logged out to the logged in state. The screen's main purpose is to confirm with the user what they entered in step 2.
The data for this screen comes from the Wise API. It will be available once the customer creates or signs in to their Wise account. It should only show recipients for the currency that the user has selected in the previous step. The API supports this filtering.
Sometimes we need extra information about the transfer from your customer. This form is built dynamically based on how Wise defines the information that you need. Your system discovers this using the transfer-requirements feature of the API. This screen often isn't needed, but should be dynamically added to the flow when it is.
This is the screen that lets the user review and confirm their transfer details, and press send. It should always include the amount being sent and to whom they are sending it to.
Once the transfer has been made, the user should be able to see confirmation. The user should also be able to easily access and review the details of the transfer.
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v1/users/{userId} \
-H "Authorization: Bearer <your api token>"
Example Response:
{
"id": 101,
"name": "Example Person",
"email": "person@example.com",
"active": true,
"details": {
"firstName": "Example",
"lastName": "Person",
"phoneNumber": "+37111111111",
"occupation": "",
"address": {
"city": "Tallinn",
"countryCode": "EE",
"postCode": "11111",
"state": "",
"firstLine": "Road 123"
},
"dateOfBirth": "1977-01-01",
"avatar": "https://lh6.googleusercontent.com/photo.jpg",
"primaryAddress": 111
}
}
Get authenticated user details by user id. Response includes also personal user profile info.
GET https://api.sandbox.transferwise.tech/v1/users/{userId}
Field | Description | Format |
---|---|---|
id | userId | Integer |
name | User full name | Text |
User email | Text | |
active | If user is active or not | Boolean |
details.firstName | User first name | Text |
details.lastName | User lastname | Text |
details.phoneNumber | Phone number | Text |
details.dateOfBirth | Date of birth | YYYY-MM-DD |
details.occupation | Person occupation | Text |
details.avatar | Link to person avatar image | Text |
details.primaryAddress | Address object id to use in addesses endpoints | Integer |
details.address.countryCode | Address country code in 2 digits. "US" for example | Text |
details.address.firstLine | Address first line | Text |
details.address.postCode | Address post code | Text |
details.address.city | Address city name | Text |
details.address.state | Address state code State code. Required if country is US, CA, AU, BR. | Text |
details.address.occupation | User occupation. Required for US, CA, JP | Text |
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v1/me \
-H "Authorization: Bearer <your api token>"
Example Response:
{
"id": 101,
"name": "Example Person",
"email": "person@example.com",
"active": true,
"details": {
"firstName": "Example",
"lastName": "Person",
"phoneNumber": "+37111111111",
"occupation": "",
"address": {
"city": "Tallinn",
"countryCode": "EE",
"postCode": "11111",
"state": "",
"firstLine": "Road 123"
},
"dateOfBirth": "1977-01-01",
"avatar": "https://lh6.googleusercontent.com/photo.jpg",
"primaryAddress": 111
}
}
Get authenticated user details for the user's token submitted in the Authorization
header. Response includes also personal user profile info.
GET https://api.sandbox.transferwise.tech/v1/me
Field | Description | Format |
---|---|---|
id | userId | Integer |
name | User full name | Text |
User email | Text | |
active | If user is active or not | Boolean |
details.firstName | User first name | Text |
details.lastName | User lastname | Text |
details.phoneNumber | Phone number | Text |
details.dateOfBirth | Date of birth | YYYY-MM-DD |
details.occupation | Person occupation | Text |
details.avatar | Link to person avatar image | Text |
details.primaryAddress | Address object id to use in addesses endpoints | Integer |
details.address.countryCode | Address country code in 2 digits. "US" for example | Text |
details.address.firstLine | Address first line | Text |
details.address.postCode | Address post code | Text |
details.address.city | Address city name | Text |
details.address.state | Address state code State code. Required if country is US, CA, AU, BR. | Text |
details.address.occupation | User occupation. Required for US, CA, JP | Text |
- Example Request: Get Client Credentials Token
curl -X "POST" "https://api.sandbox.transferwise.tech/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Authorization: Basic <base64 encoded clientId:clientSecret>" \
--data-urlencode "grant_type=client_credentials"
- Example Response: Get Client Credentials Token
{
"access_token":"ba8k1234-00f2-475a-60d8-6g45377b4062",
"token_type":"bearer",
"expires_in": 43199,
"scope":"transfers"
}
- Example Request: Create User
curl -X POST https://api.sandbox.transferwise.tech/v2/users \
-H "Authorization: Bearer <your client credentials token>" \
-H "Content-Type: application/json" \
-d '{
"email": <user email>,
"registrationCode": <registration code>,
"sendAuthorizationCodeAsEmail":false,
"language":"PT"
}'
- Example Response: Create User (Success (200) user created successfully)
{
"id": 12345,
"name": null,
"email": "new.user@domain.com",
"active": true,
"details": null
}
- Example Response: Create User (Failure (409): User already exists)
{
"errors": [
{
"code": "NOT_UNIQUE",
"message": "You’re already a member. Please login",
"path": "email",
"arguments": [
"email",
"class com.transferwise.fx.api.ApiRegisterCommand",
"existing.user@domain.com"
]
}
]
}
- Example Request: Get User Tokens
curl --location --request POST 'https://api.sandbox.transferwise.tech/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--user '[your-api-client-id]:[your-api-client-secret]' \
--data-urlencode 'grant_type=registration_code' \
--data-urlencode 'client_id=[your-api-client-id]' \
--data-urlencode 'email=<user email>' \
--data-urlencode 'registration_code=<registration code used to create user>'
- Example Response: Get User Tokens (Success: 200)
{
"access_token": "01234567-89ab-cdef-0123-456789abcdef",
"token_type": "bearer",
"refresh_token": "01234567-89ab-cdef-0123-456789abcdef",
"expires_in": 43199,
"scope": "transfers"
}
- Example Response: Get User Tokens (Failure: 400 - User reclaimed the account or invalid registration code used)
{
"error": "invalid_grant",
"error_description": "Invalid user credentials."
}
These endpoints enable onboarding new users to Wise via backend API calls only.
To get authorization from existing Wise users, you need to redirect them to our authorization webpage or have the user manually input and authorization code.
There are 3 steps you need to go through:
Obtain your application level access_token
based on your API client credentials.
POST https://api.sandbox.transferwise.tech/oauth/token
Use Basic Authentication with your client id and client secret as the username/password basic auth.
NOTE: The body of the request must be sent as x-www-form-urlencoded.
Field | Description | Format |
---|---|---|
grant_type | "client_credentials" | Text |
Field | Description | Format |
---|---|---|
access_token | Access token to be used when calling "create user" endpoint. Valid for 12 hours. | Text |
token_type | "bearer" | Text |
expires_in | Expiry time in seconds | Integer |
scope | "transfers" | Text |
Call this API to create the user on Wise. We use the email address as unique identifier for users.
If email is new (there is no active user already) then new user will be created.
When you are submitting an email which already exists amongst our users then you will get HTTP 409 response. If the user already exists, then you need to revert to the linking an existing Wise account flow, choosing one of the two options to link the account - a redirect to our website or en email based flow with the user manually typing in the authorization code.
POST https://api.sandbox.transferwise.tech/v2/users/
Use access_token
obtained in first step as authentication header.
Field | Description | Format |
---|---|---|
New user's email address | ||
registrationCode | Randomly generated registration code that is unique to this user. At least 32 characters long. You need to store registration code to obtain access token on behalf of this newly created user in next step. Please apply the same security standards to handling registration code as if it was a password. |
Text, min length is 32 chars |
sendAuthorizationCodeAsEmail | Set this variable as true if you want Wise to send an email to your customer with the authorizationCode, rather than having to redirect them to our website in the case of a duplicate user (409 CONFLICT). | |
language (Optional) | User default language for UI and email communication. Allowed values EN, US, PT, ES, FR, DE, IT, JA, RU, PL, HU, TR, RO, NL, HK. Default value EN. |
Text, 2 chars |
Field | Description | Format |
---|---|---|
id | userId | Integer |
name | User full name. Empty. | Text |
Customer email | Text | |
active | true | Boolean |
details | User details. Empty. | Group |
You can now use registration code to obtain user access token and refresh token.
This step can be repeated as long as user does not "reclaim" their Wise account, by coming to Wise and securely setting their own password on the account in order to access it directly.
If user has reclaimed the account, which can be detected by this API call failing with an authentication error code 400 then revert to the linking an existing Wise account flow should be used instead.
Refresh user access token works same way for this flow as well.
POST https://api.sandbox.transferwise.tech/oauth/token
Use Basic Authentication with your api-client-id/api-client-secret as the username/password. The body of the request must be sent as x-www-form-urlencoded.
Field | Description | Format |
---|---|---|
grant_type | "registration_code" | Text |
New user's email address | ||
client_id | Your api_client_id | Text |
registration_code | registrationCode from step 2 | Text |
Field | Description | Format |
---|---|---|
access_token | Access token to be used when calling API endpoints on behalf of user. Valid for 12 hours. | Text |
token_type | "bearer" | Text |
refresh_token | Refresh token which you need to use in order to request new access_token. The lifetime of refresh tokens is 20 years. | Text |
expires_in | Expiry time in seconds | Integer |
scope | "transfers" | Text |
POST https://api.sandbox.transferwise.tech/v2/users/exists
Note that this uses a client-credentials-token
and not a user access_token
for authentication.
Field | Description | Format |
---|---|---|
User's email address |
Field | Description | Format |
---|---|---|
exist | User exists | Boolean |
Example Request
curl -X POST https://api.sandbox.transferwise.tech/v2/users/exists \
-H "Authorization: Bearer <your client credentials token>" \
-H "Content-Type: application/json" \
-d '{
"email": <user email>,
}'
Example Response
{
"exists": true
}
Example Request:
curl -X POST https://api.sandbox.transferwise.tech/v1/profiles \
-H "Authorization: Bearer <your api token>" \
-H "Content-Type: application/json" \
-d '{
"type": "personal",
"details": {
"firstName": "Oliver",
"lastName": "Wilson",
"dateOfBirth": "1977-07-01",
"phoneNumber": "+3725064992",
"firstNameInKana": null,
"lastNameInKana": null
}
}'
Example Response (Personal):
{
"id": <your personal profile id>,
"type": "personal",
"details": {
"firstName": "Oliver",
"lastName": "Wilson",
"dateOfBirth": "1977-07-01",
"phoneNumber": "+3725064992",
"avatar": "",
"occupation": "",
"occupations": null,
"primaryAddress": null,
"firstNameInKana": null,
"lastNameInKana": null
}
}
Create personal user profile. One person cannot have multiple active duplicate user profiles, creating multiple profiles with the same details will fail.
POST https://api.sandbox.transferwise.tech/v1/profiles
Field | Description | Format | Validation rules (java regex ) |
---|---|---|---|
type | "personal" | Text | Must be "personal". |
details.firstName | First name (including middle names) | Text (max 30 chars) | Must not contain: %#^@{}"!~<>\ . ((^[^%#^@{}"!~<>\\]+$) ) |
details.lastName | Last name | Text (max 30 chars) | Must not contain: %#^@{}"!~<>\ . ((^[^%#^@{}"!~<>\\]+$) ) |
details.dateOfBirth | Date of birth | YYYY-MM-DD | Must be YYYY-MM-DD format. |
details.phoneNumber | Phone number | Text | Must be a valid phone number, validated through Google's phone number library. |
details.firstNameInKana | First name in Katakana (required for from JPY personal transfers) | Text (Katakana) | Must be in katakana with double width space or standard spaces only. ([\p{InKatakana} \u3000]+ ) |
details.lastNameInKana | Last name in Katakana (required for from JPY personal transfers) | Text (Katakana) | Must be in katakana with double width space or standard spaces only. ([\p{InKatakana} \u3000]+ ) |
If a customer you are creating a profile for has first or last names that exceed 30 characters (e.g. they have many middle names) then you should truncate the names at length 30 characters and submit that value.
Field | Description | Format |
---|---|---|
id | profileId | Integer |
type | "personal" | Text |
details.firstName | First name | Text |
details.lastName | Last name | Text |
details.dateOfBirth | Date of birth | YYYY-MM-DD |
details.phoneNumber | Phone number | Text |
details.avatar | Link to person avatar image | Text |
details.occupation (Deprecated) | Person occupation | Text |
details.occupations | Array of occupations, currently one FREE_FORM occupation is supported, required as described above. | Array (can be null or empty) |
details.occupations[n].code | User occupation, any value permitted. | Text |
details.occupations[n].format | Occupation type - always FREE_FORM |
Text |
details.primaryAddress | Address object id | Integer |
details.firstNameInKana | First name in Katakana (required for from JPY personal transfers) | Text (Katakana) |
details.lastNameInKana | Last name in Katakana (required for from JPY personal transfers) | Text (Katakana) |
Example Request:
curl -X POST https://api.sandbox.transferwise.tech/v1/profiles \
-H "Authorization: Bearer <your api token>" \
-H "Content-Type: application/json" \
-d '{
"type": "business",
"details": {
"name": "ABC Logistics Ltd",
"registrationNumber": "12144939",
"acn": null,
"abn": null,
"arbn": null,
"companyType": "LIMITED",
"companyRole": "OWNER",
"webpage": "https://abc-logistics.com",
"businessCategory":"CONSULTING_IT_BUSINESS_SERVICES",
"businessSubCategory":"DESIGN"
}
}'
Example Response (Business):
{
"id": <your business profile id>,
"type": "business",
"details": {
"name": "ABC Logistics Ltd",
"registrationNumber": "12144939",
"acn": null,
"abn": null,
"arbn": null,
"companyType": "LIMITED",
"companyRole": "OWNER",
"descriptionOfBusiness": "Information and communication",
"webpage": "https://abc-logistics.com",
"primaryAddress": 123456, //ID of placeholder
"businessCategory": "CONSULTING_IT_BUSINESS_SERVICES",
"businessSubCategory": "DESIGN"
}
}
Create business user profile. You must always create a personal profile first, business profiles cannot be created without personal profile.
POST https://api.sandbox.transferwise.tech/v1/profiles
Field | Description | Format | Validation Rules (java regex) |
---|---|---|---|
type | "business" | Text | Always "business" |
details.name | Business name | Text | Must not contain: %#^@{}"!~<>\ . ((^[^%#^@{}"!~<>\\]+$) ) |
details.registrationNumber | Business registration number | Text | 1 to 30 characters. |
details.acn | Australian Company Number (only for Australian businesses) | Text | Must be valid ACN. |
details.abn | Australian Business Number (only for Australian businesses) | Text | Must be valid ABN. |
details.arbn | Australian Registered Body Number (only for Australian businesses) | Text | Must be valid ARBN. |
details.companyType | Company legal form. Allowed values:
|
Text | Must be one of the enum values. |
details.companyRole | Role of person. Allowed Values:
|
Text | Must be one of the enum values. |
details.descriptionOfBusiness | Sector / filed of activity (DEPRECATED) | Text | Deprecated, use businessCategory and set this as null . |
details.webpage (conditional) | Business webpage. Required if companyType is OTHER | Text | Valid URL. (^(?i)\\b(https?://)?((?=[a-z0-9-]{1,63}\\.)[a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,63}\\b(/.*)*$ ) |
details.businessCategory | Type of business, see below for permitted values | Text | One of the below Business Category values. |
details.businessSubCategory | Specific sub category of the business type, see below for permitted values | Text | A valid subcategory of the selected businessCategory . |
Ensure when submitting a business profile that you submit a category and associated sub-category from the list below. You should map from the information you have about the business to one of our categories and sub-categories. If this is problematic please get in touch with us to discuss alternate solutions.
The categories and their sub-categories are as follows:
Field | Description | Format |
---|---|---|
id | profileId | Integer |
type | "business" | Text |
details.name | Business name | Text |
details.registrationNumber | Business registration number | Text |
details.acn | Australian Company Number (only for AUS businesses) | Text |
details.abn | Australian Business Number (only for AUS businesses) | Text |
details.arbn | Australian Registered Body Number (only for AUS businesses) | Text |
details.companyType | Company legal form | Text |
details.companyRole | Role of person | Text |
details.webpage | Business webpage | Text |
details.primaryAddress | Address object id | Integer |
details.businessCategory | Type of business | Text |
details.businessSubCategory | Specific sub category of the business type | Text |
Example Request:
curl -X PUT https://api.sandbox.transferwise.tech/v1/profiles \
-H "Authorization: Bearer <your api token>" \
-H "Content-Type: application/json" \
-d '{
"id": <your personal profile id>
"type": "personal",
"details": {
"firstName": "Oliver",
"lastName": "Wilson",
"dateOfBirth": "1977-07-01",
"phoneNumber": "+3725064992",
"firstNameInKana": null,
"lastNameInKana": null
}
}'
Example Response:
{
"id": <your personal profile id>,
"type": "personal",
"details": {
"firstName": "Oliver",
"lastName": "Wilson",
"dateOfBirth": "1977-07-01",
"phoneNumber": "+3725064992",
"avatar": "",
"occupation": "",
"occupations": null,
"primaryAddress": null,
"firstNameInKana": null,
"lastNameInKana": null
}
}
Update user profile information.
If user profile has been verified then there are restrictions on what information is allowed to change, where permitted, use the update window functionality to submit updated data.
PUT https://api.sandbox.transferwise.tech/v1/profiles
Request and response is same as described in Create (Personal) and Create (Business)
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v1/profiles/{profileId} \
-H "Authorization: Bearer <your api token>"
Example Response:
{
"id": <your personal profile id>,
"type": "personal",
"details": {
"firstName": "Oliver",
"lastName": "Wilson",
"dateOfBirth": "1977-07-01",
"phoneNumber": "+3725064992",
"avatar": "",
"occupation": "",
"primaryAddress": null,
"firstNameInKana": null,
"lastNameInKana": null
}
}
Get profile info by id.
GET https://api.sandbox.transferwise.tech/v1/profiles/{profileId}
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v2/profiles \
-H "Authorization: Bearer <your api token>"
Example Response:
[
{
"type": "PERSONAL",
"id": 16155137,
"userId": 5682283,
"address": {
"addressFirstLine": ".......",
"city": "London",
"countryIso2Code": "GB",
"countryIso3Code": "gbr",
"postCode": "ABC123",
"stateCode": null
},
"email": "",
"createdAt": "2021-03-25T15:18:35.000Z",
"updatedAt": "2021-03-25T15:18:35.000Z",
"obfuscated": false,
"currentState": "VISIBLE",
"firstName": "Oliver",
"lastName": "Wilson",
"dateOfBirth": "1967-04-03",
"phoneNumber": "+442038087139",
"secondaryAddresses": [],
"fullName": "Oliver Wilson"
},
{
"type": "BUSINESS",
"id": 16155138,
"userId": 5682283,
"address": {
"addressFirstLine": ".......",
"city": "London",
"countryIso2Code": "GB",
"countryIso3Code": "gbr",
"postCode": "ABC123",
"stateCode": null
},
"email": "",
"createdAt": "2021-03-25T15:18:38.000Z",
"updatedAt": "2021-03-25T15:18:38.000Z",
"obfuscated": false,
"currentState": "VISIBLE",
"businessName": "ABC Logistics Ltd",
"registrationNumber": "07209813",
"descriptionOfBusiness": "IT_SERVICES",
"companyType": "LIMITED",
"companyRole": "OWNER",
"operationalAddresses": [],
"fullName": "ABC Logistics Ltd"
}
]
List of all profiles belonging to user.
Please note that there might be more than one business profile returned in the response.
GET https://api.sandbox.transferwise.tech/v2/profiles
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v1/profiles \
-H "Authorization: Bearer <your api token>"
Example Response:
[
{
"id": <your personal profile id>,
"type": "personal",
"details": {
"firstName": "Oliver",
"lastName": "Wilson",
"dateOfBirth": "1977-07-01",
"phoneNumber": "+3725064992",
"avatar": "",
"occupation": "",
"occupations": null,
"primaryAddress": null,
"firstNameInKana": null,
"lastNameInKana": null
}
},
{
"id": <your business profile id>,
"type": "business",
"details": {
"name": "ABC Logistics Ltd",
"registrationNumber": "12144939",
"acn": null,
"abn": null,
"arbn": null,
"companyType": "LIMITED",
"companyRole": "OWNER",
"descriptionOfBusiness": "CHARITY_AND_NOT_FOR_PROFIT",
"webpage": "https://abc-logistics.com",
"primaryAddress": null,
"businessCategory": "CHARITY_AND_NOT_FOR_PROFIT",
"businessSubCategory": "CHARITY_ALL_ACTIVITIES"
}
}
]
List of all profiles belonging to user.
GET https://api.sandbox.transferwise.tech/v1/profiles
Example Request:
curl -X POST https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/verification-documents \
-H "Authorization: Bearer <your api token>" \
-H "Content-Type: application/json" \
-d '{
"firstName": "Oliver",
"lastName": "Wilson",
"type": "IDENTITY_CARD",
"uniqueIdentifier": "AA299822313",
"issueDate": "2017-12-31",
"issuerCountry": "EE",
"issuerState": "",
"expiryDate": "2027-12-31"
}'
Example Response:
{
"errorMessage": null,
"success": true
}
Add identification document details to user profile. Applicable to personal profiles (not business) only.
Returns empty result if successful.
When sending a social security number (SSN) only type
and uniqueIdentifier
(only 9 digits no letters or symbols) are required.
POST https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/verification-documents
Field | Description | Format |
---|---|---|
firstName | Person first name in document. | Text |
lastName | Person last name in document. | Text |
type (conditional) | Document type. Allowed Values:
|
Text |
uniqueIdentifier (required) | Document number. Only digits when SSN. | Text |
issueDate | Document issue date. | YYYY-MM-DD |
issuerCountry | Issued by country code. For example "US". | Text |
issuerState | Issued by state code. For example "NY". | Text |
expiryDate | Document expiry date. | YYYY-MM-DD |
nationality | 2 characters ISO country code. | Text |
employerName | The name of the employer. Type must be EMIRATES_EMPLOYER. | Text |
employerCity | The city of the employer. Type must be EMIRATES_EMPLOYER. | Text |
employerCountry | 2 characters ISO country code. Type must be EMIRATES_EMPLOYER. | Text |
birthCity | The city of birth of the customer. Type must be EMIRATES_PLACE_OF_BIRTH | Text |
birthCountry | 2 characters ISO country code. Type must be EMIRATES_PLACE_OF_BIRTH | Text |
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/directors \
-H "Authorization: Bearer <your api token>"
Example Response:
[
{
"id": 10,
"firstName": "John",
"lastName": "Doe",
"dateOfBirth": "1982-05-20",
"countryOfResidenceIso3Code": "usa"
},
{
"id": 11,
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "1981-12-07",
"countryOfResidenceIso3Code": "usa"
}
]
Returns the list of all directors associated with the business profile.
GET https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/directors
Example Request:
curl -X POST https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/directors \
-H "Authorization: Bearer <your api token>" \
-H "Content-Type: application/json" \
-d '[
{
"firstName": "John",
"lastName": "Doe",
"dateOfBirth": "1982-05-20",
"countryOfResidenceIso3Code": "usa"
},
{
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "1981-12-07",
"countryOfResidenceIso3Code": "usa"
}
]'
Example Response:
[
{
"id": 10,
"firstName": "John",
"lastName": "Doe",
"dateOfBirth": "1982-05-20",
"countryOfResidenceIso3Code": "usa"
},
{
"id": 11,
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "1981-12-07",
"countryOfResidenceIso3Code": "usa"
},
{
"id": 7,
"firstName": "Oliver",
"lastName": "Wilson",
"dateOfBirth": "2017-12-31",
"countryOfResidenceIso3Code": "gbr"
}
]
Adds new directors to the business profile. Returns the list of all directors associated with the business profile.
POST https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/directors
Field | Description | Format |
---|---|---|
firstName | Director first name | Text |
lastName | Director last name | Text |
dateOfBirth | Date of birth | YYYY-MM-DD |
countryOfResidenceIso3Code | 3 character country code | Text |
Example Request:
curl -X PUT https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/directors \
-H "Authorization: Bearer <your api token>" \
-H "Content-Type: application/json" \
-d '[
{
"firstName": "John",
"lastName": "Doe",
"dateOfBirth": "1982-05-20",
"countryOfResidenceIso3Code": "usa"
},
{
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "1981-12-07",
"countryOfResidenceIso3Code": "usa"
}
]'
Example Response:
[
{
"id": 14,
"firstName": "John",
"lastName": "Doe",
"dateOfBirth": "1982-05-20",
"countryOfResidenceIso3Code": "usa"
},
{
"id": 15,
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "1981-12-07",
"countryOfResidenceIso3Code": "usa"
}
]
Overrides directors in the business profile. Returns the list of all directors associated with the business profile.
PUT https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/directors
Field | Description | Format |
---|---|---|
firstName | Director first name | Text |
lastName | Director last name | Text |
dateOfBirth | Date of birth | YYYY-MM-DD |
countryOfResidenceIso3Code | 3 character country code | Text |
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/ubos \
-H "Authorization: Bearer <your api token>"
Example Response:
[
{
"id": "013ab1c2688d0185b582ee7e0bcb28b2",
"name": "John Doe",
"dateOfBirth": "1982-05-20",
"countryOfResidenceIso3Code": "usa",
"addressFirstLine": "123 Fake St",
"postCode": "FK 12345",
"ownershipPercentage": 30
},
{
"id": "912ce3f31c8b3a10572137e78417caa3",
"name": "Jane Doe",
"dateOfBirth": "1981-12-07",
"countryOfResidenceIso3Code": "usa",
"addressFirstLine": "125 Fake St",
"postCode": "FK 12545",
"ownershipPercentage": 70
}
]
Returns the list of all ultimate beneficial owners associated with the business profile.
GET https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/ubos
Example Request:
curl -X POST https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/ubos \
-H "Authorization: Bearer <your api token>" \
-H "Content-Type: application/json" \
-d '[
{
"name": "John Doe",
"dateOfBirth": "1982-05-20",
"countryOfResidenceIso3Code": "usa",
"addressFirstLine": "123 Fake St",
"postCode": "FK 12345",
"ownershipPercentage": 30
},
{
"name": "Jane Doe",
"dateOfBirth": "1981-12-07",
"countryOfResidenceIso3Code": "usa",
"addressFirstLine": "125 Fake St",
"postCode": "FK 12545",
"ownershipPercentage": 40
}
]'
Example Response:
[
{
"id": "f3e71aa1c97448d0b1eb5bdc0bacdcce",
"name": "John Doe",
"dateOfBirth": "1982-05-20",
"countryOfResidenceIso3Code": "usa",
"addressFirstLine": "123 Fake St",
"postCode": "FK 12345",
"ownershipPercentage": 30
},
{
"id": "c6008d58a1664413b4c4dcacec1377f4",
"name": "Jane Doe",
"dateOfBirth": "1981-12-07",
"countryOfResidenceIso3Code": "usa",
"addressFirstLine": "125 Fake St",
"postCode": "FK 12545",
"ownershipPercentage": 40
},
{
"id": "63bbdd1cf5ec4dd587597e74dbace376",
"name": "Oliver Wilson",
"dateOfBirth": "2017-12-31",
"countryOfResidenceIso3Code": "gbr",
"addressFirstLine": "222 Fake St",
"postCode": "FK 22222",
"ownershipPercentage": 30
}
]
Adds new ultimate beneficial owners to the business profile. Returns the list of all ultimate beneficial owners associated with the business profile.
Note that in some cases, we do not require the ownershipPercentage
. In these cases, null
should be passed as the value.
POST https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/ubos
Field | Description | Format |
---|---|---|
name | Owner full name | Text |
dateOfBirth | Date of birth | YYYY-MM-DD |
countryOfResidenceIso3Code | 3 character country code | Text |
addressFirstLine | First line of address | Text |
postCode | Address post code | Text |
ownershipPercentage | Percentage of ownership | Integer |
Example Request:
curl -X PUT https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/ubos \
-H "Authorization: Bearer <your api token>" \
-H "Content-Type: application/json" \
-d '[
{
"name": "John Doe",
"dateOfBirth": "1982-05-20",
"countryOfResidenceIso3Code": "usa",
"addressFirstLine": "123 Fake St",
"postCode": "FK 12345",
"ownershipPercentage": 30
},
{
"name": "Jane Doe",
"dateOfBirth": "1981-12-07",
"countryOfResidenceIso3Code": "usa",
"addressFirstLine": "125 Fake St",
"postCode": "FK 12545",
"ownershipPercentage": 70
}
]'
Example Response:
[
{
"id": "ff01cf3f206b40c090a14a1e51163e9e",
"name": "John Doe",
"dateOfBirth": "1982-05-20",
"countryOfResidenceIso3Code": "usa",
"addressFirstLine": "123 Fake St",
"postCode": "FK 12545",
"ownershipPercentage": 30
},
{
"id": "c36b687d28ad44ad8c3864411f5f2612",
"name": "Jane Doe",
"dateOfBirth": "1981-12-07",
"countryOfResidenceIso3Code": "usa",
"addressFirstLine": "125 Fake St",
"postCode": "FK 12545",
"ownershipPercentage": 70
}
]
Overrides ultimate beneficial owners in the business profile. Returns the list of all ultimate beneficial owners associated with the business profile.
PUT https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/ubos
Field | Description | Format |
---|---|---|
name | Owner full name | Text |
dateOfBirth | Date of birth | YYYY-MM-DD |
countryOfResidenceIso3Code | 3 character country code | Text |
addressFirstLine | First line of address | Text |
postCode | Address post code | Text |
ownershipPercentage | Percentage of ownership | Integer |
Example Request:
curl -X DELETE https://api.sandbox.transferwise.tech/v3/profiles/{profileId}/trusted-verification \
-H "Authorization: Bearer {client-credentials-token}"
Example Response (204):
No Content
This endpoint allows partners to remove the verification that was given to the profile through them creating the profile. This does not delete a profile nor archive it, it simply removes the trusted verification from that partner.
Note that this uses a client-credentials-token
and not a user access_token
for authentication.
Example Request:
curl -X POST https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/update-window \
-H "Authorization: Bearer <your api token>"
Example Response:
Opens the update window for updating the profile information: details, addresses, directors, owners, others.
POST https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/update-window
Example Request:
curl -X DELETE https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/update-window \
-H "Authorization: Bearer <your api token>"
Example Response:
Deletes the update window for updating the profile.
DELETE https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/update-window
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/extension-requirements \
-H "Authorization: Bearer <your api token>"
Example Response:
[
{
"type": "profile-extensions-requirements",
"usageInfo": null,
"fields": [
{
"name": "Tell us what you're using TransferWise for",
"group": [
{
"key": "accountPurpose",
"name": "Account Purpose",
"type": "select",
"refreshRequirementsOnChange": false,
"required": true,
"displayFormat": null,
"example": null,
"minLength": null,
"maxLength": null,
"validationRegexp": null,
"validationAsync": null,
"valuesAllowed": [
{
"key": "CONTRIBUTING_TO_PERSONAL_SAVINGS",
"name": "Contributing to personal savings"
},
{
"key": "GENERAL_MONTHLY_LIVING_EXPENSES",
"name": "General monthly living expenses"
},
{
"key": "INVESTING_IN_FUNDS_STOCKS_BONDS_OPTIONS_FUTURES_OR_OTHER",
"name": "Investing in funds stocks bonds options futures or other"
},
{
"key": "PAYING_FOR_GOODS_OR_SERVICES_ABROAD",
"name": "Paying for goods or services abroad"
},
{
"key": "PAYING_RENT_MORTGAGE_BANK_LOAN_INSURANCE_CREDIT",
"name": "Paying rent mortgage bank loan insurance credit"
},
{
"key": "PAYING_RENT_UTILITIES_OR_PROPERTY_CHARGES",
"name": "Paying rent utilities or property charges"
},
{
"key": "RECEIVE_SALARY_IN_DIFFERENT_CURRENCY",
"name": "Receive salary in different currency"
},
{
"key": "RECEIVE_PENSION_IN_DIFFERENT_CURRENCY",
"name": "Receive pension in different currency"
},
{
"key": "SENDING_MONEY_REGULARLY_TO_FAMILY",
"name": "Sending money regularly to family"
},
{
"key": "SENDING_MONEY_TO_MY_OWN_ACCOUNT_TO_BENEFIT_FROM_EXHCANGE_RATE",
"name": "Sending money to my own account to benefit from exchange rate"
}
]
}
]
}
]
}
]
After having a profile created, in some situations we can need more specific information about it. In order to know which fields are required for a given profile, and to send the information over, we expose a few endpoints:
GET https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/extension-requirements
POST https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/extension-requirements
and
POST https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/extensions
GET https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/extensions
The GET
and POST
profile extension-requirements endpoints help you to figure out which fields are required to create a valid profile for different regions. You can use this data to build a dynamic user interface on top of these endpoints.
The POST
and GET
profile extensions endpoints allow you to send the extra profile information and retrieve it, respectively.
This format for dynamic forms is the same as the one used for recipient creation. See Recipient.Requirements and Using account requirements
This is a step-by-step guide on how these endpoints work.
First create a profile. See User Profiles Create (Personal) and User Profiles Create (Business)
Call GET /v1/profiles/{profileId}/extension-requirements
to get the list of fields you need to fill with values in the "details" section for adding information that will make a profile valid.
Some fields require multiple levels of fields in the details request. This should be handled by the client based on the refreshRequirementsOnChange
field. A top level field can have this field set to true, indicating that there are additional fields required depending on the selected value. To manage this you should create a request with all of the initially requested data and call the POST extension-requirements
endpoint. You will be returned a response similar the previously returned data from GET extension-requirements
but with additional fields.
Once you have built your full profile extension details object you can add it to add information to the profile.
This is a valid request to add information to a profile:
POST v1/profiles/{profileId}/extensions
{
"details": {
"accountPurpose": "SENDING_MONEY_REGULARLY_TO_FAMILY"
}
}
When requesting the form data from the extension-requirements
endpoint, the response defines different types of extensions that can be added. Each extension type then has multiple fields describing the form elements required to be shown to collect information from the user. Each field will have a type value, these tell you the field type that your front end needs to render to be able to collect the data. A number of field types are permitted, these are:
type | UI element |
---|---|
text | A free text box |
select | A selection box/dialog |
radio | A radio button choice between options |
date | A text box with a date picker |
Example data is also included in each field which should be shown to the user, along with a regex or min and max length constraints that should be applied as field level validations. You can optionally implement the dynamic validation using the validationAsync
field, however these checks will also be done when a completed profile extension is submitted to POST /v1/profiles/{profileId}/extensions
.
Field | Description | Format |
---|---|---|
type | "profile-extensions-requirements" | Text |
fields[n].name | Field description | Text |
fields[n].group[n].key | Key is name of the field you should include in the JSON | Text |
fields[n].group[n].type | Display type of field (e.g. text, select, etc) | Text |
fields[n].group[n].refreshRequirementsOnChange | Tells you whether you should call POST extension-requirements once the field value is set to discover required lower level fields. | Boolean |
fields[n].group[n].required | Indicates if the field is mandatory or not | Boolean |
fields[n].group[n].displayFormat | Display format pattern. | Text |
fields[n].group[n].example | Example value. | Text |
fields[n].group[n].minLength | Min valid length of field value. | Integer |
fields[n].group[n].maxLength | Max valid length of field value. | Integer |
fields[n].group[n].validationRegexp | Regexp validation pattern. | Text |
fields[n].group[n].validationAsync | Validator URL and parameter name you should use when submitting the value for validation | Text |
fields[n].group[n].valuesAllowed[n].key | List of allowed values. Value key | Text |
fields[n].group[n].valuesAllowed[n].name | List of allowed values. Value name. | Text |
Example Request:
curl -X POST https://api.sandbox.transferwise.tech/v1/addresses \
-H "Authorization: Bearer <your api token>" \
-H "Content-Type: application/json" \
-d '{
"profile": <your profile id>,
"details": {
"country": "EE",
"firstLine": "Narva mnt 5-1",
"postCode": "10113",
"city": "Tallinn",
"occupations": [
{
"code": "Software Engineer",
"format": "FREE_FORM"
}
]
}
}'
Example Response:
{
"id": 236532,
"profile": <your profile id>,
"details": {
"country": "EE",
"firstLine": "Narva mnt 5-1",
"postCode": "10113",
"city": "Tallinn",
"state": "",
"occupation": null,
"occupations": [
{
"code": "Software Engnieer",
"format": "FREE_FORM"
}
]
}
}
Adds address info to user profile. List of required fields are different for different countries.
State field is required for US, CA, BR and AU addresses.
Occupations is required for CA, IN, JP and within the US for the states AZ and NM. If you serve any of these jurisdictions we recommend submitting occupation for all customers for simplicity, however if this is an issue we can discuss alternative solutions.
POST https://api.sandbox.transferwise.tech/v1/addresses
Field | Description | Format | Validation Rules |
---|---|---|---|
profile | User profile id. | Integer | ID of the user's personal or business profile |
details.country | 2 digit ISO country code. | Text | Two digit ISO code |
details.firstLine | Address line: street, house, apartment. | Text |
|
details.postCode | Zip code | Text (max 30 chars) | Some country selections will check for valid post codes, e.g. Canada |
details.city | City name | Text | Is not "PO Box" |
details.state | State code. Required if country is US, CA, AU, BR | Text | Is valid state code for selected country (only required for US, BR, AU and CA) |
details.occupation (Deprecated) | User occupation. Required as described above. | Text | This is deprecated - use the occupations field below |
details.occupations | Array of occupations, currently one FREE_FORM occupation is supported, required as described above. | Array (can be null or empty) | Array |
details.occupations[n].code | User occupation | Text | Any value permitted |
details.occupations[n].format | Occupation type | Text | Always FREE_FORM |
Field | Description | Format |
---|---|---|
id | Address id | Integer |
profile | User profile id. | Integer |
details.country | 2 digit ISO country code. | Text |
details.firstLine | Address line: street, house, apartment. | Text |
details.postCode | Zip code | Text |
details.city | City name | Text |
details.state | State code. Required if country is US, CA, AU, BR | Text |
details.occupation (Deprecated) | User occupation. Required for US, CA, JP | Text |
details.occupations | details.occupations | Array of occupations, currently one FREE_FORM occupation is supported, required as described above. |
details.occupations[n].code | User occupation, any value permitted. | Text |
details.occupations[n].format | Occupation type - always FREE_FORM |
Text |
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v1/addresses/{addressId} \
-H "Authorization: Bearer <your api token>"
Example Response:
{
"id": 236532,
"profile": <your profile id>,
"details": {
"country": "EE",
"firstLine": "Narva mnt 5-1",
"postCode": "10113",
"city": "Tallinn",
"state": "",
"occupation": null,
"occupations": null
}
}
Get address info by id.
GET https://api.sandbox.transferwise.tech/v1/addresses/{addressId}
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v1/addresses?profile={profileId} \
-H "Authorization: Bearer <your api token>"
Example Response:
[
{
"id": 7099091,
"profile": <your profile id>,
"details": {
"country": "EE",
"firstLine": "Veerenni 29",
"postCode": "12991",
"city": "Tallinn",
"state": null,
"occupation": null,
"occupations": null
}
}
]
List of addresses belonging to user profile.
GET https://api.sandbox.transferwise.tech/v1/addresses?profile={profileId}
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v1/address-requirements \
-H "Authorization: Bearer <your api token>"
Example Response:
[
{
"type": "address",
"fields": [
{
"name": "Country",
"group": [
{
"key": "country",
"type": "select",
"refreshRequirementsOnChange": true,
"required": true,
"displayFormat": null,
"example": "Germany",
"minLength": null,
"maxLength": null,
"validationRegexp": null,
"validationAsync": null,
"valuesAllowed": [
{
"key": "AX",
"name": "Åland Islands"
},
...
{
"key": "ZM",
"name": "Zambia"
}
]
}
]
},
{
"name": "City",
"group": [
{
"key": "city",
"type": "text",
"refreshRequirementsOnChange": false,
"required": true,
"displayFormat": null,
"example": "London",
"minLength": null,
"maxLength": null,
"validationRegexp": null,
"validationAsync": null,
"valuesAllowed": null
}
]
},
{
"name": "Postal code",
"group": [
{
"key": "postCode",
"type": "text",
"refreshRequirementsOnChange": false,
"required": true,
"displayFormat": null,
"example": "10025",
"minLength": null,
"maxLength": null,
"validationRegexp": null,
"validationAsync": null,
"valuesAllowed": null
}
]
}
...
]
}
]
GET https://api.sandbox.transferwise.tech/v1/address-requirements
POST https://api.sandbox.transferwise.tech/v1/address-requirements
GET and POST address-requirements
endpoints help you to figure out which fields are required to create a valid address for different countries.
You could even build a dynamic user interface on top of these endpoints. This is a step-by-step guide on how these endpoints work.
GET /v1/address-requirements
to get list of fields you need to fill with values in "details" section for creating a valid address. Response contains 4 required top level fields:Analyze the list of fields. Because refreshRequirementsOnChange
for field 'country' is marked as true
then this indicates that there are additional fields required depending on the selected value.
Call POST /v1/address-requirements
with selected country value to expose sub fields.
For example posting {"details": {"country" : "US"}}
will also add "state" to list of fields.
But posting {"details": {"country" : "GB"}}
will not.
If you choose "US" as country you will notice that "state" field also has refreshRequirementsOnChange=true. This means you would need to make another POST call to /v1/address-requirements
with a specific state value.
For example posting {"details": { "country" : "US", "state": "AZ" }}
will also add "occupation" to list of fields.
But posting {"details": { "country" : "US", "state": "AL" }}
will not.
So once you get to the point where you have provided values for all fields which have refreshRequirementsOnChange=true then you have complete set of fields to compose a valid request to create an address object. For example this is a valid request to create address in US Arizona:
POST /v1/addresses
{
"profile" : <your-profile-id>,
"details": {
"country" : "US",
"state": "AZ",
"city": "Phoenix",
"postCode": "10025",
"firstLine": "50 Sunflower Ave.",
"occupation": "software engineer"
}
}
Field | Description | Format |
---|---|---|
type | "address" | Text |
fields[n].name | Field description | Text |
fields[n].group[n].key | Key is name of the field you should include in the JSON | Text |
fields[n].group[n].type | Display type of field (e.g. text, select, etc) | Text |
fields[n].group[n].refreshRequirementsOnChange | Tells you whether you should call POST address-requirements once the field value is set to discover required lower level fields. | Boolean |
fields[n].group[n].required | Indicates if the field is mandatory or not | Boolean |
fields[n].group[n].displayFormat | Display format pattern. | Text |
fields[n].group[n].example | Example value. | Text |
fields[n].group[n].minLength | Min valid length of field value. | Integer |
fields[n].group[n].maxLength | Max valid length of field value. | Integer |
fields[n].group[n].validationRegexp | Regexp validation pattern. | Text |
fields[n].group[n].validationAsync | Validator URL and parameter name you should use when submitting the value for validation | Text |
fields[n].group[n].valuesAllowed[n].key | List of allowed values. Value key | Text |
fields[n].group[n].valuesAllowed[n].name | List of allowed values. Value name. | Text |
Example Request:
curl -X POST https://api.sandbox.transferwise.tech/v3/profiles/101/quotes \
-H "Authorization: Bearer <your api token>" \
-H "Content-Type: application/json" \
-d '{
"sourceCurrency": "GBP",
"targetCurrency": "USD",
"sourceAmount": 100,
"targetAmount": null,
"payOut": null,
"preferredPayIn": null
}'
Example Response:
{
"id": "11144c35-9fe8-4c32-b7fd-d05c2a7734bf",
"sourceCurrency": "GBP",
"targetCurrency": "USD",
"sourceAmount": 100,
"payOut": "BANK_TRANSFER",
"preferredPayIn": "BANK_TRANSFER",
"rate": 1.30445,
"createdTime": "2019-04-05T13:18:58Z",
"user": 55,
"profile": 101,
"rateType": "FIXED",
"rateExpirationTime": "2019-04-08T13:18:57Z",
"guaranteedTargetAmountAllowed": true,
"targetAmountAllowed": true,
"guaranteedTargetAmount": false,
"providedAmountType": "SOURCE",
"paymentOptions": [
{
"disabled": false,
"estimatedDelivery": "2019-04-08T12:30:00Z",
"formattedEstimatedDelivery": "by Apr 8",
"estimatedDeliveryDelays": [
{
"reason": "sample reason"
}
],
"fee": {
"transferwise": 3.04,
"payIn": 0,
"discount": 2.27,
"partner": 0,
"total": 0.77
},
"price": {
"priceSetId": 238,
"total": {
"type": "TOTAL",
"label": "Total fees",
"value": {
"amount": 0.77,
"currency": "GBP",
"label:": "0.77 GBP"
}
},
"items": [
{
"type": "FEE",
"label": "fee",
"value": {
"amount": 0,
"currency": "GBP",
"label": "0 GBP"
}
},
{
"type": "TRANSFERWISE",
"label": "Our fee",
"value": {
"amount": 3.04,
"currency": "GBP",
"label": "3.04 GBP"
}
},
{
"id": 123,
"type": "DISCOUNT",
"value": {
"amount": -2.27,
"currency": "GBP",
"label": "2.27 GBP"
},
"label": "Discount applied",
"explanation": {
"plainText": "You can have a discount for a number of reasons..."
}
}
]
},
"sourceAmount": 100,
"targetAmount": 129.24,
"sourceCurrency": "GBP",
"targetCurrency": "USD",
"payIn": "BANK_TRANSFER",
"payOut": "BANK_TRANSFER",
"allowedProfileTypes": [
"PERSONAL",
"BUSINESS"
],
"payInProduct": "CHEAP",
"feePercentage": 0.0092
},
{
"disabled": true,
"estimatedDelivery": null,
"formattedEstimatedDelivery": null,
"estimatedDeliveryDelays": [],
"fee": {
"transferwise": 3.04,
"payIn": 0,
"discount": 0,
"partner": 0,
"total": 3.04
},
"price": {
"priceSetId": 238,
"total": {
"type": "TOTAL",
"label": "Total fees",
"value": {
"amount": 3.04,
"currency": "GBP",
"label:": "3.04 GBP"
}
},
"items": [
{
"type": "FEE",
"label": "fee",
"value": {
"amount": 0,
"currency": "GBP",
"label": "0 GBP"
}
},
{
"type": "TRANSFERWISE",
"label": "Our fee",
"value": {
"amount": 3.04,
"currency": "GBP",
"label": "3.04 GBP"
}
}
]
},
"sourceAmount": 100,
"targetAmount": 129,
"sourceCurrency": "GBP",
"targetCurrency": "USD",
"payIn": "BALANCE",
"payOut": "BANK_TRANSFER",
"allowedProfileTypes": [
"PERSONAL",
"BUSINESS"
],
"disabledReason": {
"code": "error.payInmethod.disabled",
"message": "Open a multi-currency account and add funds to instantly pay for your transfers."
},
"payInProduct": "BALANCE",
"feePercentage": 0.0111
}
],
"status": "PENDING",
"expirationTime": "2019-04-05T13:48:58Z",
"notices": [
{
"text": "You can have a maximum of 3 open transfers with a guaranteed rate. After that, they'll be transferred using the live rate. Complete or cancel your other transfers to regain the use of guaranteed rate.",
"link": null,
"type": "WARNING"
}
]
}
The quote resource defines the basic information required for a Wise transfer - the currencies to send between, the amount to send and the profile who is sending the money. The profile must be included when creating a quote.
Quote is one of the required resources to create a transfer, along with the recipient who is to receive the funds.
The quote response contains other information such as the exchange rate, the estimated delivery time and the methods the user can pay for the transfer. Not all of this information may apply to your use case.
Upon creating a quote the current mid-market exchange rate is locked and will be used for the transfer that is created from the quote. The rate will be locked for 30 minutes to give a user time to complete the transfer creation flow.
POST https://api.sandbox.transferwise.tech/v3/profiles/{profileId}/quotes
Field | Description | Format |
---|---|---|
profileId | Personal or business profile id of the sender - required. | Integer |
sourceCurrency | Source (sending) currency code. | Text |
targetCurrency | Target (receiving) currency code. | Text |
targetAmount | Amount in target currency. | Decimal |
sourceAmount | Amount in source currency. Either sourceAmount or targetAmount is required, never both. |
Decimal |
targetAccount | Optional. If provided can be used as an alternative to updating the quote. | Integer |
payOut | Optional. Preferred payout method. Default value is BANK_TRANSFER . Other possible values are BALANCE , SWIFT , SWIFT_OUR and INTERAC . |
Text |
preferredPayIn | Optional. Preferred payin method. Use BANK_TRANSFER to return this method at the top of the response's results. |
Text |
Note - When SWIFT_OUR is set as payOut value, it enables payment protection for swift recipients for global currency transfers. By using this payOut method, you can guarantee your customers that the fee will be charged to the sender and can ensure that the recipient gets the complete target amount.
The following describes the fields of the quote response that may be useful when building your integration.
The payOut
field is used to select the correct entry in the paymentOptions
array in order to know which fees to display to your customer. Find the paymentOption that matches the payOut
field shown at the top level of the quote resource and payIn
based on the settlement model the bank is using. By default this is BANK_TRANSFER
, unless you are using a prefunded or bulk settlement model. The payOut
field will change based on the type of recipient you add to the quote in the PATCH /quote
call, for example to-USD swift_code
or to-CAD interac
have different fees.
For example sending USD to a country other than the United States is supported but with different fees to domestic USD transfers. Please see the later section on Global Currencies to learn more about how to offer this useful feature.
For each paymentOption there is a price field. It gives a full breakdown of all the taxes, fees and discounts. It is preferable to refer to this structure to show breakdowns and totals, rather than the fee structre, found as well in each paymentOption element, that only gives a summary and is not able to surface important specifics such as taxes.
When showing the price of a transfer always show the 'price.total.value.amount' of a payment option.
Each payment option is either enabled or disabled based on the disabled
value. Disabled payment options should be shown to the user in a disabled state in most cases. This ensures users are given the options that they are familiar with regardless of their availability, as well as with options that can be beneficial to their accounts.
The option.disabledReason
contains both the code
and message
, with the message being the user-friendly text to surface to the user if necessary.
The option.estimatedDeliveryDelays
contains the reason
of the delay in a string format.
Field | Description | Format |
---|---|---|
id | ID of this quote (GUID format). | Text |
sourceCurrency | Source (sending) currency code. | Text |
targetCurrency | Target (receive) currency code. | Text |
sourceAmount | Amount in source currency to send. | Decimal |
targetAmount | Amount in target currency to be received by the recipient. | Decimal |
payOut | Mechanism we use to deliver the transfer. Not usually of interest to the user. | Text |
rate | Exchange rate value used for the conversion. | Decimal |
createdTime | Quote created timestamp. | Timestamp |
user | User ID who created the quote. | Integer |
profile | Personal or business profile id. | Integer |
rateExpirationTime | Time the locked rate will expire. | Timestamp |
providedAmountType | Whether the quote was created as "SOURCE" or "TARGET". | Text |
paymentOptions | List of the methods a user can pay for the transfer. See above for help on choosing the correct one to display. | [PaymentOption] |
option.disabled | Whether this option is enabled or not for this quote. | Boolean |
option.estimatedDelivery | The estimated delivery time for this combination of payIn and payOut methods, assuming payIn is performed now. | Timestamp |
option.formattedEstimatedDelivery | A string to display to users for the estimated delivery date. | Text |
option.estimatedDeliveryDelays | Array of object with reason for delivery delays to display to users. | [EstimatedDeliveryDelays] |
option.estimatedDeliveryDelays.reason | Reason of delay | |
option.fee | Object containing fee information. | Fee |
option.fee.transferwise | The fee to be paid by the sender based on the current state of the quote. | Decimal |
option.fee.payIn | The fee for this payment option, based on the product type of the payment option. | Decimal |
option.fee.discount | Any discounts that have been applied to this quote for the user. | Decimal |
option.fee.partner | If you have agreed a custom price, it will be displayed here. | Decimal |
option.fee.total | The total fees to be paid - use this figure when displaying fees on your app. | Decimal |
option.price | Object containing the price information. | Price |
option.price.priceSetId | Id if the price structure. | Integer |
option.price.total | The total fees to be paid - use this figure when displaying fees on your app. | Total |
option.price.total.id | ID of this structure. | Integer |
option.price.total.type | Type of the pricing element - "TOTAL" in this case. | Text |
option.price.total.label | Short text describing the price structure this field is nested in. | Text |
option.price.total.value | Object containing value elements. | Value |
option.price.total.value.amount | Amount to be paid. | Decimal |
option.price.total.value.currency | Currency of the amount to be paid. | Text |
option.price.total.value.label | Text version of the price. | Text |
option.price.total.explanation | Text element giving more details about the price. | Text |
option.price.items | Object containing the detals of the different elements of the total price. | List |
option.price.items.id | Id of this item. | Integer |
option.price.items.type | Type of the pricing item. It could be "DISCOUNT" for example. | Text |
option.price.items.label | Short text describing the pricing element. | Text |
option.price.items.value | Object containing value elements. | Value |
option.price.items.value.amount | Amount associated to this pricing element. Can be negative for discounts. | Decimal |
option.price.items.value.currency | Currency on the pricing element. | Text |
option.price.items.value.label | Text feild containing the price and its currency. | Text |
option.price.items.explanation | Text element giving more details about the item. | Text |
option.sourceAmount | sourceAmount when using this payment option. | Decimal |
option.targetAmount | targetAmount when using this payment option. | Decimal |
option.payIn | Type of pay in method for this payment option. | Text |
option.payOut | Type of pay out method for this payment option. | Text |
option.allowedProfileTypes | Array of the allowed types of profile to use this payment option for this quote "PERSONAL", "BUSINESS" or both. | [Text] |
option.disabledReason | Object present if a payment option is disabled. | Disabled Reason |
option.disabledReason.code | Code to denote the reason a payment method is unavailable. | Text |
option.disabledReason.message | User friendly message to display when a method is unavailable. | Text |
status | Current status of this quote, one of: "PENDING", "ACCEPTED", "FUNDED" or "EXPIRED". | Text |
expirationTime | The time the quote expires. | Timestamp |
notices | Array of messages to display to the user in case of useful information based on their selections. May be empty ([] ) if there are no messages. |
[QuoteNotice] |
notice.text | The message to display. | Text |
notice.link | URL that provides more information to the message. May be null if there's no URL. |
Text |
notice.type | Type of message, WARNING or INFO or BLOCKED . If it is BLOCKED , don't allow the quote to be used to create the transfer. |
Text |
Example Request:
curl -X PATCH \
https://api.sandbox.transferwise.tech/v3/profiles/101/quotes/11144c35-9fe8-4c32-b7fd-d05c2a7734bf \
-H 'Authorization: Bearer <your api token>' \
-H 'Content-Type: application/merge-patch+json' \
-d '{
"targetAccount": 12345,
"payOut": "SWIFT_OUR"
}'
Field | Description | Format |
---|---|---|
targetAccount | ID of transfer recipient, found in response from POST v1/accounts (recipient creation) | Integer |
payOut | Optional. Preferred payout method. Default value is BANK_TRANSFER . Other possible values are BALANCE , SWIFT , SWIFT_OUR and INTERAC . |
Text |
Example Response:
{
"id": "11144c35-9fe8-4c32-b7fd-d05c2a7734bf",
"sourceCurrency": "GBP",
"targetCurrency": "USD",
"sourceAmount": 100,
"payOut": "BANK_TRANSFER",
"rate": 1.30445,
"createdTime": "2019-04-05T13:18:58Z",
"user": 55,
"profile": 101,
"rateType": "FIXED",
"rateExpirationTime": "2019-04-08T13:18:57Z",
"guaranteedTargetAmountAllowed": true,
"targetAmountAllowed": true,
"guaranteedTargetAmount": false,
"providedAmountType": "SOURCE",
"targetAccount": 12345,
"paymentOptions": [
{
"disabled": false,
"estimatedDelivery": "2019-04-08T12:30:00Z",
"formattedEstimatedDelivery": "by Apr 8",
"estimatedDeliveryDelays": [
{
"reason": "sample reason"
}
],
"fee": {
"transferwise": 3.04,
"payIn": 0,
"discount": 2.27,
"partner": 0,
"total": 0.77
},
"price": {
"priceSetId": 238,
"total": {
"type": "TOTAL",
"label": "Total fees",
"value": {
"amount": 0.77,
"currency": "GBP",
"label:": "0.77 GBP"
}
},
"items": [
{
"type": "FEE",
"label": "fee",
"value": {
"amount": 0,
"currency": "GBP",
"label": "0 GBP"
}
},
{
"type": "TRANSFERWISE",
"label": "Our fee",
"value": {
"amount": 3.04,
"currency": "GBP",
"label": "3.04 GBP"
}
},
{
"type": "DISCOUNT",
"value": {
"amount": -2.27,
"currency": "GBP",
"label": "2.27 GBP"
},
"label": "Discount applied",
"explanation": {
"plainText": "You can have a discount for a number of reasons..."
}
}
]
},
"sourceAmount": 100,
"targetAmount": 129.24,
"sourceCurrency": "GBP",
"targetCurrency": "USD",
"payIn": "BANK_TRANSFER",
"payOut": "BANK_TRANSFER",
"allowedProfileTypes": [
"PERSONAL",
"BUSINESS"
],
"payInProduct": "CHEAP",
"feePercentage": 0.0092
},
{
"disabled": true,
"estimatedDelivery": null,
"formattedEstimatedDelivery": null,
"estimatedDeliveryDelays": [],
"fee": {
"transferwise": 3.04,
"payIn": 0,
"discount": 0,
"partner": 0,
"total": 3.04
},
"price": {
"priceSetId": 238,
"total": {
"type": "TOTAL",
"label": "Total fees",
"value": {
"amount": 3.04,
"currency": "GBP",
"label:": "3.04 GBP"
}
},
"items": [
{
"type": "FEE",
"label": "fee",
"value": {
"amount": 0,
"currency": "GBP",
"label": "0 GBP"
}
},
{
"type": "TRANSFERWISE",
"label": "Our fee",
"value": {
"amount": 3.04,
"currency": "GBP",
"label": "3.04 GBP"
}
}
]
},
"sourceAmount": 100,
"targetAmount": 129,
"sourceCurrency": "GBP",
"targetCurrency": "USD",
"payIn": "BALANCE",
"payOut": "BANK_TRANSFER",
"allowedProfileTypes": [
"PERSONAL",
"BUSINESS"
],
"disabledReason": {
"code": "error.payInmethod.disabled",
"message": "Open a multi-currency account and add funds to instantly pay for your transfers."
},
"payInProduct": "BALANCE",
"feePercentage": 0.0111
}
],
"status": "PENDING",
"expirationTime": "2019-04-05T13:48:58Z",
"notices": []
}
You should update a quote using a PATCH
call to add a recipient. This will update the saved quote's data based on who and where the money will be sent to.
Updating the quote with a recipient may cause the available payment options, prices and estimated delivery times to change from the original quoted amounts. This is due to the fact that sending some currencies to some destinations costs a different amount based on the payment networks we use, for example sending USD to a country outside the USA uses international rather than domestic payment networks and as such costs more, or sending CAD over the Interac network is a more expensive operation.
When updating a quote the payOut
field may change to denote the payment option you should select when sending to this recipient. For example sending USD to a swift_code
recipient or CAD to an interac
recipient with change payOut
to SWIFT
or INTERAC
respectively. This field defaults to BANK_TRANSFER
so it can be used in all cases to help select the correct paymentOption
and hence show the correct pricing to users.
If you want to provide more transparency in terms of fees charged when your customers create quote with swift recipient for global currencies, you might consider to set payOut field with SWIFT_OUR value. This will ensure that the recipient receives complete target amount.
In this case, where pricing changes after a user selects recipient, you should show a message to your customer before confirming the transfer. Please see the section on Global Currencies to learn more how and why this works and the messaging you need to display.
PATCH https://api.sandbox.transferwise.tech/v3/profiles/{profileId}/quotes/{quoteId}
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v3/profiles/{profileId}/quotes/{quoteId} \
-H "Authorization: Bearer <your api token>"
Example Response:
{
"id": "11144c35-9fe8-4c32-b7fd-d05c2a7734bf",
"sourceCurrency": "GBP",
"targetCurrency": "USD",
"sourceAmount": 100,
"payOut": "BANK_TRANSFER",
"rate": 1.30445,
"createdTime": "2019-04-05T13:18:58Z",
"user": 55,
"profile": 101,
"rateType": "FIXED",
"rateExpirationTime": "2019-04-08T13:18:57Z",
"guaranteedTargetAmountAllowed": true,
"targetAmountAllowed": true,
"guaranteedTargetAmount": false,
"providedAmountType": "SOURCE",
"paymentOptions": [
{
"disabled": false,
"estimatedDelivery": "2019-04-08T12:30:00Z",
"formattedEstimatedDelivery": "by Apr 8",
"estimatedDeliveryDelays": [
{
"reason": "sample reason"
}
],
"fee": {
"transferwise": 3.04,
"payIn": 0,
"discount": 2.27,
"partner": 0,
"total": 0.77
},
"price": {
"priceSetId": 238,
"total": {
"type": "TOTAL",
"label": "Total fees",
"value": {
"amount": 0.77,
"currency": "GBP",
"label:": "0.77 GBP"
}
},
"items": [
{
"type": "FEE",
"label": "fee",
"value": {
"amount": 0,
"currency": "GBP",
"label": "0 GBP"
}
},
{
"type": "TRANSFERWISE",
"label": "Our fee",
"value": {
"amount": 3.04,
"currency": "GBP",
"label": "3.04 GBP"
}
},
{
"type": "DISCOUNT",
"value": {
"amount": -2.27,
"currency": "GBP",
"label": "2.27 GBP"
},
"label": "Discount applied",
"explanation": {
"plainText": "You can have a discount for a number of reasons..."
}
}
]
},
"sourceAmount": 100,
"targetAmount": 129.24,
"sourceCurrency": "GBP",
"targetCurrency": "USD",
"payIn": "BANK_TRANSFER",
"payOut": "BANK_TRANSFER",
"allowedProfileTypes": [
"PERSONAL",
"BUSINESS"
],
"payInProduct": "CHEAP",
"feePercentage": 0.0092
},
{
"disabled": true,
"estimatedDelivery": null,
"formattedEstimatedDelivery": null,
"estimatedDeliveryDelays": [],
"fee": {
"transferwise": 3.04,
"payIn": 0,
"discount": 0,
"partner": 0,
"total": 3.04
},
"price": {
"priceSetId": 238,
"total": {
"type": "TOTAL",
"label": "Total fees",
"value": {
"amount": 3.04,
"currency": "GBP",
"label:": "3.04 GBP"
}
},
"items": [
{
"type": "FEE",
"label": "fee",
"value": {
"amount": 0,
"currency": "GBP",
"label": "0 GBP"
}
},
{
"type": "TRANSFERWISE",
"label": "Our fee",
"value": {
"amount": 3.04,
"currency": "GBP",
"label": "3.04 GBP"
}
}
]
},
"sourceAmount": 100,
"targetAmount": 129,
"sourceCurrency": "GBP",
"targetCurrency": "USD",
"payIn": "BALANCE",
"payOut": "BANK_TRANSFER",
"allowedProfileTypes": [
"PERSONAL",
"BUSINESS"
],
"disabledReason": {
"code": "error.payInmethod.disabled",
"message": "Open a multi-currency account and add funds to instantly pay for your transfers."
},
"payInProduct": "BALANCE",
"feePercentage": 0.0111
}
],
"status": "PENDING",
"expirationTime": "2019-04-05T13:48:58Z",
"notices": [{
"text": "You can have a maximum of 3 open transfers with a guaranteed rate. After that, they'll be transferred using the live rate. Complete or cancel your other transfers to regain the use of guaranteed rate.",
"link": null,
"type": "WARNING"
}]
}
Get quote info by id.
GET https://api.sandbox.transferwise.tech/v3/profiles/{profileId}/quotes/{quoteId}
Use this endpoint to get example quotes for people to see the exchange rate and fees Wise offers before a user has created or linked an account. This can drive a version of the quote screen that shows the user what Wise offers before they sign up. Note that this endpoint does not require a token to create the resource, however, since it is just an example, the returned quote has no ID so can't be used later to create a transfer.
In order to get an accurate partner fee, we require a client credentials token to be provided. If you are a partner and would like your fee to be included in the quote returned, you must provide your auth token. If not, you do not require the Authorization header.
Example Request:
curl -X POST \
https://api.transferwise.com/v3/quotes/ \
-H "Authorization: Bearer <your client credentials token>"
-H 'Content-type: application/json' \
-d '{
"sourceCurrency": "GBP",
"targetCurrency": "USD",
"sourceAmount": null,
"targetAmount": 110
}'
Example Response:
{
"sourceCurrency": "GBP",
"targetCurrency": "USD",
"targetAmount": 110,
"payOut": "BANK_TRANSFER",
"rate": 1.30745,
"createdTime": "2019-04-09T11:46:38Z",
"rateType": "FIXED",
"guaranteedTargetAmountAllowed": true,
"targetAmountAllowed": true,
"guaranteedTargetAmount": false,
"providedAmountType": "TARGET",
"paymentOptions": [
{
"disabled": false,
"estimatedDelivery": "2019-04-08T12:30:00Z",
"formattedEstimatedDelivery": "by Apr 8",
"estimatedDeliveryDelays": [
{
"reason": "sample reason"
}
],
"fee": {
"transferwise": 3.04,
"payIn": 0,
"discount": 2.27,
"partner": 0,
"total": 0.77
},
"price": {
"priceSetId": 238,
"total": {
"type": "TOTAL",
"label": "Total fees",
"value": {
"amount": 0.77,
"currency": "GBP",
"label:": "0.77 GBP"
}
},
"items": [
{
"type": "FEE",
"label": "fee",
"value": {
"amount": 0,
"currency": "GBP",
"label": "0 GBP"
}
},
{
"type": "TRANSFERWISE",
"label": "Our fee",
"value": {
"amount": 3.04,
"currency": "GBP",
"label": "3.04 GBP"
}
},
{
"type": "DISCOUNT",
"value": {
"amount": -2.27,
"currency": "GBP",
"label": "2.27 GBP"
},
"label": "Discount applied",
"explanation": {
"plainText": "You can have a discount for a number of reasons..."
}
}
]
},
"sourceAmount": 100,
"targetAmount": 129.24,
"sourceCurrency": "GBP",
"targetCurrency": "USD",
"payIn": "BANK_TRANSFER",
"payOut": "BANK_TRANSFER",
"allowedProfileTypes": [
"PERSONAL",
"BUSINESS"
],
"payInProduct": "CHEAP",
"feePercentage": 0.0092
},
{
"disabled": true,
"estimatedDelivery": null,
"formattedEstimatedDelivery": null,
"estimatedDeliveryDelays": [],
"fee": {
"transferwise": 3.04,
"payIn": 0,
"discount": 0,
"partner": 0,
"total": 3.04
},
"price": {
"priceSetId": 238,
"total": {
"type": "TOTAL",
"label": "Total fees",
"value": {
"amount": 3.04,
"currency": "GBP",
"label:": "3.04 GBP"
}
},
"items": [
{
"type": "FEE",
"label": "fee",
"value": {
"amount": 0,
"currency": "GBP",
"label": "0 GBP"
}
},
{
"type": "TRANSFERWISE",
"label": "Our fee",
"value": {
"amount": 3.04,
"currency": "GBP",
"label": "3.04 GBP"
}
}
]
},
"sourceAmount": 85.28,
"targetAmount": 110,
"sourceCurrency": "GBP",
"targetCurrency": "USD",
"payIn": "BALANCE",
"payOut": "BANK_TRANSFER",
"allowedProfileTypes": [
"PERSONAL",
"BUSINESS"
],
"disabledReason": {
"code": "error.payInmethod.disabled",
"message": "Open a multi-currency account and add funds to instantly pay for your transfers."
},
"payInProduct": "BALANCE",
"feePercentage": 0.0135
}
],
"notices": []
}
POST https://api.sandbox.transferwise.tech/v3/quotes/
Field | Description | Format |
---|---|---|
sourceCurrency | Source (sending) currency code | Text |
targetCurrency | Target (receiving) currency code | Text |
sourceAmount | Amount in source currency. Either sourceAmount or targetAmount is required, never both. |
Decimal |
targetAmount | Amount in target currency | Decimal |
See Create quote's response field information.
These endpoints use a mixture of our v1 and v2 api - please ensure you address the right version to get the expected results. All recipient IDs are cross compatible with v1 and v2.
Example Request (Create GBP recipient):
curl -X POST https://api.sandbox.transferwise.tech/v1/accounts \
-H "Authorization: Bearer <your api token>" \
-H "Content-Type: application/json" \
-d '{
"currency": "GBP",
"type": "sort_code",
"profile": <your profile id>,
"ownedByCustomer": true,
"accountHolderName": "Ann Johnson",
"details": {
"legalType": "PRIVATE",
"sortCode": "231470",
"accountNumber": "28821822"
}
}'
Example Response (Create GBP recipient):
{
"id": 13967081,
"business": null,
"profile": <your profile id>,
"accountHolderName": "Ann Johnson",
"currency": "GBP",
"country": "GB",
"type": "sort_code",
"details": {
"address": {
"country": null,
"countryCode": null,
"firstLine": null,
"postCode": null,
"city": null,
"state": null
},
"email": null,
"legalType": "PRIVATE",
"accountNumber": "28821822",
"sortCode": "231470",
"abartn": null,
"accountType": null,
"bankgiroNumber": null,
"ifscCode": null,
"bsbCode": null,
"institutionNumber": null,
"transitNumber": null,
"phoneNumber": null,
"bankCode": null,
"russiaRegion": null,
"routingNumber": null,
"branchCode": null,
"cpf": null,
"cardNumber": null,
"idType": null,
"idNumber": null,
"idCountryIso3": null,
"idValidFrom": null,
"idValidTo": null,
"clabe": null,
"swiftCode": null,
"dateOfBirth": null,
"clearingNumber": null,
"bankName": null,
"branchName": null,
"businessNumber": null,
"province": null,
"city": null,
"rut": null,
"token": null,
"cnpj": null,
"payinReference": null,
"pspReference": null,
"orderId": null,
"idDocumentType": null,
"idDocumentNumber": null,
"targetProfile": null,
"taxId": null,
"iban": null,
"bic": null,
"IBAN": null,
"BIC": null,
"interacAccount": null
},
"user": <your user ID>,
"active": true,
"ownedByCustomer": true
}
Recipient is a person or institution who is the ultimate beneficiary of your payment.
Recipient data includes three data blocks.
Owned by customer is an optional boolean to flag to record whether this recipient is the same entity (person or business) as the one sending the funds. i.e. A user sending money to their own bank account in another country/currency. This field can be used to separate these recipients in your UI, however we do not recommend this as it adds unnecessary complexity to the solution. It is safe to ignore this field and display recipients with both true
and false
values.
There are many different variations of bank account details needed depending on recipient target currency. For example:
3) Address data Recipient address data is required only if target currency is USD, PHP, THB or TRY, or if the source currency is USD or AUD.
When creating recipient, the following general rules should be applied to "accountHolderName" field:
_()'*,.
are allowed for personal and business names.[0-9A-Za-zÀ-ÖØ-öø-ÿ-_()'*,.\s]
.Recipient requirements will vary depending on recipient type.
A GBP example is provided here.
As you can see many of the fields are null
, in order to know which fields are required for which currency we expose the Recipients.Requirements endpoint.
POST https://api.sandbox.transferwise.tech/v1/accounts
Field | Description | Format | Optional |
---|---|---|---|
currency | 3 character currency code | Text | false |
type | Recipient type | Text | false |
profile | Personal or business profile id | Integer | false |
accountHolderName | Recipient full name | Text | false |
ownedByCustomer | Whether this account is owned by the sending user | Boolean | true |
details | Currency specific fields | Object | false |
details.legalType | Recipient legal type: PRIVATE or BUSINESS | Text | false |
details.sortCode | Recipient bank sort code (GBP example) | Text | false |
details.accountNumber | Recipient bank account no (GBP example) | Text | false |
Recipient account id is needed for creating transfers in step 3. The profile ID you provided when creating your recipient will appear in the response. However, some older recipients may not have a profile ID specified.
Field | Description | Format | Nullable |
---|---|---|---|
id | accountId | Integer | false |
profile | Personal or business profile id | Integer | true |
user | User that created or owns this recipient | Integer | false |
acccountHolderName | Recipient full name | Text | false |
currency | 3 character country code | Text | false |
country | 2 character currency code | Text | false |
type | Recipient type | Text | false |
ownedByCustomer | Whether this account is owned by the sending user | Text | true |
details | Currency specific fields | Object | false |
details.legalType | Recipient legal type | Text | true |
details.sortCode | Recipient bank sort code (GBP example) | Text | Currency Dependent |
details.accountNumber | Recipient bank account no (GBP example) | Text | Currency Dependent |
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v2/accounts/{accountId} \
-H "Authorization: Bearer <your api token>"
Example Response:
{
"id": 100,
"creatorId": 300,
"profileId": 305,
"name": {
"fullName": "Firstname Lastname",
"givenName": null,
"familyName": null,
"middleName": null,
"patronymicName": null,
"cannotHavePatronymicName": null
},
"address": {
"country": "US",
"firstLine": "1 Wall Street",
"postCode": "10025",
"city": "New York",
"state": "NY"
},
"currency": "USD",
"country": "US",
"type": "Aba",
"legalEntityType": "PERSON",
"email": "example@foobar.com",
"active": true,
"details": {
"abartn": "064000020",
"accountType": "CHECKING",
"accountNumber": "00000000001"
},
"commonFieldMap": {
"accountNumberField": "accountNumber",
"bankCodeField": "abartn"
},
"hash": "a512e4066bd5997552d35e294d29bacda4b09a7610a7b75562767b17dddadf19",
"accountSummary": "(064000020) 0********01",
"longAccountSummary": "USD account ending in 0001",
"displayFields": [
{
"label": "E-mail",
"value": "example@foobar.com"
},
{
"label": "ACH routing number",
"value": "064000020"
},
{
"label": "Account number",
"value": "00000000001"
}
],
"ownedByCustomer": false
}
Get recipient account info by id. Note here we use the v2 endpoint to get the recipient details despite using v1 to create it. The v2 endpoint provides useful features such as the accountSummary
and longAccountSummary
field which can be used to represent the recipient's details in your UI. Additionally, the displayFields
array would allow you to build an UI containing all the dynamic fields of a recipient individually.
This response also includes a hash
of a recipient, this can be used to track if the recipient details change, which they can in some scenarios. This is a security feature to allow you to re-run any checks your system does on the recipient to validate them against, for example, fraud engines. The hash will remain constant unless the recipient's name or information in the details
object changes.
GET https://api.sandbox.transferwise.tech/v2/accounts/{accountId}
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v2/accounts?profile=<profileId>¤cy=GBP \
-H "Authorization: Bearer <your api token>"
Example Response:
{
"content": [
{
"id": 100,
"creatorId": 300,
"profileId": 305,
"name": {
"fullName": "Firstname Lastname",
"givenName": null,
"familyName": null,
"middleName": null,
"patronymicName": null,
"cannotHavePatronymicName": null
},
"address": {
"country": "US",
"firstLine": "1 Wall Street",
"postCode": "10025",
"city": "New York",
"state": "NY"
},
"currency": "USD",
"country": "US",
"type": "Aba",
"legalEntityType": "PERSON",
"email": "example@foobar.com",
"active": true,
"details": {
"abartn": "064000020",
"accountType": "CHECKING",
"accountNumber": "00000000001"
},
"commonFieldMap": {
"accountNumberField": "accountNumber",
"bankCodeField": "abartn"
},
"isDefaultAccount": false,
"hash": "a512e4066bd5997552d35e294d29bacda4b09a7610a7b75562767b17dddadf19",
"accountSummary": "(064000020) 0********01",
"longAccountSummary": "USD account ending in 0001",
"displayFields": [
{
"label": "E-mail",
"value": "example@foobar.com"
},
{
"label": "ACH routing number",
"value": "064000020"
},
{
"label": "Account number",
"value": "00000000001"
}
],
"ownedByCustomer": false
},
{
"id": 101,
"creatorId": 300,
"profileId": 305,
"name": {
"fullName": "Firstname Anothername",
"givenName": null,
"familyName": null,
"middleName": null,
"patronymicName": null,
"cannotHavePatronymicName": null
},
"address": {
"country": "US",
"firstLine": "1 Wall Street",
"postCode": "10025",
"city": "New York",
"state": "NY"
},
"currency": "USD",
"country": "US",
"type": "Aba",
"legalEntityType": "PERSON",
"email": "example2@foobar.com",
"active": true,
"details": {
"abartn": "064000020",
"accountType": "CHECKING",
"accountNumber": "00000000001"
},
"commonFieldMap": {
"accountNumberField": "accountNumber",
"bankCodeField": "abartn"
},
"isDefaultAccount": false,
"hash": "992ca6102a11f2055d14682e0cbacd65727fc45e98b1c42ba1292acad08302c5",
"accountSummary": "(064000020) 0********01",
"longAccountSummary": "USD account ending in 0001",
"displayFields": [
{
"label": "E-mail",
"value": "example2@foobar.com"
},
{
"label": "ACH routing number",
"value": "064000020"
},
{
"label": "Account number",
"value": "00000000002"
}
],
"ownedByCustomer": false
}
],
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"size": 2
}
Fetch a list of the user's recipient accounts. Use the profileId
parameter to filter by the profile who created the accounts, you should do this based on the personal or business profile ID you have linked to, based on your use case. Other filters are listed below for your convenience, for example currency
is a useful filter to use when presenting the user a list of recipients to chose from in the case they have already submitted the target currency of their in your flow.
Pagination is supported for this endpoint. The response includes the seekPositionForNext
and size
parameters to manage this.
It works by setting size
and seekPosition
parameters in the call. Set the value in the seekPositionForNext
of the previous response into the seekPosition
parameter of your subsequent call in order to get the next page. To get the current page again, use the seekPositionForCurrent
value.
You can also set the sort
parameter to control the sorting of the response, for example:
?sort=id,asc
sort by id
ascending.
?sort=id,desc
sort by id descending.
?sort=currency,asc
sort by currency ascending.
GET https://api.sandbox.transferwise.tech/v2/accounts?profileId=<profileId>
All query parameters are optional.
Field | Description | Format |
---|---|---|
creatorId | Creator of the account. | Integer |
profileId | Filter by personal or business profile, returns only those owned by this profile. Defaults to the personal profile. | Integer |
currency | Filter responses by currency, comma separated values are supported (e.g. USD,GBP ). |
Text |
active | Filter by whether this profile is active. Defaults to true . |
Boolean |
type | Filter responses by account type, comma separated values are supported (e.g. iban,swift_code ). |
Text |
ownedByCustomer | Filter to get accounts owned by the customer or not, leave out to get all accounts. | Boolean |
size | Page size of the response. Defaults to a maximum of 20. | Integer |
seekPosition | Account ID to start the page of responses from in the response. null if no more pages. |
Integer |
sort | Sorting strategy for the response. Comma separated options: firstly either id or currency , followed by asc or desc for direction. |
Integer |
Example Request:
curl -X DELETE https://api.sandbox.transferwise.tech/v2/accounts/{accountId} \
-H "Authorization: Bearer <your api token>"
Example Response:
{
"id": 101,
"creatorId": 300,
"profileId": 305,
"name": {
"fullName": "Firstname Anothername",
"givenName": null,
"familyName": null,
"middleName": null,
"patronymicName": null,
"cannotHavePatronymicName": null
},
"address": {
"country": "US",
"firstLine": "1 Wall Street",
"postCode": "10025",
"city": "New York",
"state": "NY"
},
"currency": "USD",
"country": "US",
"type": "Aba",
"legalEntityType": "PERSON",
"email": "example2@foobar.com",
"active": false,
"details": {
"abartn": "064000020",
"accountType": "CHECKING",
"accountNumber": "00000000001"
},
"commonFieldMap": {
"accountNumberField": "accountNumber",
"bankCodeField": "abartn"
},
"isDefaultAccount": false,
"hash": "992ca6102a11f2055d14682e0cbacd65727fc45e98b1c42ba1292acad08302c5",
"accountSummary": "(064000020) 0********01",
"longAccountSummary": "USD account ending in 0001",
"displayFields": [
{
"label": "E-mail",
"value": "example2@foobar.com"
},
{
"label": "ACH routing number",
"value": "064000020"
},
{
"label": "Account number",
"value": "00000000002"
}
],
"ownedByCustomer": false
}
Deletes a recipient by changing its status to inactive ("active": false
). Only active recipients can be deleted and a recipient cannot be reactivated, however you can create a new recipient with the same details instead if necessary.
Requesting to delete a recipient that is already inactive will return an HTTP status 403 (forbidden).
DELETE https://api.sandbox.transferwise.tech/v2/accounts/{accountId}
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v1/quotes/{quoteId}/account-requirements \
-H "Authorization: Bearer <your api token>"
-H "Accept-Minor-Version: 1"
Example Response:
[
{
"type": "south_korean_paygate",
"title": "PayGate",
"usageInfo": null,
"fields": [
{
"name": "E-mail",
"group": [
{
"key": "email",
"name": "E-mail",
"type": "text",
"refreshRequirementsOnChange": false,
"required": true,
"displayFormat": null,
"example": "example@example.ex",
"minLength": null,
"maxLength": null,
"validationRegexp": "^[^\\s]+@[^\\s]+\\.[^\\s]{2,}$",
"validationAsync": null,
"valuesAllowed": null
}
]
},
{
"name": "Recipient type",
"group": [
{
"key": "legalType",
"name": "Recipient type",
"type": "select",
"refreshRequirementsOnChange": false,
"required": true,
"displayFormat": null,
"example": "",
"minLength": null,
"maxLength": null,
"validationRegexp": null,
"validationAsync": null,
"valuesAllowed": [
{
"key": "PRIVATE",
"name": "Person"
}
]
}
]
},
{
"name": "Full Name",
"group": [
{
"key": "accountHolderName",
"name": "Full Name",
"type": "text",
"refreshRequirementsOnChange": false,
"required": true,
"displayFormat": null,
"example": "",
"minLength": 2,
"maxLength": 140,
"validationRegexp": "^[0-9A-Za-zÀ-ÖØ-öø-ÿ-_()'*,.%#^@{}~<>+$\"\\[\\]\\\\ ]+$",
"validationAsync": null,
"valuesAllowed": null
}
]
},
{
"name": "Recipient's Date of Birth",
"group": [
{
"key": "dateOfBirth",
"name": "Recipient's Date of Birth",
"type": "date",
"refreshRequirementsOnChange": false,
"required": true,
"displayFormat": null,
"example": "",
"minLength": null,
"maxLength": null,
"validationRegexp": "^\\d{4}\\-\\d{2}\\-\\d{2}$",
"validationAsync": null,
"valuesAllowed": null
}
]
},
{
"name": "Recipient Bank Name",
"group": [
{
"key": "bankCode",
"name": "Recipient Bank Name",
"type": "select",
"refreshRequirementsOnChange": false,
"required": true,
"displayFormat": null,
"example": "Choose recipient bank",
"minLength": null,
"maxLength": null,
"validationRegexp": null,
"validationAsync": null,
"valuesAllowed": [
{
"key": "",
"name": "Choose recipient bank"
},
...
]
}
]
},
{
"name": "Account number (KRW accounts only)",
"group": [
{
"key": "accountNumber",
"name": "Account number (KRW accounts only)",
"type": "text",
"refreshRequirementsOnChange": false,
"required": true,
"displayFormat": null,
"example": "1254693521232",
"minLength": 10,
"maxLength": 16,
"validationRegexp": null,
"validationAsync": null,
"valuesAllowed": null
}
]
}
]
},
GET https://api.sandbox.transferwise.tech/v1/quotes/{quoteId}/account-requirements
POST https://api.sandbox.transferwise.tech/v1/quotes/{quoteId}/account-requirements
GET https://api.sandbox.transferwise.tech/v1/account-requirements?source=EUR&target=USD&sourceAmount=1000
You can use the data returned by this API to build a dynamic user interface for recipient creation. The GET
and POST
account-requirements endpoints help you to figure out which fields are required to create a valid recipient for different currencies. This is a step-by-step guide on how these endpoints work.
The third endpoint above is used to get account requirements for a specific currency route and amount without referring to a quote but with the amount, source and target currencies passed as URL parameters. Generally this approach is not recommended, you should have your user create a quote resource first and use this to generate the recipient account requirements.
All new integrations should use the v1.1 of GET
and POST
account requirements, enabled using the Accept-Minor-Version
header. It enables you to fetch the requirements including both the recipient name and email fields in the dynamic form, simplifying implementation of the form. Name and email address dynamic fields are required to support currencies such as KRW, JPY and RUB, and also remove the need for manual name validation. Set the request header Accept-Minor-Version
to 1
to use this version.
These endpoints allows use of both v1 and v2 quotes using long or UUID based IDs, supporting legacy implementations using v1 quotes.
1.First create a quote to specify currencies and transfer amounts. See Create.Quote.
2.Call GET /v1/quotes/{quoteId|quoteUuid}/account-requirements
to get the list of fields you need to fill with values in the "details" section for creating a valid recipient account. Don't forget to set the request header Accept-Minor-Version
to 1
.
In order to create an "aba" recipient type you need these top level fields:
Some fields require multiple levels of fields in the details request, for example Country followed by State. This should be handled by the client based on the refreshRequirementsOnChange
field. In the example above 'address.country' has this field set to true, indicating that there are additional fields required depending on the selected value. To manage this you should create a request with all of the initially requested data and call the POST account-requirements
endpoint. You will be returned a response similar the previously returned data from GET account-requirements
but with additional fields.
3.For example, construct a recipient object with all top level fields and call POST /v1/quotes/{quoteId|quoteuuid}/account-requirements with these value to expose sub fields. Again, don't forget to set the request header Accept-Minor-Version
to 1
.
For example posting US as country will also add "state" to list of fields.
{
"type": "aba",
"details": {
"legalType": "PRIVATE",
"abartn": "111000025",
"accountNumber": "12345678",
"accountType": "CHECKING",
"address": {
"country": "US"
}
}
}
However, posting GB as country will not add any new fields as GB addresses do not have this extra requirement.
{
"type": "aba",
"details": {
"legalType": "PRIVATE",
"abartn": "111000025",
"accountNumber": "12345678",
"accountType": "CHECKING",
"address": {
"country": "GB"
}
}
}
It is possible that any new fields returned may also have refreshRequirementsOnChange
field set to true. Therefore you must keep iterating on the partially created details object until POST account-requirements
returns you no new fields that it previously didn't include in the response, you can do this by checking the size of the array returned.
4.Once you have built your full recipient details object you can use it to create a recipient.
For example this is a valid request to create a recipient with address in US Arizona:
POST /v1/accounts
:
{
"profile": your-profile-id,
"accountHolderName": "John Smith",
"currency": "USD",
"type": "aba",
"details": {
"legalType": "PRIVATE",
"abartn": "111000025",
"accountNumber": "12345678",
"accountType": "CHECKING",
"address": {
"country": "US",
"state": "AZ"
"city": "New York",
"postCode": "10025",
"firstLine": "45 Sunflower Ave"
}
}
}
We do not require the recipient's address for most receiving currencies and as such do not return these form elements by default. In some cases it may be desirable for you to collect this from users and store it as part of the recipient object in the Wise platform. If you wish to do this you can include the parameter &addressRequired=true
in your call to GET /v1/quotes/{quoteId/quoteUuid}/account-requirements
, if this is present we will return address fields as part of the form in all cases.
Account requirements help us understand how to create a valid account given a certain context. As a tool to help explore this API, please visit Dynamic Forms UI. Please be careful to only use the V1 version of the forms as the other versions are not yet ready for public use. This app allows specifying different requests and calls our sandbox environment for account requirements. It then displays the response in JSON along with an example UI of the rendered form from the said response.
When requesting the form data from the account-requirements
endpoint, the first level of the response defines different types of recipient you can create, the first thing to do is present the user a choice of which recipient type they wish to create, e.g. to GBP this could be local details or IBAN format. Each recipient type then has multiple fields
describing the form elements required to be shown to collect information from the user. Each field will have a type
value, these tell you the field type that your front end needs to render to be able to collect the data. A number of field types are permitted, these are:
type | UI element |
---|---|
text | A free text box |
select | A selection box/dialog |
radio | A radio button choice between options |
date | A text box with a date picker |
Example data is also included in each field which should be shown to the user, along with a regex or min and max length constraints that should be applied as field level validations. You can optionally implement the dynamic validation using the validationAsync
field, however these checks will also be done when a completed recipient is submitted to POST /v1/accounts
.
Some good recipient currencies to test are:
refreshRequirementsOnChange
.refreshRequirementsOnChange
.Field | Description | Format |
---|---|---|
type | "address" | Text |
fields[n].name | Field description | Text |
fields[n].group[n].key | Key is name of the field you should include in the JSON | Text |
fields[n].group[n].type | Display type of field (e.g. text, select, etc) | Text |
fields[n].group[n].refreshRequirementsOnChange | Tells you whether you should call POST account-requirements once the field value is set to discover required lower level fields. | Boolean |
fields[n].group[n].required | Indicates if the field is mandatory or not | Boolean |
fields[n].group[n].displayFormat | Display format pattern. | Text |
fields[n].group[n].example | Example value. | Text |
fields[n].group[n].minLength | Min valid length of field value. | Integer |
fields[n].group[n].maxLength | Max valid length of field value. | Integer |
fields[n].group[n].validationRegexp | Regexp validation pattern. | Text |
fields[n].group[n].validationAsync | Validator URL and parameter name you should use when submitting the value for validation | Text |
fields[n].group[n].valuesAllowed[n].key | List of allowed values. Value key | Text |
fields[n].group[n].valuesAllowed[n].name | List of allowed values. Value name. | Text |
Sometimes we may need to refund the transfer back to the sender - see the transfer status here for cases when this may happen.
A refund recipient is a person or institution where we will refund transfer the money back to if necessary. This is not always a mandatory resource to create. If the funds are sent over a fast local payment network we can usually infer the refund recipient from the bank transaction that funded the transfer. PLease discuss this with your Wise implementation team if you are unsure if the refund recipient is needed.
If funds are sent using a slow domestic payment network, or you are using a bulk settlement model, we may require you to share the bank details of the source bank account. In order to do this we have the following API:
Example Request (Create GBP refund recipient):
curl -X POST https://api.sandbox.transferwise.tech/v1/refund-accounts \
-H "Authorization: Bearer <your api token>" \
-H "Content-Type: application/json" \
-d '{
"currency": "GBP",
"country": "GB",
"type": "sort_code",
"profile": <your profile id>,
"legalEntityType": "PERSON",
"name": {
"fullName": "Ann Johnson"
},
"details": {
"sortCode": "231470",
"accountNumber": "28821822"
}
}'
Example Response (Create GBP refund recipient):
{
"id": 13967081,
"creatorId": <your user ID>,
"name": {
"fullName": "Ann Johnson",
"givenName": null,
"familyName": null,
"middleName": null,
"patronymicName": null,
"cannotHavePatronymicName": null
},
"currency": "GBP",
"country": "GB",
"type": "SortCode",
"legalEntityType": "PERSON",
"active": true,
"details": {
"accountNumber": "28821822",
"sortCode": "231470"
},
"commonFieldMap": {
"accountNumberField": "accountNumber",
"bankCodeField": "sortCode"
},
"hash": "abd0e73e6497e678f20cd5d5d700c8926d47d3dae23e092ef45a904455bd70e1",
"accountSummary": "(23-14-70) 28821822",
"longAccountSummary": "GBP account ending in 0000",
"displayFields": [
{
"label": "UK Sort code",
"value": "23-14-70"
},
{
"label": "Account number",
"value": "28821822"
}
],
"ownedByCustomer": false
}
The refund recipient account id returned here is needed when creating transfers in step 3 as the field sourceAccount
in the POST /v1/transfers
call.
The format of the request payload for refund recipient creation will be different depending on the currency you will send transfers from. The above example is for GBP only. We can provide the correct format for your region upon request.
Example Request (Create email recipient):
curl -X POST https://api.sandbox.transferwise.tech/v1/accounts \
-H "Authorization: Bearer <your api token>" \
-H "Content-Type: application/json" \
-d '{
"profile": <your profile id>,
"accountHolderName": "Ann Johnson",
"currency": "EUR",
"type": "email",
"details": {
"email": "ann.johnson@gmail.com"
}
}'
Example Response (Lookup email recipient):
{
"id": 13967196,
"business": null,
"profile": <your profile id>,
"accountHolderName": "Ann Johnson",
"currency": "EUR",
"country": null,
"type": "email",
"details": {
"address": {
"country": null,
"countryCode": null,
"firstLine": null,
"postCode": null,
"city": null,
"state": null
},
"email": "ann.johnson@gmail.com",
"legalType": "PRIVATE",
"accountNumber": null,
"sortCode": null,
"abartn": null,
"accountType": null,
"bankgiroNumber": null,
"ifscCode": null,
"bsbCode": null,
"institutionNumber": null,
"transitNumber": null,
"phoneNumber": null,
"bankCode": null,
"russiaRegion": null,
"routingNumber": null,
"branchCode": null,
"cpf": null,
"cardNumber": null,
"idType": null,
"idNumber": null,
"idCountryIso3": null,
"idValidFrom": null,
"idValidTo": null,
"clabe": null,
"swiftCode": null,
"dateOfBirth": null,
"clearingNumber": null,
"bankName": null,
"branchName": null,
"businessNumber": null,
"province": null,
"city": null,
"rut": null,
"token": null,
"cnpj": null,
"payinReference": null,
"pspReference": null,
"orderId": null,
"idDocumentType": null,
"idDocumentNumber": null,
"targetProfile": null,
"taxId": null,
"iban": null,
"bic": null,
"IBAN": null,
"BIC": null,
"interacAccount": null
},
"user": <your user id>,
"active": true,
"ownedByCustomer": false
}
If you don't know recipient bank account details you can set up an email recipient; Wise will collect bank details directly from the recipient.
Wise will email your recipient with a link to collect their bank account details securely. After the bank account details have been provided Wise will complete your transfer.
Please be aware of the following caveats:
Example Request:
curl -X POST https://api.sandbox.transferwise.tech/v1/transfers \
-H "Authorization: Bearer <your api token>" \
-H "Content-Type: application/json" \
-d '{
"sourceAccount": <refund recipient account id>,
"targetAccount": <recipient account id>,
"quoteUuid": <v2 quote id>,
"customerTransactionId": "<the unique identifier you generated for the transfer attempt>",
"details" : {
"reference" : "to my friend",
"transferPurpose": "verification.transfers.purpose.pay.bills",
"transferPurposeSubTransferPurpose": "verification.sub.transfers.purpose.pay.interpretation.service"
"sourceOfFunds": "verification.source.of.funds.other"
}
}'
Example Response:
{
"id": 468956,
"user": <your user id>,
"targetAccount": <recipient account id>,
"sourceAccount": null,
"quote": null,
"quoteUuid": <v2 quote id>,
"status": "incoming_payment_waiting",
"reference": "to my friend",
"rate": 0.9065,
"created": "2018-08-28 07:43:55",
"business": <your business profile id>,
"transferRequest": null,
"details": {
"reference": "to my friend"
},
"hasActiveIssues": false,
"sourceCurrency": "EUR",
"sourceValue": 661.89,
"targetCurrency": "GBP",
"targetValue": 600,
"customerTransactionId": "bd244a95-dcf8-4c31-aac8-bf5e2f3e54c0"
}
A transfer is a payment order to recipient account based on a quote. Once created, a transfer needs to be funded within the next five working days. Otherwise, it will be automatically canceled.
POST https://api.sandbox.transferwise.tech/v1/transfers
Field | Description | Format |
---|---|---|
sourceAccount (optional) | Refund recipient account id. | Integer |
targetAccount | Recipient account id. You can create multiple transfers to same recipient account. | Integer |
quoteUuid | V2 quote id. You can only create one transfer per one quote. You cannot use same quote ID to create multiple transfers. |
Text |
customerTransactionId | This is required to perform idempotency check to avoid duplicate transfers in case of network failures or timeouts. | UUID |
details.reference (optional) | Recipient will see this reference text in their bank statement. Maximum allowed characters depends on the currency route. Business Payments Tips article has a full list. | Text |
details.transferPurpose (conditionally required) | For example when target currency is THB. See more about conditions at Transfers.Requirements | Text |
details.transferPurposeSubTransferPurpose (conditionally required) | For example when target currency is CNY. See more about conditions at Transfers.Requirements | Text |
details.sourceOfFunds (conditionally required) | For example when target currency is USD and transfer amount exceeds 80k. See more about conditions at Transfers.Requirements | Text |
There are two options to deal with conditionally required fields:
You need to save the transfer id for tracking its status later via webhooks.
Field | Description | Format |
---|---|---|
id | Transfer id | Integer |
user | Your user id | Integer |
targetAccount | Recipient account id | Integer |
sourceAccount | Refund recipient account id | Integer |
quote | v1 quote id (where applicable) | Integer |
quoteUuid | v2 quote id | Text |
status | Transfer current status | Text |
reference | Deprecated, use details.reference instead | Text |
rate | Exchange rate value | Decimal |
created | Timestamp when transfer was created | Timestamp |
business | Your business profile id | |
transferRequest | Not used | Integer |
details.reference | Payment reference text | Text |
hasActiveIssues | Are there any pending issues which stop executing the transfer? | Boolean |
sourceCurrency | Source currency code | Text |
sourceValue | Transfer amount in source currency | Decimal |
targetCurrency | Target currency code | Text |
targetValue | Transfer amount in target currency | Decimal |
customerTransactionId | Unique identifier randomly generated per transfer request by the calling client | UUID |
We use customerTransactionId field to avoid duplicate transfer requests. If your initial call to create a transfer fails (error or timeout) then you should retry the call using the same value in the customerTransactionId field that you used in the original call. This way we can treat subsequent retry messages as repeat messages and will not create duplicate transfers to your account should one have succeeded before. You should not retry indefinitely but use a sensible limit, perhaps with a back-off approach.
Example Request:
curl -X POST https://api.sandbox.transferwise.tech/v3/profiles/{profileId}/transfers/{transferId}/payments \
-H "Authorization: Bearer <your api token>" \
-H "Content-Type: application/json" \
-d '{
"type": "BALANCE"
}'
Example Response:
{
"type": "BALANCE",
"status": "COMPLETED",
"errorCode": null
}
This API call is the final step for executing payouts. Upon calling the endpoint Wise will start processing your transfer and fund the transfer immediately if possible.
When using the BALANCE
option, Wise will debit funds from your multi-currency account and start processing your transfer. If your multi-currency account does not have the required funds to complete the action then this call will fail with an "insufficient funds" error.
When using the TRUSTED_PRE_FUND_BULK
option, Wise will count the transfer amount towards your held collateral. If there are not enough collateral funds to cover the amount of the transfer, the call will still succeed but be placed in a queue and funded when sufficient collateral is restored. For more information on this, please speak to our team.
This endpoint is not required if you fund transfers using the following methods:
If you are unsure if you need this please contact your Wise implementation team to clarify.
POST https://api.sandbox.transferwise.tech/v3/profiles/{profileId}/transfers/{transferId}/payments
Use transfer id that you obtained in previous step.
Field | Description | Format |
---|---|---|
type | "BALANCE". This indicates that your transfer will be funded from your multi-currency account. |
Text |
You need to save transfer id for tracking its status later.
Field | Description | Format |
---|---|---|
type | "BALANCE" | Text |
status | "COMPLETED" or "REJECTED" | Text |
errorCode | Failure reason. For example "balance.payment-option-unavailable" | Text |
Example Request:
curl -X PUT https://api.sandbox.transferwise.tech/v1/transfers/{transferId}/cancel \
-H "Authorization: Bearer <your api token>"
Example Response:
{
"id": 16521632,
"user": 4342275,
"targetAccount": 8692237,
"sourceAccount": null,
"quote": 657171,
"status": "cancelled",
"reference": "reference text",
"rate": 0.89,
"created": "2017-11-24 10:47:49",
"business": null,
"transferRequest": null,
"details": {
"reference": "Testing"
},
"hasActiveIssues": false,
"sourceCurrency": "EUR",
"sourceValue": 0,
"targetCurrency": "GBP",
"targetValue": 150,
"customerTransactionId": "54a6bc09-cef9-49a8-9041-f1f0c654cd88"
}
Only transfers which are not funded can be cancelled. Cancellation is final - it can not be undone.
PUT https://api.sandbox.transferwise.tech/v1/transfers/{transferId}/cancel
Use transfer id that you obtained when creating a transfer.
Field | Description | Format |
---|---|---|
id | Transfer id | Integer |
user | Your user id | Integer |
targetAccount | Recipient account id | Integer |
sourceAccount | Refund recipient account id | Integer |
quote | Quote id | Integer |
status | Transfer current status | Text |
reference | Deprecated, use details.reference instead | Text |
rate | Exchange rate value | Decimal |
created | Timestamp when transfer was created | Timestamp |
business | Your business profile id | |
transferRequest | Not used | Integer |
details.reference | Payment reference text | Text |
hasActiveIssues | Are there any pending issues which stop executing the transfer? | Boolean |
sourceCurrency | Source currency code | Text |
sourceValue | Transfer amount in source currency | Decimal |
targetCurrency | Target currency code | Text |
targetValue | Transfer amount in target currency | Decimal |
customerTransactionId | Unique identifier randomly generated per transfer request by the calling client | UUID |
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v1/transfers/{transferId} \
-H "Authorization: Bearer <your api token>"
Example Response:
{
"id": 15574445,
"user": 294205,
"targetAccount": 7993919,
"sourceAccount": null,
"quote": 113379,
"status": "incoming_payment_waiting",
"reference": "good times",
"rate": 1.2151,
"created": "2017-03-14 15:25:51",
"business": null,
"transferRequest": null,
"details": {
"reference": "good times"
},
"hasActiveIssues": false,
"sourceValue": 1000,
"sourceCurrency": "EUR",
"targetValue": 895.32,
"targetCurrency": "GPB",
"customerTransactionId": "6D9188CF-FA59-44C3-87A2-4506CE9C1EA3"
}
Get transfer info by id. Since we don't have push notifications yet, you can poll this endpoint to track your transfer status.
GET https://api.sandbox.transferwise.tech/v1/transfers/{transferId}
Field | Description | Format |
---|---|---|
id | Transfer id | Integer |
user | Your user id | Integer |
targetAccount | Recipient account id | Integer |
sourceAccount | Refund recipient account id | Integer |
quote | Quote id | Integer |
status | Transfer current status | Text |
reference | Deprecated, use details.reference instead | Text |
rate | Exchange rate value | Decimal |
created | Timestamp when transfer was created | Timestamp |
business | Your business profile id | |
transferRequest | Not used | Integer |
details.reference | Payment reference text | Text |
hasActiveIssues | Are there any pending issues which stop executing the transfer? | Boolean |
sourceCurrency | Source currency code | Text |
sourceValue | Transfer amount in source currency | Decimal |
targetCurrency | Target currency code | Text |
targetValue | Transfer amount in target currency | Decimal |
customerTransactionId | Unique identifier randomly generated per transfer request by the calling client | UUID |
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v1/delivery-estimates/{transferId} \
-H "Authorization: Bearer <your api token>"
Example Response:
{
"estimatedDeliveryDate" : "2018-01-10T12:15:00.000+0000"
}
Get the live delivery estimate for a transfer by the transfer ID.
The delivery estimate is the time at which we currently expect the transfer to arrive in the beneficiary's bank account.
This is not a guaranteed time but we are working hard to make these estimates as accurate as possible.
GET https://api.sandbox.transferwise.tech/v1/delivery-estimates/{transferId}
Field | Description | Format |
---|---|---|
estimatedDeliveryDate | Estimated time when funds will arrive to recipient's bank account | Timestamp |
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v1/transfers/{transferId}/receipt.pdf \
-H "Authorization: Bearer <your api token>"
Example Response:
Receipt presented as application/pdf content-type
Download transfer confirmation receipt in PDF format for transfers that are in status outgoing_payment_sent.
GET https://api.sandbox.transferwise.tech/v1/transfers/{transferId}/receipt.pdf
Transfer confirmation receipt in PDF format.
Example Request:
curl -X GET https://api.sandbox.transferwise.tech/v1/transfers?offset=0&limit=100&profile=<your profile id>&status=funds_refunded&createdDateStart=2018-12-15&createdDateEnd=2018-12-30 \
-H "Authorization: Bearer <your api token>"
Example Response:
[
{
"id": 15574445,
"user": 294205,
"targetAccount": 7993919,
"sourceAccount": null,
"quote": 113379,
"status": "funds_refunded",
"reference": "good times",
"rate": 1.1179,
"created": "2018-12-16 15:25:51",
"business": null,
"transferRequest": null,
"details": {
"reference": "good times"
},
"hasActiveIssues": false,
"sourceValue": 1000,
"sourceCurrency": "EUR",
"targetValue": 895.32,
"targetCurrency": "GPB",
"customerTransactionId": "6D9188CF-FA59-44C3-87A2-4506CE9C1EA3"
},
{
"id": 14759252,
"user": 294205,
"targetAccount": 5570192,
"sourceAccount": null,
"quote": 113371,
"status": "funds_refunded",
"reference": "",
"rate": 1.1179,
"created": "2018-12-26 15:25:51",
"business": null,
"transferRequest": null,
"details": {
"reference": ""
},
"hasActiveIssues": false,
"sourceValue": 1000,
"sourceCurrency": "EUR",
"targetValue": 895.32,
"targetCurrency": "GPB",
"customerTransactionId": "785C67AD-7E29-4DBC-9D4A-4C45D4D5333A"
}
]
Get the list of transfers for given user's profile (defaults to user's personal profile).
You can add query parameters to specify user's profile (personal or business), time period and/or payment status.
For example you can query:
GET https://api.sandbox.transferwise.tech/v1/transfers/?offset=0&limit=100&profile=<your profile id>&status=funds_refunded&sourceCurrency=EUR&createdDateStart=2018-12-15T01:30:00.000Z&createdDateEnd=2018-12-30T01:30:00.000Z
Field | Description | Format |
---|---|---|
profile | User profile id. If parameter is omitted, defaults to user's personal profile | Integer |
status | Status code or codes list (as comma separated value list) to filter returned transfers with. See Track transfer status for complete list of statuses. | Text |
sourceCurrency | Source currency code | Text |
targetCurrency | Target currency code | Text |
createdDateStart | Starting date to filter transfers, inclusive of the provided date. | yyyy-MM-dd'T'HH:mm:ss.SSS'Z' |
createdDateEnd | Ending date to filter transfers, inclusive of the provided date. | yyyy-MM-dd'T'HH:mm:ss.SSS'Z' |
limit | Maximum number of records to be returned in response | Integer |
offset | Starting record number | Integer |
Field | Description | Format |
---|---|---|
id | Transfer id | Integer |
user | Your user id | Integer |
targetAccount | Recipient account id | Integer |
sourceAccount | Refund recipient account id | Integer |
quote | Quote id | Integer |
status | Transfer current status | Text |
reference | Deprecated, use details.reference instead | Text |
rate | Exchange rate value | Decimal |
created | Timestamp when transfer was created | Timestamp |
business | Your business profile id | |
transferRequest | Not used | Integer |
details.reference | Payment reference text | Text |
hasActiveIssues | Are there any pending issues which stop executing the transfer? | Boolean |
sourceCurrency | Source currency code | Text |
sourceValue | Transfer amount in source currency | Decimal |
targetCurrency | Target currency code | Text |
targetValue | Transfer amount in target currency | Decimal |
customerTransactionId | Unique identifier assigned by customer. Used for idempotency check purposes. | UUID |
Example Request:
curl -X POST https://api.sandbox.transferwise.tech/v1/transfer-requirements \
-H "Authorization: Bearer <your api token>" \
-H "Content-Type: application/json" \
-d '{
"targetAccount": <recipient account id>,
"quoteUuid": <quote uuid>,
"details": {
"reference": "good times",
"sourceOfFunds": "verification.source.of.funds.other",
"sourceOfFundsOther": "Trust funds"
},
"customerTransactionId": "6D9188CF-FA59-44C3-87A2-4506CE9C1EA3"
}'
Example Response:
[
{
"type": "transfer",
"fields": [
{
"name": "Transfer reference",
"group": [
{
"key": "reference",
"name": "Transfer reference",
"type": "text",
"refreshRequirementsOnChange": false,
"required": false,
"displayFormat": null,
"example": null,
"minLength": null,
"maxLength": 10,
"validationRegexp": null,
"validationAsync": null,
"valuesAllowed": null
}
]
},
{
"name": "Transfer purpose",
"group": [
{
"key": "transferPurpose",
"name": "Transfer purpose",
"type": "select",
"refreshRequirementsOnChange": true,
"required": true,
"displayFormat": null,
"example": null,
"minLength": null,
"maxLength": null,
"validationRegexp": null,
"validationAsync": null,
"valuesAllowed": [
{
"key": "verification.transfers.purpose.purchase.property",
"name": "Buying property abroad"
},
{
"key": "verification.transfers.purpose.pay.bills",
"name": "Rent or other property expenses"
},
{
"key": "verification.transfers.purpose.mortgage",
"name": "Mortgage payment"
},
{
"key": "verification.transfers.purpose.pay.tuition",
"name": "Tuition fees or studying expenses"
},
{
"key": "verification.transfers.purpose.send.to.family",
"name": "Sending money home to family"
},
{
"key": "verification.transfers.purpose.living.expenses",
"name": "General monthly living expenses"
},
{
"key": "verification.transfers.purpose.other",
"name": "Other"
}
]
},
{
"key": "transferPurposeSubTransferPurpose",
"name": "Please select a specific reason for your transfer",
"type": "select",
"refreshRequirementsOnChange": true,
"required": true,
"displayFormat": null,
"example": null,
"minLength": null,
"maxLength": null,
"validationRegexp": null,
"validationAsync": null,
"valuesAllowed": [
{
"key": "INTERPRETATION_SERVICE",
"name": "Interpretation service"
},
{
"key": "TRANSLATION_SERVICE",
"name": "Translation service"
},
{
"key": "HUMAN_RESOURCE_SERVICE",
"name": "Human resource service"
},
{
"key": "ESTATE_AGENCY_SERVICE",
"name": "Estate agency service"
},
{
"key": "SOFTWARE_DEVELOPMENT_SERVICE",
"name": "Software development service"
},
{
"key": "WEB_DESIGN_OR_DEVELOPMENT_SERVICE",
"name": "Web design or development service"
},
{
"key": "DRAFTING_LEGAL_SERVICE",
"name": "Drafting legal service"
},
{
"key": "LEGAL_RELATED_CERTIFICATION_SERVICE",
"name": "Legal related certification service"
},
{
"key": "ACCOUNTING_SERVICE",
"name": "Accounting service"
},
{
"key": "TAX_SERVICE",
"name": "Tax service"
},
{
"key": "ARCHITECTURAL_DECORATION_DESIGN_SERVICE",
"name": "Architectural decoration design service"
},
{
"key": "ADVERTISING_SERVICE",
"name": "Advertising service"
},
{
"key": "MARKET_RESEARCH_SERVICE",
"name": "Market research service"
},
{
"key": "EXHIBITION_BOOTH_SERVICE",
"name": "Exhibition booth service"
}
]
}
]
},
{
"name": "Source of funds",
"group": [
{
"key": "sourceOfFunds",
"name": "Source of funds",
"type": "select",
"refreshRequirementsOnChange": true,
"required": true,
"displayFormat": null,
"example": null,
"minLength": null,
"maxLength": null,
"validationRegexp": null,
"validationAsync": null,
"valuesAllowed": [
{
"key": "verification.source.of.funds.salary",
"name": "Salary"
},
{
"key": "verification.source.of.funds.investment",
"name": "Investments (stocks, properties, etc.)"
},
{
"key": "verification.source.of.funds.inheritance",
"name": "Inheritance"
},
{
"key": "verification.source.of.funds.loan",
"name": "Loan"
},
{
"key": "verification.source.of.funds.other",
"name": "Other"
}
]
}
]
}
]
}
]
Almost every region has its own specific nuances when it comes to the nitty gritty details of domestic payment systems and money transfer regulations. The maximum allowed length of reference text is a good example. The US payment system, ACH, supports 10 characters only, but transfers within Mexico allow up to 100 characters, and so on.
The same is true for requirements arising from Anti Money Laundering regulations adopted in different countries. Depending on the chosen currencies and the amount to be transferred, either in one go or cumulatively over time, Wise may require more details about the customer's source of funds or transfer purpose, for example.
The endpoint /transfer-requirements
exposes all these specific requirements based on the sender, the specific quote and selected target recipient account.
To make sure that processing of your customer's transfers does not get delayed because of missing details, we highly recommend to verify the transfer requirements before submitting any transfer and collecting the data we request from the user using the returned dynamic form.
POST https://api.sandbox.transferwise.tech/v1/transfer-requirements
Prepare the request body to create transfer object first.
Now post this request body to the transfer-requirements
endpoint to figure out if there are any other required fields.
Analyze the returned list of fields. Our example includes reference, sourceOfFunds and transferPurpose fields. Field 'reference' is optional. Fields 'sourceOfFunds' and 'transferPurpose' are required and both have
refreshRequirementsOnChange=true which indicates that there could be additional fields required depending on the selected value.
In our example you will have to POST request to /v1/transfer-requirements
second time as well with values set for 'transferPurpose' and 'sourceOfFunds'.
So in case you set sourceOfFunds = 'verification.source.of.funds.other' then another text field called "sourceOfFundsOther" is also required where you need to specify the details in free format.
Once you get to the point where you have provided values for all fields which have refreshRequirementsOnChange=true then you have complete set of fields to compose a valid request to create a transfer object. For example this is a valid request to create a transfer.
POST /v1/transfers
{
"targetAccount": <recipient account id>,
"quote": <quote id>,
"details": {
"reference": "good times",
"sourceOfFunds": "verification.source.of.funds.other",
"sourceOfFundsOther": "Trust funds"
},
"customerTransactionId": "6D9188CF-FA59-44C3-87A2-4506CE9C1EA3"
}
Field | Description | Format |
---|---|---|
type | "transfer" | Text |
fields[n].name | Field description | Text |
fields[n].group[n].key | Key is name of the field you should include in the JSON | Text |
fields[n].group[n].name | Field description | Text |
fields[n].group[n].type | Display type of field (e.g. text, select, etc) | Text |
fields[n].group[n].refreshRequirementsOnChange | Tells you whether you should call POST transfer-requirements once the field value is set to discover required lower level fields. | Boolean |
fields[n].group[n].required | Indicates if the field is mandatory or not | Boolean |
fields[n].group[n].displayFormat | Display format pattern. | Text |
fields[n].group[n].example | Example value. | Text |
fields[n].group[n].minLength | Min valid length of field value. | Integer |
fields[n].group[n].maxLength | Max valid length of field value. | Integer |
fields[n].group[n].validationRegexp | Regexp validation pattern. | Text |
fields[n].group[n].validationAsync | Validator URL and parameter name you should use when submitting the value for validation | Text |
fields[n].group[n].valuesAllowed[n].key | List of allowed values. Value key | Text |
fields[n].group[n].valuesAllowed[n].name | List of allowed values. Value name. | Text |
Example Request (Originator Type = PRIVATE):
curl --location -g --request POST 'https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/partner-licence-transfers' \
--header 'Authorization: Bearer <user-token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"targetAccount": 99999,
"sourceAccount": 123456,
"quoteUuid": "daa83d36-5fad-47bf-8923-6f2e0463cd6c",
"customerTransactionId": "b77dcbe3-53fc-423d-9c18-1aea91d0a0b9",
"details": {
"reference": "Holiday"
},
"originator": {
"legalEntityType": "PRIVATE",
"externalId": "123abc",
"name": {
"givenName": "John",
"middleNames": [
"Jay"
],
"familyName": "Doe"
},
"dateOfBirth": "1977-07-01",
"address": {
"firstLine": "56 Shoreditch",
"city": "Tallinn",
"countryCode": "US",
"postCode": "12112",
"stateCode": "NV"
}
}
}'
Example Request (Originator Type = BUSINESS):
curl --location -g --request POST 'https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/partner-licence-transfers' \
--header 'Authorization: Bearer <user-token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"targetAccount": 99999,
"sourceAccount": 123456,
"quoteUuid": "200d42dd-defe-4b64-b8f7-872ae72c1f0b",
"customerTransactionId": "0f330553-aba1-425e-8310-6415118d1bd9",
"details": {
"reference": "invoice 123"
},
"originator": {
"legalEntityType": "BUSINESS",
"externalId": "54321",
"name": {
"fullName": "Some Biz PLC"
},
"businessRegistrationCode": "00000001234",
"address": {
"firstLine": "56 Shoreditch",
"city": "Tallinn",
"countryCode": "US",
"postCode": "12112",
"stateCode": "NV"
}
}
}'
Example Response (Originator Type = PRIVATE):
{
"id": 123456789,
"customerTransactionId": "b77dcbe3-53fc-423d-9c18-1aea91d0a0b9",
"user": 55555,
"targetAccount": 99999,
"sourceAccount": 123456,
"quoteUuid": "daa83d36-5fad-47bf-8923-6f2e0463cd6c",
"status": "incoming_payment_waiting",
"rate": 0.8762,
"details": {
"reference": "Holiday"
},
"hasActiveIssues": false,
"sourceCurrency": "EUR",
"sourceValue": 497.46,
"targetCurrency": "GBP",
"targetValue": 427.16,
"created": "2021-02-18T12:59:42.000",
"originator": {
"legalEntityType": "PRIVATE",
"externalId": "123abc",
"name": {
"givenName": "John",
"middleNames": [
"Jay"
],
"familyName": "Doe"
},
"dateOfBirth": "1977-07-01",
"address": {
"firstLine": "56 Shoreditch ",
"city": "Tallinn",
"countryCode": "EE",
"postCode": "12112"
}
}
}
Example Response (Originator Type = BUSINESS):
{
"id": 123456790,
"customerTransactionId": "0f330553-aba1-425e-8310-6415118d1bd9",
"user": 55555,
"sourceAccount": 123456,
"targetAccount": 99999,
"quoteUuid": "200d42dd-defe-4b64-b8f7-872ae72c1f0b",
"status": "incoming_payment_waiting",
"rate": 0.8762,
"details": {
"reference": "invoice 123"
},
"hasActiveIssues": false,
"sourceCurrency": "JPY",
"sourceValue": 497.46,
"targetCurrency": "GBP",
"targetValue": 427.16,
"originator": {
"legalEntityType": "BUSINESS",
"externalId": "54321",
"name": {
"fullName": "Some Biz PLC"
},
"businessRegistrationCode": "00000001234",
"address": {
"firstLine": "1 Aaa Road",
"city": "London",
"countryCode": "GB",
"postCode": "N1 1AA"
}
},
"created": "2019-02-18T12:59:42.000"
}
POST https://api.sandbox.transferwise.tech/v1/profiles/{profileId}/partner-licence-transfers
This is very similar to Create transfers endpoint, but please note these differences:
Field | Description | Format |
---|---|---|
targetAccount | Recipient account id. You can create multiple transfers to same recipient account. | Integer |
sourceAccount (optional) | Refund recipient account id. | Integer |
quoteUuid | V2 quote id. You can only create one transfer per one quote. You cannot use same quote ID to create multiple transfers. |
UUID |
customerTransactionId | Unique transfer id. We use this field to perform idempotency check to avoid duplicate transfers in case of network failures or timeouts. You can only submit one transfer with same customerTransactionId. | UUID |
details.reference (optional) | Recipient will see this reference text in their bank statement. Maximum allowed characters depends on the currency route. Tips for paying invoices article has a full list. | Text |
details.transferPurpose (conditionally required) | For example when target currency is THB. See more about conditions at Transfers.Requirements | Text |
originator | Data block to capture payment originator details. | Group |
originator.legalEntityType | PRIVATE or BUSINESS. Payment originator legal type. | Text |
originator.externalId | Unique customer id in your system. This allows us to uniquely identify each originator. Required. | Text |
originator.name.givenName | Payment originator first name. Required if legalEntityType = PRIVATE. | Text |
originator.name.middleNames | Payment originator middle name(s). Used only if legalEntityType = PRIVATE. Optional | Text Array |
originator.name.familyName | Payment originator family name. Required if legalEntityType = PRIVATE. | Text |
originator.name.patronymicName | Payment originator patronymic name. Used only if legalEntityType = PRIVATE. Optional | Text |
originator.name.fullName | Payment originator full legal name. Required if legalEntityType = BUSINESS. | Text |
originator.dateOfBirth | Payment originator date of birth. Required if legalEntityType = PRIVATE. | YYYY-MM-DD |
originator.businessRegistrationCode | Payment originator business registry number / incorporation number. Required if legalEntityType = BUSINESS. | Text |
originator.address.firstLine | Payment originator address first line. Required | Text |
originator.address.city | Payment originator address city. Required | Text |
originator.address.stateCode | Payment originator address state code. Required if address country code in (US, CA, BR, AU). | Text |
originator.address.countryCode | Payment originator address first line. Required | Text |
originator.address.postCode | Originator address zip code. Optional | Text |
You need to save transfer id for tracking its status later.
Field | Description | Format |
---|---|---|
id | Transfer id | Integer |
customerTransactionId | Transfer unique id provided by a caller | UUID |
user | Your user id | Integer |
targetAccount | Recipient account id | Integer |
sourceAccount | Refund recipient account id. | Integer |
quote | Not used | Integer |
quoteUuid | v2 quote id | Text |
status | Transfer current status | Text |
rate | Exchange rate value | Decimal |
details.reference | Payment reference text | Text |
hasActiveIssues | Are there any pending issues which stop executing the transfer? | Boolean |
sourceCurrency | Source currency code | Text |
sourceValue | Transfer amount in source currency | Decimal |
targetCurrency | Target currency code | Text |
targetValue | Transfer amount in target currency | Decimal |
originator | Data block to capture payment originator details | Group |
originator.legalEntityType | Payment originator legal type. | Text |
originator.externalId | Unique customer id in your system. | Text |
originator.name.givenName | Payment originator first name. | Text |
originator.name.middleNames | Payment originator middle name(s). | Text Array |
originator.name.familyName | Payment originator family name. | Text |
originator.name.patronymicName | Payment originator patronymic name. | Text |
originator.name.fullName | Payment originator full legal name. | Text |
originator.dateOfBirth | Payment originator date of birth. | YYYY-MM-DD |
originator.businessRegistrationCode | Payment originator business registry number / incorporation number. | Text |
originator.address.firstLine | Payment originator address first line. | Text |
originator.address.city | Payment originator address city. | Text |
originator.address.stateCode | Payment originator address state code. | Text |
originator.address.countryCode | Payment originator address first line. | Text |
originator.address.postCode | Originator address zip code. | Text |
created | Timestamp when transfer was created | Timestamp |
We use customerTransactionId field to avoid duplicate transfer requests. When your first call fails (error or timeout) then you should use the same value in customerTransactionId field that you used in the original call when you are submitting a retry message. This way we can treat subsequent retry messages as repeat messages and will not create duplicate transfers to your account. We recommend using UUID V4 to avoid collisions.
Please refer to our Transfers section.
Please refer to our Transfer Fund section.
Example Request:
curl --location --request GET 'https://api.transferwise.com/v1/profiles/123456/transfers/987654/deposit-details/bank-transfer' \
--header 'Authorization: Bearer <your api token>' \
--header 'x-mock-response-name: get-deposit-details-success' \
--header 'Content-Type: application/json' \
--header 'Accept-Language: en-GB'
Example Response:
{
"payinBank": {
"bankName": "TransferWise Europe SA",
"bankAddress": {
"country": "BE",
"firstLine": "Avenue Louise 54, Room s52",
"postCode": "1050",
"city": "Brussels",
"state": null
}
},
"payinBankAccount": {
"currency": "EUR",
"details": [
{
"type": "iban",
"label": "IBAN",
"value": "BE11111111111111"
},
{
"type": "bic",
"label": "Bank code (BIC/SWIFT)",
"value": "TRWIBEB1XXX"
},
{
"type": "recipientName",
"label": "Recipient name",
"value": "TransferWise Europe SA"
}
]
},
"wiseInformation": {
"localCompanyName": "TransferWise Europe SA",
"localAddress": {
"country": "United Kingdom",
"firstLine": "6th Floor, The Tea Building, 56 Shoreditch High Street",
"postCode": "E1 6JJ",
"city": "London",
"state": null
}
}
}
The payin deposit details API allows you to get the bank details for the account that the customer should send funds to when paying for a Wise transfer via a bank transfer. These details will be provided in the local format for that currency and usually contain bank account information - like iban, swift code etc. It also includes the name and address of the receiving bank (payinBank
) and the name and address of the Wise entity that owns the bank account (wiseInformation
) as sometimes these are required to make a payment.
The payinBankAccount
field allows the bank details to be displayed dynamically in a user interface, by displaying the label and value fields.
Currently this API supprts the following currencies:
Currency |
---|
AUD |
BGN |
BRL |
CAD |
CHF |
CZK |
DKK |
EUR |
GBP |
HKD |
HRK |
HUF |
IDR |
INR |
JPY |
MYR |
NOK |
NZD |
PLN |
RON |
SEK |
SGD |
TRY |
USD |
GET https://api.transferwise.com/v1/profiles/{profileId}/transfers/{transferId}/deposit-details/bank-transfer
Field | Description | Format |
---|---|---|
profileId | Profile id. | Text |
transferId | Transfer id. | Text |
Field | Description | Format |
---|---|---|
payinBank | Information about the receiving bank. | Object |
payinBank.bankName | Bank name. | Text |
payinBank.bankAddress | Bank address. | Object (can be null) |
payinBank.bankAddress.country | Country ISO 2 code | Text |
payinBank.bankAddress.firstLine | Street address | Text |
payinBank.bankAddress.postCode | Post code / zip code | Text |
payinBank.bankAddress.city | City | Text |
payinBank.bankAddress.state | State. Can be null. | Text (can be null) |
payinBankAccount | Bank account details to use to send the payment to. | Object |
payinBankAccount.currency | ISO 4217 source currency code | Text |
payinBankAccount.details | Account details | Array of Objects |
payinBankAccount.details[n].type | Account details type e.g. accountNumber, iban etc. | Text |
payinBankAccount.details[n].label | Account details label that should be displayed in your user interface. | Text |
payinBankAccount.details[n].value | Account details value - the value of account details (like iban, account number etc). | Text |
wiseInformation | Information about the receiving Wise entity, the owner of the bank account. | Object |
wiseInformation.localCompanyName | Wise local company name. | Text |
wiseInformation.localAddress | Wise local address. | Object (can be null) |
wiseInformation.localAddress.country | Country ISO 2 code | Text |
wiseInformation.localAddress.firstLine | Street address | Text |
wiseInformation.localAddress.postCode | Post code / zip code | Text |
wiseInformation.localAddress.city | City | Text |
wiseInformation.localAddress.state | State. Can be null. | Text (can be null) |
Example Request:
curl -X GET "https://api.sandbox.transferwise.tech/v1/simulation/transfers/{transferId}/processing" \
-H "Authorization: Bearer <your api token>"
Example Response:
{
"id": 15574445,
"user": 294205,
"targetAccount": 7993919,
"sourceAccount": null,
"quote": 113379,
"status": "processing",
"reference": "good times",
"rate": 1.2151,
"created": "2017-03-14 15:25:51",
"business": null,
"transferRequest": null,
"details": {
"reference": "good times"
},
"hasActiveIssues": false,
"sourceValue": 1000,
"sourceCurrency": "EUR",
"targetValue": 895.32,
"targetCurrency": "GPB"
}
You can simulate payment processing by changing transfer statuses using these endpoints.
This feature is limited to sandbox only.
GET https://api.sandbox.transferwise.tech/v1/simulation/transfers/{transferId}/processing
Changes transfer status from incoming_payment_waiting to processing.
GET https://api.sandbox.transferwise.tech/v1/simulation/transfers/{transferId}/funds_converted
Changes transfer status from processing to funds_converted.
GET https://api.sandbox.transferwise.tech/v1/simulation/transfers/{transferId}/outgoing_payment_sent
Changes transfer status from funds_converted to outgoing_payment_sent.
GET https://api.sandbox.transferwise.tech/v1/simulation/transfers/{transferId}/bounced_back
Changes transfer status from outgoing_payment_sent to bounced_back.
GET https://api.sandbox.transferwise.tech/v1/simulation/transfers/{transferId}/funds_refunded
Changes transfer status from bounced_back to funds_refunded.
GET https://api.sandbox.transferwise.tech/v1/simulation/transfers/{transferId}/webhook_refund
Triggers a webhook refund for the transfer (requires previous custom configuration on both ends, please contact support for more info).
The transfer needs to be in INCOMING_PAYMENT_WAITING
state to trigger a webhook refund, so please refrain from using any of the transfer status change APIs above before this.
You only need to use this state if you are using a webhook based refund solution, which not all integrations require.
Transfer entity with changed status.
Example Request:
curl -L -X POST 'https://api.sandbox.transferwise.tech/v1/simulation/verify-profile' \
-H 'Authorization: Bearer <your api token>'
This endpoint verifies the user's profiles. The webhook event profiles#verification-state-change
will be triggered which can help to test the integration.
Having a verified user is a requirement for MCA setup, otherwise account details will not be issued.
POST https://api.sandbox.transferwise.tech/v1/simulation/verify-profile
Verify all user profiles. It also triggers the notification webhook.
Use the user api token in the request. The profiles will be fetched based on the token's authentication details.
Example Request:
curl -L -X POST 'https://api.sandbox.transferwise.tech/v1/simulation/balance/topup' \
-H 'Authorization: Bearer <your api token>' \
-H 'Content-Type: application/json' \
-d '{
"profileId": 2,
"balanceId": 5,
"currency": "EUR",
"amount": 100
}'
Example Response:
{
"transactionId": 5,
"state": "COMPLETED",
"balancesAfter": [
{
"id": 5,
"value": 100.00,
"currency": "EUR"
}
]
}
Simulates a topup so a balance can be used to fund transfers.
POST https://api.sandbox.transferwise.tech/v1/simulation/balance/topup
Field | Description | Format |
---|---|---|
profileId | User profile id | Number |
balanceId | Balance id | Number |
currency | Balance currency (should be the same currency as the balance) | Text |
amount | Top up amount | Number |
This endpoint simulates a card transaction authorisation in the sandbox environment. It can simulate ATM withdrawals and purchasing transactions.
POST https://api.sandbox.transferwise.tech/v1/simulation/spend/profiles/{profileId}/cards/{cardToken}/transactions/authorisation
The transactionType
field is optional. You can either set it to CASH_WITHDRAWAL
for ATM withdrawals or remove it for other card transactions.
The possible pos
values are:
CHIP_AND_PIN
CONTACTLESS_CARD
E_COMMERCE
MAGNETIC_STRIPE
MINIMAL
MOBILE_WALLET
The PIN checks has been removed on the card transaction authorisation simulation API endpoint. Any 4-digit number is accepted for the PIN.
The CVV1, ICVV and CVV2 values should be "123", "456" and "789" respectively.
Example request
curl --location --request POST 'https://api.sandbox.transferwise.tech/v1/simulation/spend/profiles/{profileId}/cards/{cardToken}/transactions/authorisation' \
--header 'Authorization: Bearer <your api token>'
--header 'Content-Type: application/json' \
--data '{
"pos": "CHIP_AND_PIN",
"transactionType": "CASH_WITHDRAWAL",
"amount": {
"value": <transaction amount>
"currency": "<ISO 4217 transaction currency code>"
},
"acquirer": {
"institutionId": <Acquiring Institution ID>,
"name": "<acquirer name>,
"city": "<acquirer city>",
"merchantCategoryCode": <merchant category code>,
"country": "<2 letter country code using ISO 3166-1 alpha-2>",
"acceptorTerminalId": "<Card Acceptor Terminal ID>",
"acceptorIdCode": "<Card Acceptor ID Code>",
"forwardingInstitutionId": "<Acquiring Institution ID>",
},
"cardData": {
"pan": "<card 16-digit PAN>",
"pin": "1234",
"cvv1": "123" ,
"icvv": "456",
"cvv2": "789"
}
}'
Example response
{
"reference": {
"transaction": {
"acquirer": {
"institutionId": "430001",
"name": "Test ATM withdrawal",
"city": "Rouen",
"merchantCategoryCode": 5999,
"country": "FR",
"acceptorTerminalId": "TERMID01",
"acceptorIdCode": "CARD ACCEPTOR",
"forwardingInstitutionId": "400050"
},
"card": {
"token": "9dbf5c07-c142-4986-aa8e-2c2def9f6874",
"schemeName": "VISA",
"pan": "<card 16-digit PAN>",
"pin": "1234",
"cvv1": "123",
"icvv": "456",
"cvv2": "789",
"expiration": [
2027,
9
],
"sequenceNumber": 1,
"country": "AU",
"currencies": [
"AUD"
]
},
"pos": {
"type": "CHIP_AND_PIN",
"acceptsOnlinePins": true,
"maxPinLength": 12,
"supports3ds": false,
"hasChip": true
},
"transactionStartTime": 1667541087.047643305,
"stan": "363054",
"schemeTransactionId": "932290252416153",
"retrievalReferenceNum": "230805363054"
},
"requestMti": "0200",
"authorizationIdResponse": "123646"
},
"error": null
}
This endpoint simulates a clearing request in the sandbox environment.
POST https://api.sandbox.transferwise.tech/v1/simulation/spend/profiles/{profileId}/cards/{cardToken}/transactions/clearing
The ref
field should be set to the same value as reference
from authorisation response.
Example request
curl --location --request POST 'https://api.sandbox.transferwise.tech/v1/simulation/spend/profiles/{profileId}/cards/{cardToken}/transactions/clearing' \
--header 'Authorization: Bearer <your api token>'
--header 'Content-Type: application/json' \
--data '{
"amount": {
"value": <clearing amount>
"currency": "<ISO 4217 transaction currency code>"
},
"ref": {
"transaction": {
"acquirer": {
"institutionId": "430001",
"name": "Test ATM withdrawal",
"city": "Rouen",
"merchantCategoryCode": 5999,
"country": "FR",
"acceptorTerminalId": "TERMID01",
"acceptorIdCode": "CARD ACCEPTOR",
"forwardingInstitutionId": "400050"
},
"card": {
"token": "9dbf5c07-c142-4986-aa8e-2c2def9f6874",
"schemeName": "VISA",
"pan": "<card 16-digit PAN>",
"pin": "1234",
"cvv1": "123",
"icvv": "456",
"cvv2": "789",
"expiration": [
2027,
9
],
"sequenceNumber": 1,
"country": "AU",
"currencies": [
"AUD"
]
},
"pos": {
"type": "CHIP_AND_PIN",
"acceptsOnlinePins": true,
"maxPinLength": 12,
"supports3ds": false,
"hasChip": true
},
"transactionStartTime": 1667541087.047643305,
"stan": "363054",
"schemeTransactionId": "932290252416153",
"retrievalReferenceNum": "230805363054"
},
"requestMti": "0200",
"authorizationIdResponse": "123646"
}
}'
Example response
{
"reference": {
"transaction": {
"acquirer": {
"institutionId": "430001",
"name": "Test ATM withdrawal",
"city": "Rouen",
"merchantCategoryCode": 5999,
"country": "FR",
"acceptorTerminalId": "TERMID01",
"acceptorIdCode": "CARD ACCEPTOR",
"forwardingInstitutionId": "400050"
},
"card": {
"token": "9dbf5c07-c142-4986-aa8e-2c2def9f6874",
"schemeName": "VISA",
"pan": "<card 16-digit PAN>",
"pin": "1234",
"cvv1": "123",
"icvv": "456",
"cvv2": "789",
"expiration": [
2027,
9
],
"sequenceNumber": 1,
"country": "AU",
"currencies": [
"AUD"
]
},
"pos": {
"type": "CHIP_AND_PIN",
"acceptsOnlinePins": true,
"maxPinLength": 12,
"supports3ds": false,
"hasChip": true
},
"transactionStartTime": 1667541087.047643305,
"stan": "363054",
"schemeTransactionId": "932290252416153",
"retrievalReferenceNum": "230805363054"
},
"requestMti": "0200",
"authorizationIdResponse": "123646"
},
"error": null
}
Application webhook subscription is a mechanism that will allow you to receive notifications to your servers whenever various events happen in relation to different resources created by an application.
Before proceeding, make sure the endpoint where you intend to receive webhooks satisfies the following requirements:
443
https://webhooks.example.com/transfers-state-change
is a valid URL; http://webhooks.example.com:8080/hook.php?type=transfers-state
is not.
You can have multiple subscriptions per event type though be mindful you will receive duplicate callbacks, one for each subscription. Find out more about webhook events here.
POST https://api.sandbox.transferwise.tech/oauth/token
Use Basic Authentication with your api-client-id and api-client-secret as username and password. be sure to also use the header Content-Type: application/x-www-form-urlencoded
.
Field | Description | Format |
---|---|---|
grant_type | "client_credentials" | Text |
Field | Description | Format |
---|---|---|
access_token | Access token to be used when creating an application subscription. Valid for 12 hours. | Text |
token_type | "bearer" | Text |
expires_in | Expiry time in seconds | Integer |
scope | Text |
Example Request:
curl -X POST "https://api.sandbox.transferwise.tech/v3/applications/{clientKey}/subscriptions" \
-H "Authorization: Bearer <your client level token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Webhook Subscription #1",
"trigger_on": "transfers#state-change",
"delivery": {
"version": "2.0.0",
"url": "https://your.webhook.url/12345"
},
"request_headers":[
{
"header_name":"Ocp-Apim-Subscription-Key",
"header_value":"34ee49a843874882ae8075bd02a60dfa"
}
]
}'
Example Response:
{
"id": "72195556-e5cb-495e-a010-b37a4f2a3043",
"name": "Webhook Subscription #1",
"delivery": {
"version": "2.0.0",
"url": "https://your.webhook.url/12345"
},
"trigger_on": "transfers#state-change",
"scope": {
"domain": "application",
"id": "<your client key>"
},
"created_by": {
"type": "application",
"id": "<your client ID>", //clientId and key are not always the same
},
"created_at": "2019-10-10T13:55:57Z",
"request_headers":[
{
"header_name":"Ocp-Apim-Subscription-Key",
"header_value":"34ee49a8-4387-4882-ae80-75bd02a60dfa"
}
]
}
POST https://api.sandbox.transferwise.tech/v3/applications/{clientKey}/subscriptions
{clientKey}
can be received upon obtaining client credentials from our tech support.
All fields listed below not marked Optional are required for creating a webhook subscription.
Field | Description | Format |
---|---|---|
name | A custom name for your webhook to ease with identification | Text |
trigger_on | transfers#state-change | Text |
delivery.version | The event representation semantic version | Text |
delivery.url | The URL where your server will be listening for events. | Text |
request_headers | Optional. The list of HTTP headers you would like to be sent back in the webhook call. See restrictions below. | Array |
request_headers[0].header_name | Optional. The name of the header you would like to be sent back in the webhook call. | Text |
request_headers[0].header_name | Optional. The header value you would like to be sent back in the webhook call. | Text |
Field | Description | Format |
---|---|---|
id | Unique identifier that identifies the subscription | Text |
name | A custom name for your webhook to ease with identification | Text |
trigger_on | transfers#state-change | Text |
delivery.version | The event representation semantic version | Text |
delivery.url | The URL where your server will be listening for events. | Text |
scope.domain | Scope of this subscription, always "application" in this case | Text |
scope.id | Client key used to create this subscription | Text |
created_by.type | Creator type. Always application in this case | Text |
created_by.id | Client ID of the creator. Not always the same as the client key | Text |
created_at | Timestamp of when the subscription was created | Text |
request_headers | The list of HTTP headers you would like to be sent back in the webhook call. | Array |
request_headers[0].header_name | The name of the header you would like to be sent back in the webhook call. | Text |
request_headers[0].header_name | The header value you would like to be sent back in the webhook call. | Text |
The headers we will send are subject to strict validation. The current permitted header names and corresponding value formats are listed below. If you need another name or value to be permitted please contact us.
Header Name | Header Value |
---|---|
"Ocp-Apim-Subscription-Key" |
32 character string passing the regex ^([a-f0-9]){32}$ |
Deletes a subscription by its identifier.
Example Request:
curl -X DELETE "https://api.sandbox.transferwise.tech/v3/applications/{clientKey}/subscriptions/{id}" \
-H "Authorization: Bearer <your client level token>"
Example Response:
DELETE https://api.sandbox.transferwise.tech/v3/applications/{clientKey}/subscriptions/{id}
Retrieves a subscription by its identifier.
Example Request:
curl -X GET "https://api.sandbox.transferwise.tech/v3/applications/{clientKey}/subscriptions/{id}" \
-H "Authorization: Bearer <your client level token>"
Example Response:
{
"id": "f215f353-35fd-405b-b27f-4fd603c905ed",
"name": "Webhook Subscription #1",
"delivery": {
"version": "2.0.0",
"url": "https://your.webhook.url/12345"
},
"trigger_on": "transfers#state-change",
"scope": {
"domain": "application",
"id": "<your client key>"
},
"created_by": {
"type": "application",
"id": "<your client ID>", //clientId and key are not always the same
},
"created_at": "2008-09-15T15:53:00+00:00Z",
}
GET https://api.sandbox.transferwise.tech/v3/applications/{clientKey}/subscriptions/{id}
List all your subscriptions
Example Request:
curl -X GET "https://api.sandbox.transferwise.tech/v3/applications/{clientKey}/subscriptions" \
-H "Authorization: Bearer <your client level token>"
Example Response:
[
{
"id": "e889e085-3677-4110-be26-3e9160ac9f25",
"name": "#1 subscription",
"delivery": {
"version": "2.0.0",
"url": "https://your.webhook.url/12345"
},
"trigger_on": "transfers#state-change",
"created_by": {
"type": "application",
"id": "<your client ID>"
},
"scope": {
"domain": "application",
"id": "<your client key>"
}
},
{
"id": "eabeb3f5-c134-4a1c-99e2-86a1163daf1b",
"name": "#2 subscription",
"delivery": {
"version": "2.0.0",
"url": "https://your.webhook.url/12345"
},
"trigger_on": "transfers#state-change",
"created_by": {
"type": "application",
"id": "<your client ID>"
},
"scope": {
"domain": "application",
"id": "<your client key>"
}
}
]
GET https://api.sandbox.transferwise.tech/v3/applications/{clientKey}/subscriptions
Events are messages that will be sent to your server as HTTP POST
requests once you create a webhook subscription (and a resource you have access to is updated).
They will not contain any personally identifiable information.
To acknowledge that you have successfully processed an event, make sure your server answers with the HTTP status code 200
within 5s. Otherwise, we will consider the delivery attempt as having failed and will later try to resend the message.
We will attempt to redeliver messages at increasing intervals over a two week period. We will try at most 25 times to do this.
Wise public key for sandbox environment:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwpb91cEYuyJNQepZAVfP
ZIlPZfNUefH+n6w9SW3fykqKu938cR7WadQv87oF2VuT+fDt7kqeRziTmPSUhqPU
ys/V2Q1rlfJuXbE+Gga37t7zwd0egQ+KyOEHQOpcTwKmtZ81ieGHynAQzsn1We3j
wt760MsCPJ7GMT141ByQM+yW1Bx+4SG3IGjXWyqOWrcXsxAvIXkpUD/jK/L958Cg
nZEgz0BSEh0QxYLITnW1lLokSx/dTianWPFEhMC9BgijempgNXHNfcVirg1lPSyg
z7KqoKUN0oHqWLr2U1A+7kqrl6O2nx3CKs1bj1hToT1+p4kcMoHXA7kA+VBLUpEs
VwIDAQAB
Wise public key for production environment:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvO8vXV+JksBzZAY6GhSO
XdoTCfhXaaiZ+qAbtaDBiu2AGkGVpmEygFmWP4Li9m5+Ni85BhVvZOodM9epgW3F
bA5Q1SexvAF1PPjX4JpMstak/QhAgl1qMSqEevL8cmUeTgcMuVWCJmlge9h7B1CS
D4rtlimGZozG39rUBDg6Qt2K+P4wBfLblL0k4C4YUdLnpGYEDIth+i8XsRpFlogx
CAFyH9+knYsDbR43UJ9shtc42Ybd40Afihj8KnYKXzchyQ42aC8aZ/h5hyZ28yVy
Oj3Vos0VdBIs/gAyJ/4yyQFCXYte64I7ssrlbGRaco4nKF3HmaNhxwyKyJafz19e
HwIDAQAB
How to verify SHA256 signatures (in Java):
public boolean verifySignature(String encodedPublicKey, String signature, String payload) {
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64.getMimeDecoder().decode(encodedPublicKey));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
Signature sign = Signature.getInstance("SHA256WithRSA");
sign.initVerify(publicKey);
sign.update(payload.getBytes());
byte[] signatureBytes = Base64.getDecoder().decode(signature);
return sign.verify(signatureBytes);
}
Each outgoing webhook request is signed. Whilst event payloads do not contain any sensitive information, you may want to verify if the request is coming from Wise (however this is optional). We advise you not to process any requests where signature appears to be forged.
Each POST
request includes X-Signature
header, which contains a signature.
Basic event payload:
{
"data": {},
"subscription_id": "db463333-3333-3333-3333-185d1ee81585",
"event_type": "...",
"schema_version": "2.0.0",
"sent_at": "2019-10-14T12:43:37Z"
}
All events share same payload structure. Depending on the event_type
the only object that will change is data
.
Field | Description | Format |
---|---|---|
data | Object with information about updated resource | Object |
subscription_id | ID of the subscription that triggered the event | Text |
event_type | Type of event, which defines data object |
Integer |
schema_version | The event representation semantic version | String |
sent_at | Timestamp when update happened | Timestamp |
Example event:
{
"data": {
"resource": {
"id": 99330022,
"profile_id": 11121314,
"account_id": 1514131211,
"type": "transfer"
},
"current_state": "processing",
"previous_state": "incoming_payment_waiting",
"occurred_at": "2019-10-14T12:43:37Z"
},
"subscription_id": "db463333-3333-3333-3333-185d1ee81585",
"event_type": "transfers#state-change",
"schema_version": "2.0.0",
"sent_at": "2019-10-14T12:43:37Z"
}
Events will be triggered every time transfer status is updated.
Keep in mind that we do not guarantee the order of when events are sent, so you may get an older update after the most recent one.
Please use the data.occurred_at
field to reconcile the order.
If you would like to subscribe to transfer state change events, please use transfers#state-change
when creating your subscription.
To see how you can simulate transfer status changes, please see Simulate Transfer Processing.
Field | Description | Format |
---|---|---|
data.resource.id | ID of the transfer that got updated | String |
data.resource.profile_id | ID of the profile that owns the transfer | Integer |
data.resourceI.account_id | ID of transfer's recipient | Integer |
data.resource.type | Type of the resource that was updated | String |
data.current_state | Newly acquired state of the resource, possible values are same as transfer statuses | String |
data.previous_state | Previous state of the resource, possible values are same as transfer statuses | String |
data.occured_at | Timestamp when the update happened | Timestamp |
Example Request:
curl -X GET https://api.transferwise.com/v3/comparisons/?sourceCurrency=GBP&targetCurrency=EUR&sendAmount=10000
Example Response:
{
"sourceCurrency": "GBP",
"targetCurrency": "EUR",
"sourceCountry": null,
"targetCountry": null,
"providerCountry": null,
"providerType": null,
"sendAmount": 10000.0,
"providers": [
{
"id": 39,
"alias": "transferwise",
"name": "Wise",
"logo": "https://dq8dwmysp7hk1.cloudfront.net/logos/transferwise.svg",
"type": "moneyTransferProvider",
"partner": false,
"quotes": [
{
"rate": 1.15989,
"fee": 37.12,
"receivedAmount": 11555.84,
"dateCollected": "2019-10-22T14:36:43Z",
"sourceCountry": null,
"targetCountry": null,
"markup": 0.0,
"deliveryEstimation": {
"duration": {
"min": "PT20H8M16.305111S",
"max": "PT20H8M16.305111S"
},
"providerGivesEstimate": true
}
}
]
},
{
"id": 1,
"alias": "barclays",
"name": "Barclays",
"logo": "https://dq8dwmysp7hk1.cloudfront.net/logos/barclays.svg",
"type": "bank",
"partner": false,
"quotes": [
{
"rate": 1.12792426,
"fee": 0.0,
"receivedAmount": 11279.24,
"dateCollected": "2019-10-22T14:00:04Z",
"sourceCountry": "GB",
"targetCountry": "ES",
"markup": 2.75592858,
"deliveryEstimation": {
"duration": {
"min": "PT24H",
"max": "PT24H"
},
"providerGivesEstimate": true
}
},
...
{
"rate": 1.12792426,
"fee": 0.0,
"receivedAmount": 11279.24,
"dateCollected": "2019-10-22T14:00:04Z",
"sourceCountry": "GB",
"targetCountry": "FI",
"markup": 2.75592858,
"deliveryEstimation": {
"duration": {
"min": "PT24H",
"max": "PT24H"
},
"providerGivesEstimate": true
},
...
}
...
]
}
]
}
The comparison API can be used to request price and speed information about various money transfer providers. This includes not only Wise but other providers in the market.
The quotes (price / speed) provided by this API are based off of real quotes collected from 3rd party websites. We collect both the advertised exchange rate and fee for each provider for various amounts. When a comparison is requested we calculate the markup percentage on the collected exchange rate on the mid-market rate at the time of collection, we then apply this markup % on the current mid-market rate to provide a realistic estimate of what each provider offers. We try to collect data for all providers once per hour to ensure we provide as accurate and up to date information as possible, although our attempts do not always succeed.
Note: Today, we only provide estimations for FX transactions with a Bank Transfer pay-in / pay-out option. This is important to stress as many providers offer significantly different fees / exchange rates when used debit / credit card, cash etc.
For more details on the data collection process please see the following page:
https://wise.com/gb/compare/disclaimer
If you have questions or suspect any data to be inaccurate or incomplete please contact us at:
Similar to price, we collect speed data for most (if not all) providers which we have price information for. Many providers display speed estimates to their customers in a number of different ways.
Some examples:
The below API intends to model these in a consistent format by providing a min / max range for all delivery estimations. An estimate that states "up to X" will have "max" set to a duration but "min" as null, a "from X" will have "min" set to a duration and "max" as null. Finally for those providers who offer a specific, point in time estimation (like Wise), the API will surface a duration where min/max are equal.
In order to provide the most flexible and accurate data for clients, we surface a denormalised list of quotes per provider where each quote represents a unique collection of comparison "dimensions".
A single given provider may expose multiple quotes for the same currency route. The most common example is where a provider offers different pricing for one country vs another country which uses the same currency. e.g:
Provider X:
The same principle applies for speed. I.e a provider may have different speed estimates for different target countries and hence we expose these as discrete quotes - where a quote is a unique combination of route / country / speed / price factors.
A client may choose to reduce this set of quotes down to a single or several quotes in order to display a relevant quote to a given user. An example where we take the cheapest quote for a given currency route (and also surface the target country) can be seen at the below link:
https://wise.com/gb/compare/?sourceCurrency=GBP&targetCurrency=EUR&sendAmount=1000
GET https://api.transferwise.com/v3/comparisons/?sourceCurrency=GBP&targetCurrency=EUR&sendAmount=10000
Field (Query Param) | Description | Format |
---|---|---|
sourceCurrency | ISO 4217 source currency code | Text |
targetCurrency | ISO 4217 target currency code | Text |
sendAmount | Amount in source currency | Decimal |
sourceCountry | (Optional) Filter by source country (ISO 3166-1 Alpha-2 code) | Text |
targetCountry | (Optional) Filter by target country (ISO 3166-1 Alpha-2 code) | Text |
providerType | (Optional) Filter by provider type | One of "bank","moneyTransferProvider" |
Field | Description | Format |
---|---|---|
id | Provider id | Integer |
alias | Provider alias (lowercase slug name) | Text |
name | Provider name (presentational / formal name) | Text |
logo | URL pointing to provider logo (default svg format) | Text |
type | Provider type | Text |
partner | Whether a partner of Wise or not | Boolean |
quotes | An array of estimated quotes per provider | Array |
quotes.rate | The live estimated exchange for the provider for this quote | Timestamp |
quotes.fee | The estimated fee for the provider for this quote | Integer |
quotes.receivedAmount | The total estimated receive amount for the provider for this quote | Integer |
quotes.dateCollected | The date of collection for the original quote | Text |
quotes.sourceCountry | source country (ISO 3166-1 Alpha-2 code) | Timestamp |
quotes.targetCountry | target country (ISO 3166-1 Alpha-2 code) | Decimal |
quotes.deliveryEstimation | Delivery estimation details - i.e a speed estimate | Object |
quotes.deliveryEstimation.duration | Duration range | Object |
quotes.deliveryEstimation.duration.min | Minimum quoted time for transaction delivery | ISO 8601 duration format |
quotes.deliveryEstimation.duration.max | Maximum quoted time for transaction delivery | ISO 8601 duration format |
quotes.deliveryEstimation.providerGivesEstimate | Whether a provider publicly surfaces / exposes a speed estimate | Boolean |
Example Request:
curl -X GET "https://api.sandbox.transferwise.tech/v1/terms/default"
Example Response:
<h2><strong>1. How to read this Agreement</strong></h2>
<p>This Agreement contains 30 sections. You may go directly to any section by selecting the appropriate link provided. The headings are for reference only. Some capitalised terms have specific definitions in section [3]. Underlined words in this Agreement contain hyperlinks to further information.</p>
<h2><strong>2. Why you should read this Agreement</strong></h2>
<p>2.1 What this Agreement cover. These are the term and conditions on which we provide our Services to you.</p>
<p>2.2 Why you should read them. Please read this Agreement carefully before you start to use our Services. This Agreement (always together with the documents referred to in it) tell you who we are, how we will provide the Services to you, how this Agreement may be changed or ended, what to do if there is a problem and other important information. If you think that there is a mistake in this Agreement or require any changes, please <u>contact us</u> to discuss.</p>
<p>2.3 Other additional documents which applies to you. This Agreement refers to the following additional documents, which also apply to your use of our Services:</p>
<ul><li>(a) <u>Our Privacy Policy</u>, which sets out the terms on which we process any personal data we collect about you, or that you provide to us. By using our Services, you consent to such processing and you promise that all data provided by you is accurate.</li>
<li>(b) <u>Our Cookie Policy</u>, which sets out information about the “cookies” on our Website.</li>
...
Get Wise terms and conditions in HTML format to show to your customers who are signing up for their Wise account.
GET https://api.sandbox.transferwise.tech/v1/terms/{clientId}
Use "default" as clientId to fetch Wise general terms and conditions.
No authentication is required for this endpoint.
Terms and conditions in HTML format.
Internationalisation support for translation of strings returned by the API is supported for the following endpoints:
Endpoint |
---|
/v1/quotes |
/v1/quotes/<quoteId/quoteUuid>/account-requirements |
/v1/accounts |
/v1/transfers |
/v1/transfer-requirements |
/v1/profiles/<profileId>/transfers/<transferId>/deposit-details/bank-transfer |
When calling these endpoints if you include an Accept-Language
header with a supported language code as the value then strings will be returned in the requested language. The languages supported by Wise are:
Language | Code |
---|---|
American English | en_US |
Arabic | ar |
British English | en |
Chinese | zh |
Czech | cs |
Dutch | nl |
French | fr |
German | de |
Hebrew | he |
Hungarian | hu |
Italian | it |
Japanese | ja |
Korean | ko |
Polish | pl |
Portuguese | pt |
Romanian | ro |
Russian | ru |
Spanish | es |
Turkish | tr |
Ukrainian | uk |
If you request an unsupported language then British English will be returned by default.
We use common HTTP status codes included in the response header to indicate success or failure.
Code | Description |
---|---|
200 | OK. Successful request. |
201 | OK. Resource created. |
400 | Bad request. Request message data did not pass validation. |
401 | Unauthorised. Not authorised to access requested data. |
403 | Forbidden. Access to requested data is forbidden. |
404 | Not Found. Requested resource does not exist. |
408 | Timeout. Operation timed out. |
422 | Unprocessable entity. Request message data did not pass validation. |
500 | Server error. |
Example Validation Error:
{
"errors": [
{
"code": "error.route.not.supported",
"message": "This route is not supported",
"arguments": [
"CNY-EUR"
]
}
]
}
Data validation or violation of business rules related errors. Response could contain multiple errors.
Example Authentication Error:
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
Security related errors.
Example System Error:
{
"timestamp": "2017-02-02T13:07:39.644+0000",
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.NullPointerException",
"message": "No message available",
"path": "/v1/quotes/0b63b0cb-2041-4bc4-b3fc-1e51a1454a1b/account-requirements"
}
Something went wrong in our side.
Q: | I need to update a redirect URL for my (Sandbox/Live) account. How can I do it? |
A: | Our API tech support can help you with it. You can reach us via email api@wise.com or contact your account manager. |
Q: | Why is it a good idea to show the Anonymous quote to the user before using the service or creating a new one? |
A: | It is good for conversion, as it allows your users to check the cost and delivery estimate for each specific currency route prior to using the service. |
Q: | Will our users receive any emails from Wise? Including transfer status updates, marketing emails and regular updates? |
A: | All communications with the end-users will happen from your side. Though users, that already have a preexisting Wise account at the moment of linking will continue receiving email updates. |
Q: | Is it required to Open/Close update window when creating a profile Address or is it only for updating address details? |
A: | For creating an address it is not required. We generally recommend that you can create a single process (open window, send data, close window) in order to simplify things, but this is not necessary. For new profiles you can just call POST /v1/addresses without calling the open/close window APIs. For existing profiles, either created via API by your integration or directly with Wise, you need to call the open and close window endpoints. |
Q: | What is the purpose of POST /v1/transfer-requirements call? I have successfully created a transfer with no errors without using it. |
A: | These requirements are needed for the recipient bank to successfully process your transfer according to the compliance requirements in the recipients country. Sometimes it is needed to specify transfer purpose or source of funds so you have to use transfer-requirements to check whether you need to specify these details. Without providing this info when it’s needed the transfer will be blocked from further processing. |
Q: | The quote is valid only for 30 mins. Is it possible that the exchange rate and fee change after this time? |
A: | The exchange rate will stay the same within guaranteed rate period of 2-72 hours. The price and delivery estimate can change depending on the recipient type the user chooses. Please see more information about this here |
Q: | As we are moving forward with the development, we need to test some specific scenarios, notedly the warning messages returned by Quote API. We have three types of notice messages: WARNING, INFO and BLOCKED. Please, could you tell us which condition must be used to have any of them returned? |
A: | For less popular currency routes we've set the notices for testing purposes. These are: to CRC quote will come with WARNING notice, to BDT quote will come with INFO notice and to BWP quote will come with BLOCKED notice. |
Q: | Do I have to provide a profileId when creating a recipient or quote? I have noticed that it works even without profileId. |
A: | Yes, you have to explicitly specify a profileId to avoid any confusions on the client side. Without a specified profileId the personal profile will be assigned by default. |
Q: | When creating KRW recipients I get an error about a missing email, but email is not returned as required with account-requirements. |
A: | That is correct, you need to provide an email when creating recipients for some currencies. Set the request header Accept-Minor-Version to 1 to use this version to your account-requirements call and you will get the correct response. More info here |
Q: | Do you have any test/example data that we can use for recipient creation? IBANS, SWIFTS, Account numbers etc for the currencies we will use for the launch? |
A: | Currently we don’t have it in one list, but we return samples with account-requirements response. It is stored in an “example” field and you can also find a regex used for validation. Please contact our API Support api@wise.com in case if you have any questions. |
Q: | Is it possible to edit a saved recipient's details over the API? |
A: | It is not possible to change the details of the existing recipients, you would need to create a new one and delete the existing. |
Q: | In order to get account-requirements I have to create a quote first and then based on this I can get account-requirements for this quote. Our flow starts with recipient creation. Do I still have to create a “placeholder” quote in order to get account-requirements? |
A: | You can pass source, target currencies and amount as url parameters in the following format: |
GET https://api.sandbox.transferwise.tech/v1/account-requirements?source=EUR&target=USD&sourceAmount=1000
This call doesn’t require an authentication header. Please note, this is not a recommended flow, as it doesn't take to account the amount and data requirements may be different when a customer actually comes to send a high amount transfer.
Q: | Do we have to provide an address when creating a recipient? How do I know whether or not the address is required? |
A: | You have to use the account-requirements endpoint which returns the list of fields that are required to create a recipient. It also returns address requirements. A detailed guide on how to use account-requirements you can find here |
Q: | When I try to create a SWIFT recipient I get an “unsupported country” error. |
A: | Check the lists of countries where we can send USD, EUR, GBP via SWIFT network.Please note, you cannot send a local transfer via SWIFT (for example sending USD to US SWIFT code is not permitted). |
Q: | If you need to add more than one account for the same recipient (ex: Like one Mexican account and one Brazilian account), is it correct to set these up as separate recipients? |
A: | Yes, those will be separate recipients. They will share the same name but banking details and recipient IDs will be different. |
Q: | Will we get a webhook notification on each transfer status change event? |
A: | Yes, you will get a webhook for each transfer status update. All the transfer statuses are documented here. |
Q: | I've created a new application webhook subscription via API but it is not listed on the website UI on the Settings page. |
A: | We list only profile webhooks on the Settings page. You can check your application webhooks with the endpoint documented here |
Q: | We will have 1000+ customers, do l have to create 1000 endpoints to track transfer statuses? Do you have limitations for a number of webhook endpoints? |
A: | You don’t have to create 1000 endpoints. You can use only one webhook callback url that will receive notifications regarding all of your managed accounts. The notification contains profileId so you will know which user owns this notification. |
Q: | In the webhook notification I have seen 2 headers: “x-signature” and “x-signature-sha256”. Which one do we have to use? |
A: | X-Signature-SHA256 header is recommended to use. It is based on SHA256WithRSA algo. The sha1-based header will remain; it is not technically deprecated, so won’t be removed for now. |
Q: | When creating an Application webhook subscription, why do I get a 401 error response? I have obtained a client token but the subscription creation call fails. |
A: | We have to add your clientId into the configuration file first. It is a quick process and after that you will be able to use Application webhooks. |
Q: | When I’m calling a Simulation endpoint it returns an error. |
A: | After funding the transfer, you still have to manually simulate to “processing” status even though the transfer status returned with GET /v1/transfers/{{ID}} is “processing” already. Also it is not possible to simulate transfers to email recipients, as there is a special transfer status involved, which expects the recipient to provide its banking details. |
Q: | I forgot my Sandbox password. How do I reset it? |
A: | It is not possible to change the Sandbox password from the client side. Our API tech support can help you. Send and email to api@wise.com or contact your Account Manager. For reference, we don’t send any emails from Sandbox. |
Q: | Do you have any certifications for data storage and management? |
A: | Wise has ISO27001 certification. Wise products run on AWS cloud with their sites located in Germany and Ireland. Amazon holds their own ISO certification and offers the highest standards in information security. Wise may use outsourced customer support agents who would have access to some parts of the payment information. This arrangement is done in compliance with our data protection, information security and vendor management policies which are under GDPR regulations. |
Q: | Are we licensed or regulated? |
A: | Yes, we are licensed and/or regulated in many countries. A full list of our licensing status is available here. |
Q: | Please confirm if an approved formal Data Protection Policy is in place and the role of DPO is properly assigned. |
A: | Yes, we have a formal Data Protection Policy and DPO assigned in line with General Data Protection Regulation (GDPR). Global DPO can be reached out at privacy@wise.com. The latest version of our privacy policy can be found here. |
Q: | Please define the monthly uptime percentage of the Wise examined service and how this is achieved. How does Wise resolve service downtime that may affect transfers? |
A: | Wise endeavour to ensure we have a highly available platform that both our direct customers and partners can rely on. This is achieved by a team of hundreds of engineers who work to ensure the reliability of the service offered. Unfortunately, incidents can still happen and any disruptions will be posted on our status page and you can also sign up for incident and upcoming maintenance notifications by contacting us. |
Q: | What is Wise expecting as an API Token mechanism from the calling client? Please provide details on how to generate an API token with Wise. |
A: | Please find below the links with detail for the token generation in each different scenario. User authorization for existing accounts Sign up new users via API (available for bank integrations only) User tokens Refresh user access token |