Card Issuance Integration Guide

Card issuance is a Wise product that allows issuing and managing Wise cards via API.

For more details and working examples of how to use our API - including how to link users to your integration, create cards and more - you can request our Postman API collection from your account or implementation manager.

There are several "one-off" steps that need to be done in order to get the Authorisation token to be used in your backend calls to Wise. In order to get the token please:

  1. Obtain the client_id for your app from Wise (needs to be created manually by Wise onboarding team)
  2. Connect the client_id to the profile which will be making the actual calls to Wise. In order to do it login to Wise account using any web browser, then visit the following link: https://sandbox.transferwise.tech/oauth/authorize/?client_id=<your_client_id>&redirect_uri=http://localhost .
  3. Save the code and profileId from the localhost URL. It’s expected to get an ERR_CONNECTION_REFUSED since localhost doesn’t host anything. This flow is designed to redirect existing Wise users to the partners website, which we won’t need to do in this case.
  4. Retrieve and store the refresh token: curl -u '<your client id>:<your client secret>' -d 'grant_type=authorization_code' -d 'client_id=<your client id>' -d 'code=<your code>' -d 'redirect_uri=http://localhost' 'https://api.sandbox.transferwise.tech/oauth/token'
  5. The Authorisation token will be used in all the subsequent card API calls
  6. In order to refresh the token the following API can be used: curl 'https://api.sandbox.transferwise.tech/oauth/token' -u '<your client id>:<your client secret>' -d 'grant_type=refresh_token' -d 'refresh_token=<your refresh token>'

Access tokens are valid for 12 hours, so upon expiry you need to use the refresh token to generate a new access token.

In order to maintain an uninterrupted connection, you can request a new access token whenever it’s close to expiring. There is no need to wait for the actual expiration to happen first.

Depending on how your application uses the Wise Platform API, you may find that requesting a new access token before attempting a series of API calls on behalf of an individual user will avoid issues with expired access tokens.

Card issuance

View card programs availability

Retrieves the list of available card programs and their details.

Card program: a Card Program is what Wise calls all of the cards you will be issuing with us, grouped by product type and by issuing country. Issuing country: the country where your card is created. Product type: the type of card we are issuing on your behalf (consumer, corporate, digital, physical, etc.).

Example request:

curl --location --request GET https://api.sandbox.transferwise.tech/v3/spend/profiles/<your profile id>/card-orders/availability \
--header 'Authorization: Bearer <your api token>' \
--header 'Content-Type: application/json' \
--header 'Accept-Language: en-GB'

Example response:

{
  "cardPrograms": [
    {
      "name": "VISA_DEBIT_BUSINESS_UK_1",
      "scheme": "VISA",
      "defaultCurrency": "GBP"
    }
  ]
}

Create a card order

Orders a new card for a given profile. The card program should come from the list of available card programs.

The lifetimeLimit parameter is the maximum amount that can be spent with the card for the entire lifetime of the card. The lifetime limit currency is the card's default currency defined in the card program. The default lifetime limit value is 0, which means the card cannot be used until the lifetime limit is updated.

Example request:

curl --location --request POST https://api.sandbox.transferwise.tech/v3/spend/profiles/<your profile id>/card-orders \
     --header 'Authorization: Bearer <your access token>' \
     --header 'Content-Type: application/json' \
     --data '{
        "program": "<card program name>",
        "cardHolderName": "<card holder name>",
        "phoneNumber": "<phone number>",
        "billingAddress": {
          "firstLine": "<first line of the address>",
          "secondLine": "<optional second line>",
          "thirdLine": "<optional third line>",
          "city": "<city>",
          "state": "<optional state>",
          "postCode": "<optional postal code>",
          "country": "<2 letter country code using ISO 3166-1 alpha-2>",
          "phoneNumber": "<billing address phone number>"
        },
         "lifetimeLimit": 100,
     }'

Example response:

