Internal API description of Revolut personal. Mostly just for card management.

g 37e81f59b0 Add card creation 3 months ago
README.md 37e81f59b0 Add card creation 3 months ago

README.md

Revolut Card API

Base address

https://app.revolut.com/api/revolut-secure/retail

Headers

{
    "authority": "app.revolut.com",
    "accept": "application/json",
    "content-type": "application/jso",
    "origin": "chrome-extension://hdlehfdjcalidklijenibmpcdgjfmafn",
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "cross-site",
    "user-agent": "<chrome user agent>",
    "x-browser-application": "BROWSER_EXTENSION",
    "x-client-version": "100.0",
    "x-device-id": "<random uid>",
    "x-device-model": "<same as user agent>"
}

Authentication

POST /signin

Description

Login with phone number and passcode and get a token.

Request

{
    "phone":"<+XX phone>",
    "password":"<passcode>",
    "channel":"APP"
}

Response

{
    "tokenId":"<token uid>"
}

POST /token (issue)

Description

Once a token is obtained through the login request, the signin request has to be confirmed on the app. This request is used for polling for that authorization and then getting actual credentials.

Request

{
    "phone":"<+XX phone>",
    "password":"<passcode>",
    "tokenId":"<token uid>"
}

Response

Case 1 - Authorization Pending
{
    "message": "One should obtain consent from the user before continuing",
    "code": 9035
}
Case 2 - Authorization Granted
{
    "tokenExpiryDate": <expiry timestamp>,
    "refreshCode": "<refresh code>",
    "ownerId": "<owner id>,
    "accessToken": "<access token>",
    "user": {
        "id": "<user id> (should be same as <owner id>)",
        "state": "ACTIVE"
    }
}

POST /token (refresh)

Description

The token has to be periodically refreshed.

Request

{
    "userId":"<user id>"
    "refreshCode":"<refresh code>"
}

Response

{
    "tokenExpiryDate": <expiry date>,
    "refreshCode": "<refresh code>",
    "ownerId": "<owner id>",
    "accessToken": "<access token>"
}

GET /user/current/picture

Description

Get user profile picture.

Headers

{
    "Authorization": <owner id>:<access token>
}

Response

Profile picture raw bytes.

GET /user/current

Description

Get user details, including email, phone, full address, username, id, code.

Headers

{
    "Authorization": <owner id>:<access token>
}

Response

{
    "user": {
        "id": "<user id>",
        "individualId": "<user id>",
        "createdDate": <creation date>,
        "address": {
            "city": "<city>",
            "country": "<country code>",
            "postcode": "<post code>",
            "region": "<region>",
            "streetLine1": "<address line 1>",
            "streetLine2": "<address line 2>"
        },
        "birthDate": [
            <year>,
            <month>,
            <day>
        ],
        "firstName": "<first name>",
        "lastName": "<last name>",
        "phone": "<+XX phone>",
        "email": "<email>",
        "emailVerified": true,
        "state": "ACTIVE",
        "referralCode": "<code>",
        "code": "<code>",
        "kyc": "PASSED",
        "underReview": false,
        "locale": "en-GB",
        "riskAssessed": false,
        "username": "<username>",
        "identityDetails": {
            "accountPurpose": "DAILY_SPENDING",
            "taxResidencies": [],
            "identificationNumbers": [
                {
                    "country": "<country code>",
                    "name": "genericTin",
                    "value": "<?>"
                }
            ]
        },
        "hasProfilePicture": true,
        "appMode": "FULL"
    },
    "wallet": {
        "baseCurrency": "EUR"
    }
}

GET /my-card/all

Description

Get an array of all the available cards in the account, without secret details. In a personal account, cards can be either virtual or physical. Virtual cards can also be tagged as single use (disposable). It is also known whether a card is for professional use (PRO) or for personal use (RETAIL).

Headers

{
    "Authorization": <owner id>:<access token>
}

Response

[
    {
        "id": "<card id>",
        "walletId": "<wallet id>",
        "label": "<label>",
        "state": "ACTIVE",
        "virtual": true,
        "disposable": false,
        "credit": false,
        "instalment": false,
        "customised": false,
        "brand": "MASTERCARD",
        "design": "<design enum>",
        "designGroup": "VIRTUAL",
        "colour": "#<hex color>",
        "lastUsedDate": <last used>,
        "lastFour": "<last four digits>",
        "expiryDate": <expiry date>,
        "replaced": false,
        "createdDate": <created date>,
        "productType": "PRO",
        "settings": {
            "locationSecurityEnabled": false,
            "magstripeDisabled": true,
            "atmWithdrawalsDisabled": true,
            "ecommerceDisabled": false,
            "contactlessDisabled": false,
            "initial": false,
            "pockets": [
                {
                    "id": "<pocket id>",
                    "pocketType": "MERCHANT"
                },
                {
                    "id": "<pocket id>",
                    "pocketType": "MERCHANT"
                }
            ]
        },
        "delivery": {
            "status": "NONE"
        },
        "usage": <?>,
        "preexpired": false,
        "applePayEligible": true,
        "googlePayEligible": true,
        "cardClickActivationEligible": false,
        "panActivationEligible": false
    },
    {
        "id": "<card id>",
        "walletId": "<wallet id>",
        "label": "<label>",
        "state": "ACTIVE",
        "virtual": true,
        "disposable": false,
        "credit": false,
        "instalment": false,
        "customised": false,
        "brand": "VISA",
        "design": "<design enum>",
        "designGroup": "VIRTUAL",
        "colour": "#<hex color>",
        "lastUsedDate": <last used>,
        "lastFour": <last four digits>",
        "expiryDate": "<expiry date>",
        "replaced": false,
        "createdDate": <created date>,
        "productType": "RETAIL",
        "settings": {
            "locationSecurityEnabled": false,
            "magstripeDisabled": true,
            "atmWithdrawalsDisabled": true,
            "ecommerceDisabled": false,
            "contactlessDisabled": false,
            "initial": false,
            "pockets": []
        },
        "delivery": {
            "status": "NONE"
        },
        "usage": <?>,
        "preexpired": false,
        "applePayEligible": true,
        "googlePayEligible": true,
        "cardClickActivationEligible": false,
        "panActivationEligible": false
    }
]

GET /my-card//details

Description

Get card secret details: pan, cvv, expiration date. The pan and the cvv are encrypted using AES ECB with the first 32 ascii chars of the token as key.

Headers

{
    "Authorization": <owner id>:<access token>
}

Response

{
    "pan":"<base64 of AES encrypted pan>",
    "cvv":"<base64 of AES encrypted cvv>",
    "expiry":{"month":<month>,"year":<year>}
}

POST /my-card/issue/virtual

Description

Create a new virtual card. Design is an enum and must match the card type (there are virtual designs and disposable designs).

Headers

{
    "Authorization": <owner id>:<access token>
}

Request

{
    design: "<color>_VIRTUAL",
    label: "<label>",
    disposable: false
}

Response

Card object, same as /my-card/all.

TODO

Support for card termination, issue, freeze, unfreeze, labeling, and creation is quite simple too.