{
  "id": "revolut",
  "name": "Revolut",
  "service": "revolut",
  "trust": "community",
  "domain": "revolut.com",
  "country": "global",
  "categories": [
    "banking"
  ],
  "version": "2026-08-12",
  "minVersion": "0.9.3",
  "minorUnits": true,
  "keepRaw": true,
  "throttle": {
    "minMs": 300,
    "jitterMs": 400
  },
  "match": [
    "https://app.revolut.com/*"
  ],
  "openUrl": "https://app.revolut.com/home",
  "crossDomainHosts": [
    "storage.googleapis.com"
  ],
  "auth": {
    "mode": "cookie",
    "replayHeaders": [
      "x-device-id"
    ],
    "readyUrl": "https://app.revolut.com/home"
  },
  "api": {
    "host": "https://app.revolut.com",
    "groups": {
      "path": "/api/retail/user/current/wallet",
      "itemsPath": "pockets",
      "fields": {
        "id": "id",
        "name": "currency",
        "currency": "currency",
        "type": "type"
      }
    }
  },
  "streams": [
    {
      "id": "transactions",
      "name": "Transacciones",
      "schema": "transaction@1",
      "normalize": {
        "counterparty": {
          "from": "description",
          "re": [
            "^(?:To|From|Payment to|Payment from)\\s+(.+)$"
          ]
        }
      },
      "api": {
        "list": {
          "path": "/api/retail/user/current/transactions/last",
          "itemsPath": "$",
          "paging": "cursor",
          "cursorParam": "to",
          "cursorFromItem": "startedDate",
          "params": {
            "count": "500",
            "internalPocketId": "{group.id}"
          },
          "headers": {
            "x-browser-application": "WEB_CLIENT",
            "x-client-version": "100.0"
          },
          "referer": "https://app.revolut.com/home"
        }
      },
      "fields": {
        "internalId": "id",
        "date": "startedDate",
        "amount": "amount",
        "currency": "currency",
        "description": "description",
        "type": "type",
        "counterparty": "merchant.name",
        "balanceAfter": "balance",
        "valueDate": "completedDate"
      }
    },
    {
      "id": "extracto",
      "name": "Extractos de cuenta",
      "schema": "invoice@1",
      "api": {
        "list": {
          "paging": "synthetic",
          "synthetic": {
            "each": "group-months"
          },
          "maxAgeDays": 366
        }
      },
      "fields": {
        "internalId": "{group.currency}-{period}",
        "date": "{date}",
        "number": "{period}",
        "account": "{group.currency}",
        "description": "Extracto {group.currency} {period}"
      },
      "formats": [
        {
          "id": "pdf",
          "name": "PDF",
          "api": {
            "pdf": {
              "poll": {
                "path": "/api/retail/user/current/statements/account-statements?from={fromDate}&to={date}&ccy={group.currency}&format=PDF&type=OFFICIAL",
                "statePath": "state",
                "readyValue": "READY",
                "urlField": "url",
                "tries": 10,
                "delayMs": 1500
              },
              "ext": "pdf",
              "mime": "application/pdf"
            }
          }
        },
        {
          "id": "csv",
          "name": "CSV",
          "api": {
            "pdf": {
              "poll": {
                "path": "/api/retail/user/current/statements/account-statements?from={fromDate}&to={date}&ccy={group.currency}&format=CSV&type=OFFICIAL",
                "statePath": "state",
                "readyValue": "READY",
                "urlField": "url",
                "tries": 10,
                "delayMs": 1500
              },
              "ext": "csv",
              "mime": "text/csv"
            }
          }
        }
      ]
    }
  ],
  "changelog": [
    {
      "version": "2026-08-12",
      "changes": "La sincronización automática ya no arranca hasta que app.revolut.com/home ha terminado de cargar. La app dispara peticiones autenticadas en pantallas anteriores, así que una sincronización podía empezar antes de que existiera la vista con los datos."
    },
    {
      "version": "2026-08-08",
      "changes": "Adds `content` (bilingual copy for the guide page on habeas.dev) and `brand`."
    },
    {
      "version": "2026-07-16",
      "changes": "Map the running balance (minor-unit scaled) to record.balanceAfter and the completed date to record.valueDate — promoted to canonical fields for the Cuéntamo contract. Requires extension >= 0.3.0.24."
    },
    {
      "version": "2026-07-15.2",
      "changes": "Add account statements (PDF/CSV) per account × month: async-generated (poll account-statements until READY) then download the signed Google Storage URL (crossDomainHosts + consent). Statements bounded to the last ~12 months."
    },
    {
      "version": "2026-07-15.1",
      "changes": "Add per-account (pocket) filter with a currency-labelled Accounts picker."
    },
    {
      "version": "2026-07-15",
      "changes": "Initial: personal transactions across all accounts, paged by the `to` timestamp cursor; cookie + captured x-device-id; minor-unit amounts scaled per ISO 4217."
    }
  ],
  "content": {
    "en": {
      "slug": "revolut-statements",
      "docs": "transactions and statements",
      "h1": "How to download your Revolut transactions and statements",
      "intro": "Revolut offers statements as PDF or CSV, one period at a time. Building a continuous history, or keeping it alongside your other accounts, still means going back every month and downloading by hand."
    },
    "es": {
      "slug": "extractos-revolut",
      "docs": "movimientos y extractos",
      "h1": "Cómo descargar tus movimientos y extractos de Revolut",
      "intro": "Revolut ofrece extractos en PDF o CSV, un periodo cada vez. Construir un histórico continuo, o mantenerlo junto al de tus otras cuentas, sigue exigiendo volver cada mes y descargar a mano."
    }
  },
  "brand": "Revolut"
}