{
  "id": 142,
  "profileId": 123456,
  "clientId": "<your client id>",
  "cardProgram": {
    "name": "VISA_DEBIT_BUSINESS_UK_1",
    "scheme": "VISA",
    "defaultCurrency": "GBP"
  },
  "billingAddress": {
    "country": "GB",
    "city": "London",
    "postCode": "E1 6JJ",
    "firstLine": "The Tea Building",
    "secondLine": "56 Shoreditch High St",
    "thirdLine": null,
    "state": null,
    "phoneNumber": "+442071111111"
  },
  "cardType": "VIRTUAL_NON_UPGRADEABLE",
  "cardToken": null,
  "replacesCard": null,
  "creationTime": "2022-05-31T01:43:24.596321434Z",
  "modificationTime": "2022-05-31T01:43:24.596321825Z",
  "status": "REQUIREMENTS_FULFILLED",
  "cardHolderName": "Valentin K",
  "phoneNumber": "+442079640500",
  "lifetimeLimit": 100
}

Retrieve all cards orders by profile id

Example request

curl --location --request GET https://api.sandbox.transferwise.tech/v3/spend/profiles/<your profile id>/card-orders?pageNumber=1&pageSize=10 \
--header 'Authorization: Bearer <your access token>'

The following parameters are optional:

  • pageSize - the maximal number of requested card orders (used for pagination). This parameter has to be between 10 and 100 inclusive. If ommitted the default value 10 is used.
  • pageNumber - the requested page number starting from 1 (used for pagination). This parameter has to be equal or greater than 1. If ommitted the default value of 1 is used.

Both parameters are optional and if not provided their default values will be used.

The returned response contains 2 elements:

  • totalCount - the total number of card orders, this number is never affected by the given pageSize or pageNumber parameters
  • cardOrders - the list of card orders starting from the given pageNumber (please keep in mind that the size of this list is limited by the given pageSize parameter with a default value of 10)

Example response

{
  "totalCount": 1,
  "cardOrders": [
    {
      "id": 143,
      "profileId": 123456,
      "clientId": "<client id>",
      "cardProgram": {
        "name": "VISA_DEBIT_BUSINESS_UK_1",
        "scheme": "VISA",
        "defaultCurrency": "GBP"
      },
      "billingAddress": {
        "country": "GB",
        "city": "London",
        "postCode": "E1 6JJ",
        "firstLine": "The Tea Building",
        "secondLine": "56 Shoreditch High St",
        "thirdLine": null,
        "state": null,
        "phoneNumber": "+442071111111"
      },
      "cardType": "VIRTUAL_NON_UPGRADEABLE",
      "cardToken": "58f61c06-1431-4769-a445-5d0c0c396de4",
      "replacesCard": null,
      "creationTime": "2022-05-31T01:47:27.882234Z",
      "modificationTime": "2022-05-31T01:47:28.509848Z",
      "status": "COMPLETED",
      "cardHolderName": "Valentin K",
      "phoneNumber": "+442079640500",
      "lifetimeLimit": 100
    }
  ]
}

Retrieve details of a particular card order

Retrieves the details of a particuular card order given its id.

Example request

curl --location --request GET https://api.sandbox.transferwise.tech/v3/spend/profiles/<your profile id>/card-orders/<card order id>
--header 'Authorization: Bearer <your access token>'

Example response

{
  "id": 143,
  "profileId": 123456,
  "clientId": "<your client id>",
  "cardProgram": {
    "name": "VISA_DEBIT_BUSINESS_UK_1",
    "scheme": "VISA",
    "defaultCurrency": "GBP"
  },
  "billingAddress": {
    "country": "GB",
    "city": "London",
    "postCode": "E1 6JJ",
    "firstLine": "The Tea Building",
    "secondLine": "56 Shoreditch High St",
    "thirdLine": null,
    "state": null,
    "phoneNumber": "+442071111111"
  },
  "cardType": "VIRTUAL_NON_UPGRADEABLE",
  "cardToken": "58f61c06-1431-4769-a445-5d0c0c396de4",
  "replacesCard": null,
  "creationTime": "2022-05-31T01:47:27.882234Z",
  "modificationTime": "2022-05-31T01:47:28.509848Z",
  "status": "COMPLETED",
  "cardHolderName": "Valentin K",
  "phoneNumber": "+442079640500",
  "lifetimeLimit": 100
}

Retrieve all cards by profile id

Retrieves the details of all cards that belong to a given profile.

