> ## Documentation Index
> Fetch the complete documentation index at: https://developer.uphold.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SEPA bank withdrawal via the REST API

> Send EUR SEPA bank withdrawals using the Uphold REST API: list linked external accounts, generate and confirm a quote, and monitor settlement.

This guide walks you through the steps to support SEPA bank withdrawals using the REST API — from listing external accounts to monitoring for settlement.

## Prerequisites

* The user has [completed onboarding](/developer-guides/user-onboarding/overview) and has the required capabilities enabled.

## Walkthrough

```mermaid theme={null}
sequenceDiagram
  autonumber
  participant Usr as User
  participant U as Your App
  participant B as Your Backend
  participant A as Uphold
  participant F as Bank

  Usr->>U: Start withdrawal
  U->>B: Find bank account
  B->>A: GET /core/external-accounts
  A-->>B: { externalAccounts }
  B-->>U: { externalAccounts }
  Usr->>U: Select bank account
  Usr->>U: Choose source account and amount
  U->>B: Request quote
  B->>A: Create quote (account -> external account)
  A-->>B: { quote }
  B-->>U: { quote }
  Usr->>U: Confirm quote
  U->>B: Create transaction
  B->>A: Create transaction
  A-->>B: { transaction }
  A-->>B: webhook: transaction.created (processing)
  A->>F: Submit bank transfer
  F-->>A: Settlement confirmed
  A-->>B: webhook: transaction.status-changed (completed/failed)
  B-->>Usr: Notify the user
```

***

## Check available rails

Before creating a quote, verify the SEPA rail is available for withdrawals. Call [List Rails](/rest-apis/core-api/assets/list-rails) for `EUR` to confirm the `sepa` network has the `withdraw` feature.

```http theme={null}
GET /core/rails?type=bank&network=sepa&asset=EUR
```

The rail always exists in the system, but the `withdraw` feature is only present if it's enabled for the user.

```json theme={null}
{
  "rails": [
    {
      "type": "bank",
      "network": "sepa",
      "method": "bank-transfer",
      "asset": "EUR",
      "decimals": 2,
      "features": [
        "deposit",
        "withdraw"
      ]
    }
  ]
}
```

## Select source account

SEPA withdrawals can be sourced from any account. If the selected account is not in EUR, the balance will be converted at the time of the transaction using Uphold's prevailing rate. Make sure the origin asset has the necessary [features enabled](/rest-apis/core-api/assets/introduction#features-and-deposits-/-withdrawals).

Call [List accounts](/rest-apis/core-api/accounts/list-accounts) to retrieve the user's accounts and let them pick the one to withdraw from.

```http theme={null}
GET /core/accounts
```

```json theme={null}
{
  "accounts": [
    {
      "id": "a00507fe-628c-4f27-ae81-e1c40b2a8fb8",
      "ownerId": "e4ce04dc-67b7-4e9f-af91-482cb6f9fc4a",
      "label": "My EUR account",
      "asset": "EUR",
      "balance": {
        "total": "500.00",
        "available": "500.00"
      }
    }
  ]
}
```

## Select a bank account

SEPA bank accounts are registered automatically as external accounts when a user makes their first SEPA deposit — they cannot be added manually. If the user has no SEPA-linked accounts yet, direct them to complete a [SEPA deposit](/developer-guides/bank-transfers/deposit/via-rest-api/sepa) first.

Call [List external accounts](/rest-apis/core-api/external-accounts/list-external-accounts) and filter for accounts with `type: "bank"`.

```http theme={null}
GET /core/external-accounts
```

Make sure the selected account has `status: "ok"` and `"withdraw"` in `features`.

```json theme={null}
{
  "externalAccounts": [
    {
      "id": "cc8e8abc-0f95-4b9f-8490-2569a67bd80e",
      "type": "bank",
      "status": "ok",
      "network": "sepa",
      "label": "My EUR Account",
      "features": [
        "withdraw"
      ],
      "details": {
        "iban": "AT487954841229809844",
        "bic": "EXAAAT2K"
      }
    }
  ]
}
```

## Create a quote

To initiate the withdrawal, call [Create quote](/rest-apis/core-api/transactions/create-quote) with the origin as the user's account and the destination as the selected SEPA external account. Specify the amount and asset for the withdrawal.

