Skip to content

How to implement ISA transfers

Prerequisites

  • Authentication scopes — isa_transfers:admin

    For more information, refer to Authentication scopes.

  • An active ISA tax wrapper — The receiving account group must have a Stocks and Shares ISA tax wrapper in status ACTIVE.

    For more information, refer to How to integrate ISA tax wrappers.

  • Webhook handler ready — Set up a webhook endpoint and subscribe to ISA_TRANSFER.* events. Creating a transfer is asynchronous and there is no endpoint to read a transfer back, so webhooks are the only way to observe its progress.

    For more information, refer to Implementing webhooks.

Overview

A transfer within the same ISA manager moves cash from an end user's existing ISA into their Upvest-supported Stocks and Shares ISA. You create the transfer through the Investment API, and Upvest credits the account group and applies any current year subscription to the end user's annual ISA allowance.

Upvest has no visibility of the source ISA, so you supply two things the API cannot derive: the cash amount to credit, and how much of that amount relates to the current tax year. For background on the stages and the allowance rules, see the ISA transfers overview.

The Investment API currently supports incoming, cash settled transfers within the same ISA manager. Transfers between different ISA managers, outgoing transfers, and in-specie settlement are planned for a future release.

Transfer lifecycle

Each status change emits a matching ISA_TRANSFER.* webhook event.

StatusDescription
NEWThe transfer has been created and has not started processing.
PROCESSINGThe transfer is being processed.
SETTLEDThe transfer is complete. The cash has been credited and any current year subscription has been applied to the allowance. This status is terminal.
CANCELLEDThe transfer has been cancelled and no funds move. This status is terminal.

Create an ISA transfer

POST /isa/transfers

This operation requires an idempotency-key header containing a randomly generated UUID. Reuse the same value when retrying a request so the transfer is not created twice. For more information, refer to Idempotency.

The response returns the created transfer with status NEW.

{
  "direction": "INCOMING",
  "type": "NO_ISA_MANAGER_CHANGE",
  "method": "CASH",
  "user_id": "4b2e2e64-7544-4c3f-a98d-dfdc03d487aa",
  "account_group_id": "79e9f9a2-dd29-4b54-9cca-b4c31956beb3",
  "cash_amount": "10000.00",
  "currency": "GBP",
  "counterparty": {
    "account_number": "5WJSZB3C8"
  },
  "current_year_subscription": {
    "tax_year": "2025/2026",
    "cash_amount": "3000.00",
    "first_subscription_at": "2025-09-14T12:10:00.00Z"
  }
}

Request fields

FieldRequired/OptionalDescription
directionRequiredThe direction of the transfer. Currently, only INCOMING (funds move into the Upvest-supported Stocks and Shares ISA) is supported.
typeRequiredThe type of the transfer. Currently, only NO_ISA_MANAGER_CHANGE (the transfer occurs within the same ISA manager) is supported.
methodRequiredThe settlement method of the transfer. Currently, only CASH is supported.
user_idRequiredUnique identifier of the end user making the transfer.
account_group_idRequiredUnique identifier of the account group that receives the cash. This account group must hold an ACTIVE ISA tax wrapper.
currencyRequiredCurrency of the transfer, as an ISO 4217 code. Currently, only GBP is supported.
cash_amountConditionalTotal cash amount of the transfer, as a decimal string with up to nine digits and two decimal places, for example "10000.00". Required for NO_ISA_MANAGER_CHANGE transfers.
counterparty.account_numberConditionalThe unique reference of the account group being transferred, such as the ISA reference number of the source ISA. Maximum 28 characters. Required when counterparty is provided.
current_year_subscriptionConditionalDetails of the part of the transfer that relates to the current tax year. Required for NO_ISA_MANAGER_CHANGE transfers where the end user has subscribed to the source ISA in the current tax year.
current_year_subscription.tax_yearConditionalThe current tax year in YYYY/YYYY format, for example "2025/2026". Required when current_year_subscription is provided.
current_year_subscription.cash_amountConditionalThe part of cash_amount that relates to current tax year subscriptions and so counts towards the current year ISA allowance. Required when current_year_subscription is provided.
current_year_subscription.first_subscription_atOptionalThe date and time of the first subscription made to the source ISA in the current tax year. RFC 3339-5, ISO8601 UTC.

current_year_subscription.cash_amount must not exceed cash_amount. Only the current year portion counts towards the annual allowance — the remainder is treated as previous years' balance and does not affect the allowance.

Response fields

Only the fields not present in the request are listed below.

FieldRequired/OptionalDescription
idRequiredUnique identifier of the ISA transfer, as a UUID.
created_atRequiredDate and time when the resource was created. RFC 3339-5, ISO8601 UTC.
updated_atRequiredDate and time when the resource was last updated. RFC 3339-5, ISO8601 UTC.
statusRequiredStatus of the transfer: NEW, PROCESSING, SETTLED, or CANCELLED. See Transfer lifecycle.

After the transfer settles

When a transfer reaches SETTLED:

  • The cash is credited to the account group, and the credit appears as a cash transaction of type ISA_TRANSFER. You can retrieve it through the cash transactions endpoint, and it also triggers a CASH_TRANSACTION.EXECUTED webhook event whose object.type is ISA_TRANSFER.
  • Where the transfer included a current_year_subscription, the used amount on the receiving wrapper's annual allowance increases and an ISA_WRAPPER_ALLOWANCE.UPDATED event is sent. Update the allowance on the source ISA to reflect the amount that moved.