The following parameters are optional:

  • pageSize - the maximal number of requested cards (used for pagination). This parameter has to be between 10 and 100 inclusive. If ommitted the default value 10 is used.
  • pageNumber - the requested page number starting from 1 (used for pagination). This parameter has to be equal or greater than 1. If ommitted the default value of 1 is used.

Both parameters are optional and if not provided their default values will be used.

The returned response contains 2 elements:

  • totalCount - the total number of cards, this number is never affected by the given pageSize or pageNumber parameters
  • cards - the list of cards starting from the given pageNumber (please keep in mind that the size of this list is limited by the given pageSize parameter with a default value of 10)

Example request

curl --location --request GET https://api.sandbox.transferwise.tech/v3/spend/profiles/<your profile id>/cards?pageNumber=1&pageSize=10 \
-header 'Authorization: Bearer <your access token>'

Example response

{
  "totalCount": 1,
  "cards": [
    {
      "token": "2c035466-ed3e-4fd7-9f1c-80ca0952f233",
      "profileId": 16474483,
      "clientId": "<your client id>",
      "status": {
        "value": "ACTIVE",
        "allowedTransitions": [
          "FROZEN",
          "BLOCKED"
        ]
      },
      "cardHolderName": "VALENTIN K",
      "expiryDate": "2027-06-30T00:00:00Z",
      "lastFourDigits": "5517",
      "cardProgram": {
        "name": "VISA_DEBIT_BUSINESS_UK_1",
        "scheme": "VISA",
        "defaultCurrency": "GBP"
      },
      "creationTime": "2022-06-01T01:58:40.686874Z",
      "modificationTime": "2022-06-01T01:58:41.932492Z"
    }
  ]
}

Retrieve details of one card by card token

Retrieves the details of one card given its id (also referred as a card token).

Example request

curl --location --request GET https://api.sandbox.transferwise.tech/v3/spend/profiles/<your profile id>/cards/<card token> \
-header 'Authorization: Bearer <your api token>'

Example response

{
  "token": "2c035466-ed3e-4fd7-9f1c-80ca0952f233",
  "profileId": 16474483,
  "clientId": "<your client id>",
  "status": {
    "value": "ACTIVE",
    "allowedTransitions": [
      "FROZEN",
      "BLOCKED"
    ]
  },
  "cardHolderName": "VALENTIN K",
  "expiryDate": "2027-06-30T00:00:00Z",
  "lastFourDigits": "5517",
  "cardProgram": {
    "name": "VISA_DEBIT_BUSINESS_UK_1",
    "scheme": "VISA",
    "defaultCurrency": "GBP"
  },
  "creationTime": "2022-06-01T01:58:40.686874Z",
  "modificationTime": "2022-06-01T01:58:41.932492Z"
}

Set card status by card token

Modifies the card status, where the possible new status values are:

  • ACTIVE - the card is active and usable
  • FROZEN - the card is temporarily frozen resulting in all authorisation requests to be declined
  • BLOCKED - the card is irreversibly blocked and is no longer usable

Example request

curl --request PUT https://api.sandbox.transferwise.tech/v3/spend/profiles/<your profile id>/cards/<your card token>/status \
     --header "Authorization: Bearer <your access token>" \
     --header "Content-Type: application/json" \
     -d '{
        "status": "<new card status>"
     }'

Example response

{
  "token": "2c035466-ed3e-4fd7-9f1c-80ca0952f233",
  "profileId": 16474483,
  "clientId": "<your client id>",
  "status": {
    "value": "FROZEN",
    "allowedTransitions": [
      "ACTIVE",
      "BLOCKED"
    ]
  },
  "cardHolderName": "VALENTIN K",
  "expiryDate": "2027-06-30T00:00:00Z",
  "lastFourDigits": "5517",
  "cardProgram": {
    "name": "VISA_DEBIT_BUSINESS_UK_1",
    "scheme": "VISA",
    "defaultCurrency": "GBP"
  },
  "creationTime": "2022-06-01T01:58:40.686874Z",
  "modificationTime": "2022-06-01T01:58:41.932492Z"
}

Get card limits

Retrieves the card limits

Example request