```http theme={null}
POST /core/transactions/quote
{
  "origin": {
    "type": "account",
    "id": "a00507fe-628c-4f27-ae81-e1c40b2a8fb8"
  },
  "destination": {
    "type": "external-account",
    "id": "cc8e8abc-0f95-4b9f-8490-2569a67bd80e"
  },
  "denomination": {
    "asset": "EUR",
    "amount": "250.00",
    "target": "origin"
  }
}
```

A successful response returns a `quote` object with details about the withdrawal, including fees and expiration.

```json [expandable] theme={null}
{
  "quote": {
    "id": "623000c8-9bdf-4a2b-aa3d-6a6b44a7f6a0",
    "origin": {
      "amount": "250.00",
      "asset": "EUR",
      "node": {
        "type": "account",
        "id": "a00507fe-628c-4f27-ae81-e1c40b2a8fb8",
        "ownerId": "e4ce04dc-67b7-4e9f-af91-482cb6f9fc4a"
      },
      "rate": "1"
    },
    "destination": {
      "amount": "250.00",
      "asset": "EUR",
      "node": {
        "type": "external-account",
        "id": "cc8e8abc-0f95-4b9f-8490-2569a67bd80e",
        "ownerId": "e4ce04dc-67b7-4e9f-af91-482cb6f9fc4a"
      },
      "rate": "1"
    },
    "denomination": {
      "asset": "EUR",
      "amount": "250.00",
      "target": "origin",
      "rate": "1"
    },
    "fees": [],
    "expiresAt": "2024-07-24T15:22:39Z"
  }
}
```

<Info>Quotes typically **expire** quickly. Prompt for user confirmation within the expiry window and requote if needed.</Info>

## Confirm and create transaction

After the user confirms the withdrawal, call [Create transaction](/rest-apis/core-api/transactions/create-transaction) with the quote ID to execute the transfer.

```http theme={null}
POST /core/transactions
{
  "quoteId": "623000c8-9bdf-4a2b-aa3d-6a6b44a7f6a0"
}
```

In a successful SEPA withdrawal, the origin is the user's `account` and the destination is the `external-account` representing the user's bank. The transaction status is initially `processing` and updates to `completed` once the transfer settles.

```json [expandable] theme={null}
{
  "transaction": {
    "id": "d3e4f5a6-1b2c-4d5e-9f8a-7b6c5d4e3f2a",
    "origin": {
      "asset": "EUR",
      "amount": "250.00",
      "node": {
        "type": "account",
        "id": "a00507fe-628c-4f27-ae81-e1c40b2a8fb8",
        "ownerId": "e4ce04dc-67b7-4e9f-af91-482cb6f9fc4a"
      }
    },
    "destination": {
      "asset": "EUR",
      "amount": "250.00",
      "node": {
        "type": "external-account",
        "id": "cc8e8abc-0f95-4b9f-8490-2569a67bd80e",
        "ownerId": "e4ce04dc-67b7-4e9f-af91-482cb6f9fc4a",
        "network": "sepa"
      }
    },
    "status": "processing",
    "quotedAt": "2025-01-10T14:22:15Z",
    "createdAt": "2025-01-10T14:22:45Z",
    "updatedAt": "2025-01-10T14:22:45Z",
    "denomination": {
      "asset": "EUR",
      "amount": "250.00",
      "target": "origin"
    }
  }
}
```

## Monitor for settlement

Prefer **webhooks** for real-time updates, or fall back to **polling** if webhooks are not feasible.

* Webhook events (recommended):
  * [core.transaction.created](/rest-apis/core-api/transactions/webhooks/transaction-created)
    * `status: processing` → payout submitted to the bank network
  * [core.transaction.status-changed](/rest-apis/core-api/transactions/webhooks/transaction-status-changed)
    * `status: completed` → funds delivered to the user's bank
    * `status: failed` → irrecoverable error
* Polling (fallback): [Get transaction](/rest-apis/core-api/transactions/get-transaction)

## Notify the user

Display an in-app confirmation when the transaction is `completed`, and send an email if applicable.

<Check>You now support SEPA bank transfer withdrawals via the REST API.</Check>