curl --location --request GET https://api.sandbox.transferwise.tech/v3/spend/profiles/<your profile id>/cards/<your card token>/spending-limits \
     -- header 'Authorization: Bearer <your access token>'

Example response

{
    "spendings": [
        {
            "type": "ATM_WITHDRAWAL",
            "permissions": [
                {
                    "type": "ATM_WITHDRAWAL",
                    "isEnabled": true
                }
            ],
            "isDefault": false,
            "limits": [],
            "fees": []
        },
        {
            "type": "ECOM_PURCHASE",
            "permissions": [
                {
                    "type": "ECOM",
                    "isEnabled": true
                }
            ],
            "isDefault": false,
            "limits": [],
            "fees": []
        },
        {
            "type": "CHIP_WALLET_PURCHASE",
            "permissions": [
                {
                    "type": "POS_CHIP",
                    "isEnabled": true
                },
                {
                    "type": "MOBILE_WALLETS",
                    "isEnabled": true
                }
            ],
            "isDefault": false,
            "limits": [],
            "fees": []
        },
        {
            "type": "GENERAL",
            "permissions": [],
            "isDefault": false,
            "limits": [],
            "fees": []
        }
    ]
}

Set card limits

Set or change card spending limits. By default, a new card will have a lifetime limit set to 0.

Request

PATCH https://api.sandbox.transferwise.tech/v3/spend/profiles/{profileId}/cards/{cardToken}/spending-limits

Field Description Format
cardLimitType Card limit type. Allowed Values:
  • TRANSACTION
  • DAILY
  • MONTHLY
  • LIFETIME
Text
maxLimitAmount Limit amount on the card for the specified limit type. Decimal

Example request

curl --location --request PATCH https://api.sandbox.transferwise.tech/v3/spend/profiles/<your profile id>/cards/<your card token>/spending-limits \
     --header 'Authorization: Bearer <your access token>' \
     --header 'Content-Type: application/json' \
     --data '{
        "cardLimitType": "LIFETIME",
        "maxLimitAmount": <your spending limit value>
     }'

Response

No response content is expected, only response code should be used.

Example request

No response content is expected, only response code should be used.

Get card transactions

Retrieves the card spending limits.

Example request

curl --location --request GET https://api.sandbox.transferwise.tech/v3/spend/profiles/<your profile id>/card-transactions?cardToken=<your card token>&pageSize=10&pageNumber=1 \
--header 'Authorization: Bearer <your access token>'

Example response

{
    "totalCount": 1,
    "transactions": [
        {
            "id": "294294296",
            "cardToken": "<card token>",
            "type": "ECOM_PURCHASE",
            "declineReason": "CARD_FROZEN",
            "createdDate": "2022-04-22T10:21:09.491079Z",
            "state": "DECLINED",
            "cardLastDigits": "1908",
            "transactionAmount": {
                "amount": 5.70,
                "currency": "SGD"
            },
            "fees": [],
            "transactionAmountWithFees": {
                "amount": 5.70,
                "currency": "SGD"
            },
            "merchant": {
                "id": "405781676",
                "name": "Deliveroo",
                "location": {
                    "country": "Singapore",
                    "city": "+6531637074",
                    "zipCode": "068804    ",
                    "region": null,
                    "state": null
                },
                "category": {
                    "name": "EatingPlacesRestaurants",
                    "code": "5812",
                    "description": "Eating Places, Restaurants"
                }
            },
            "authorisationMethod": "MANUAL_ENTRY"
        }
    ]
}

Sensitive card details endpoint

The sensitive card details endpoint allows you to retrieve card data such as Primary Account Number, expiry date, CVV and PIN.

Wise is a PCI DSS compliant provider, and stores all of your Cards API data securely. The scope for PCI compliance depends on your use case and will impact how you integrate with Cards API. To know more, reach out to your implementation manager.

Errors

HTTP Status Codes

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.

Validation Errors

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.

Authentication Errors

Example Authentication Error:

{
    "error": "unauthorized",
    "error_description": "Full authentication is required to access this resource"
}

Security related errors.

System 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.

FAQ

User Onboarding

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.

Transfer Flow

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.

Recipient

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.

Webhooks

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.

Sandbox

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.

Business

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