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

# Halliday API Quickstart

Halliday is a unified deposits API combining a global on/off ramp network, cross-chain swap router, and centralized exchange connector into a single product.

The REST API is available to developers building custom user interfaces on any device.

Open source apps with custom web interfaces that use the REST API directly instead of the SDK are available in the [API Example Apps](/pages/api-example-apps) section.

For a simpler payments integration with a web user interface, see the [Payments widget](/pages/payments-sdk-docs) documentation.

### Authentication

All API requests require authentication using an API key in the `Authorization` HTTP request header. Get a free API key at [dashboard.halliday.xyz](https://dashboard.halliday.xyz/).

```
Authorization: Bearer pk_HALLIDAY_API_KEY_HERE
```

### One Time Wallet (OTW)

Each unique payment begins with a new onchain address called a **one-time wallet** (OTW) also known as the **deposit address**. The OTW is always different from the payment destination address. The Payments SDK widget UI refers to this address as a one-time wallet in the delivery details section.

Each onramp or swap will create a new OTW that is controlled only by the **owner wallet address** specified in the API call parameters. In the event a payment gets stuck or is funded after expiration, the owner address has the ability to sign transactions to recover the assets from the OTW or retry the payment.

A payment can be initiated by simply funding the OTW from any address. For fiat onramps specifically, the onramp providers will be instructed to send tokens to the deposit address.

## Discovering Available Assets

Halliday supports hundreds of crypto assets for fiat onramps from centralized exchanges and payment providers, as well as onchain swaps via bridges and DEXs.

Before initializing a payment, use this endpoint to see which crypto assets are supported.

### Get All Supported Assets

<CodeGroup>
  ```bash cURL theme={null}
  curl https://v2.prod.halliday.xyz/assets \
    -H "Authorization: Bearer pk_HALLIDAY_API_KEY_HERE"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://v2.prod.halliday.xyz/assets', {
    headers: {
      'Authorization': 'Bearer pk_HALLIDAY_API_KEY_HERE'
    }
  });
  const data = await response.json();
  ```

  ```python Python theme={null}
  import urllib.request

  request = urllib.request.Request(
      'https://v2.prod.halliday.xyz/assets',
      headers={'Authorization': 'Bearer pk_HALLIDAY_API_KEY_HERE'}
  )
  response = urllib.request.urlopen(request)
  data = response.read()
  ```

  ```php PHP theme={null}
  <?php
  $ch = curl_init('https://v2.prod.halliday.xyz/assets');
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'Authorization: Bearer pk_HALLIDAY_API_KEY_HERE'
  ]);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $response = curl_exec($ch);
  curl_close($ch);
  ?>
  ```

  ```go Go theme={null}
  package main

  import (
      "net/http"
      "io"
  )

  func main() {
      req, _ := http.NewRequest("GET", "https://v2.prod.halliday.xyz/assets", nil)
      req.Header.Set("Authorization", "Bearer pk_HALLIDAY_API_KEY_HERE")
      
      client := &http.Client{}
      resp, _ := client.Do(req)
      defer resp.Body.Close()
      
      body, _ := io.ReadAll(resp.Body)
  }
  ```

  ```java Java theme={null}
  import java.net.HttpURLConnection;
  import java.net.URL;

  URL url = new URL("https://v2.prod.halliday.xyz/assets");
  HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  conn.setRequestProperty("Authorization", "Bearer pk_HALLIDAY_API_KEY_HERE");
  conn.setRequestMethod("GET");

  int status = conn.getResponseCode();
  ```

  ```ruby Ruby theme={null}
  require 'net/http'
  require 'uri'

  uri = URI('https://v2.prod.halliday.xyz/assets')
  request = Net::HTTP::Get.new(uri)
  request['Authorization'] = 'Bearer pk_HALLIDAY_API_KEY_HERE'

  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
    http.request(request)
  end
  ```
</CodeGroup>

**Response:**

```json theme={null}
{
  "usd": {
    "issuer": "USA",
    "name": "United States Dollar",
    "symbol": "USD",
    "decimals": 2,
    "image_url": "<IMAGE_URL>..."
  },
  "arbitrum:0xaf88d065e77c8cc2239327c5edb3a432268e5831": {
    "chain": "arbitrum",
    "address": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
    "name": "USD Coin",
    "symbol": "USDC",
    "decimals": 6,
    "image_url": "<IMAGE_URL>...",
    "is_native": false
  },
  "ethereum:0x": {
    "chain": "ethereum",
    "address": "0x",
    "name": "Ether",
    "symbol": "ETH",
    "decimals": 18,
    "image_url": "<IMAGE_URL>...",
    "is_native": true
  }
}
```

### Get valid deposit routes

To verify conversion from a specific input to a desired output, query both assets together like in the following example.

Get a list of assets that can be received as outputs for the given input assets and providers using `/assets/available-outputs`.

Each of the query parameters of `inputs` and `outputs` are both arrays of strings of asset IDs which are returned by `/assets`.

The `/assets/available-outputs` endpoint requires at least one input asset.

Additionally, onramp provider checks can be done by passing an `onramps` array of strings (`moonpay`, `coinbase`, et al). This filtering ensures a path from an input to an output can be achieved with a given onramp provider.

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://v2.prod.halliday.xyz/assets/available-outputs\
  ?inputs[]=usd\
  &outputs[]=ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" \
    -H "Authorization: Bearer pk_HALLIDAY_API_KEY_HERE"
  ```

  ```javascript JavaScript theme={null}
  const params = new URLSearchParams({
    'inputs[]': 'usd',
    'outputs[]': 'ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'
  });

  const response = await fetch(`https://v2.prod.halliday.xyz/assets/available-outputs?${params}`, {
    headers: {
      'Authorization': 'Bearer pk_HALLIDAY_API_KEY_HERE'
    }
  });
  const data = await response.json();
  ```

  ```python Python theme={null}
  import urllib.request
  import urllib.parse

  params = urllib.parse.urlencode({
      'inputs[]': 'usd',
      'outputs[]': 'ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'
  })
  url = f'https://v2.prod.halliday.xyz/assets/available-outputs?{params}'

  request = urllib.request.Request(
      url,
      headers={'Authorization': 'Bearer pk_HALLIDAY_API_KEY_HERE'}
  )
  response = urllib.request.urlopen(request)
  data = response.read()
  ```

  ```php PHP theme={null}
  <?php
  $params = http_build_query([
      'inputs' => ['usd'],
      'outputs' => ['ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48']
  ]);
  $url = 'https://v2.prod.halliday.xyz/assets/available-outputs?' . $params;

  $ch = curl_init($url);
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'Authorization: Bearer pk_HALLIDAY_API_KEY_HERE'
  ]);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $response = curl_exec($ch);
  curl_close($ch);
  ?>
  ```

  ```go Go theme={null}
  package main

  import (
      "net/http"
      "net/url"
      "io"
  )

  func main() {
      baseURL, _ := url.Parse("https://v2.prod.halliday.xyz/assets/available-outputs")
      params := url.Values{}
      params.Add("inputs[]", "usd")
      params.Add("outputs[]", "ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48")
      baseURL.RawQuery = params.Encode()
      
      req, _ := http.NewRequest("GET", baseURL.String(), nil)
      req.Header.Set("Authorization", "Bearer pk_HALLIDAY_API_KEY_HERE")
      
      client := &http.Client{}
      resp, _ := client.Do(req)
      defer resp.Body.Close()
      
      body, _ := io.ReadAll(resp.Body)
  }
  ```

  ```java Java theme={null}
  import java.net.HttpURLConnection;
  import java.net.URL;
  import java.net.URLEncoder;

  String params = "inputs[]=usd&outputs[]=ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48";
  String urlString = "https://v2.prod.halliday.xyz/assets/available-outputs?" + params;

  URL url = new URL(urlString);
  HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  conn.setRequestProperty("Authorization", "Bearer pk_HALLIDAY_API_KEY_HERE");
  conn.setRequestMethod("GET");

  int status = conn.getResponseCode();
  ```

  ```ruby Ruby theme={null}
  require 'net/http'
  require 'uri'

  uri = URI('https://v2.prod.halliday.xyz/assets/available-outputs')
  params = {
    'inputs[]' => 'usd',
    'outputs[]' => 'ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'
  }
  uri.query = URI.encode_www_form(params)

  request = Net::HTTP::Get.new(uri)
  request['Authorization'] = 'Bearer pk_HALLIDAY_API_KEY_HERE'

  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
    http.request(request)
  end
  ```
</CodeGroup>

This checks if USD can be converted to USDC on Ethereum. If the response includes the desired output asset, the route is supported.

**Response:**

```json theme={null}
{
  "usd": {
    "fiats": [],
    "tokens": [
      "ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
    ]
  }
}
```

The response shows that USDC on Ethereum can be obtained from USD (fiat). A 200 OK empty object JSON string response `{}` would indicate that there is no route currently supported.

### Verify multiple routes in one request

The following is an example of providing more than one member to an array in the request query parameters.

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://v2.prod.halliday.xyz/assets/available-outputs\
  ?inputs[]=usd\
  &outputs[]=base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913\
  &outputs[]=avalanche:0x" \
    -H "Authorization: Bearer pk_HALLIDAY_API_KEY_HERE"
  ```

  ```javascript JavaScript theme={null}
  const params = new URLSearchParams();
  params.append('inputs[]', 'usd');
  params.append('outputs[]', 'base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913');
  params.append('outputs[]', 'avalanche:0x');

  const response = await fetch(`https://v2.prod.halliday.xyz/assets/available-outputs?${params}`, {
    headers: {
      'Authorization': 'Bearer pk_HALLIDAY_API_KEY_HERE'
    }
  });
  const data = await response.json();
  ```

  ```python Python theme={null}
  import urllib.request
  import urllib.parse

  params = [
      ('inputs[]', 'usd'),
      ('outputs[]', 'base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913'),
      ('outputs[]', 'avalanche:0x')
  ]
  url = f'https://v2.prod.halliday.xyz/assets/available-outputs?{urllib.parse.urlencode(params)}'

  request = urllib.request.Request(
      url,
      headers={'Authorization': 'Bearer pk_HALLIDAY_API_KEY_HERE'}
  )
  response = urllib.request.urlopen(request)
  data = response.read()
  ```

  ```php PHP theme={null}
  <?php
  $params = http_build_query([
      'inputs' => ['usd'],
      'outputs' => [
          'base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
          'avalanche:0x'
      ]
  ]);
  $url = 'https://v2.prod.halliday.xyz/assets/available-outputs?' . $params;

  $ch = curl_init($url);
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'Authorization: Bearer pk_HALLIDAY_API_KEY_HERE'
  ]);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $response = curl_exec($ch);
  curl_close($ch);
  ?>
  ```

  ```go Go theme={null}
  package main

  import (
      "net/http"
      "net/url"
      "io"
  )

  func main() {
      baseURL, _ := url.Parse("https://v2.prod.halliday.xyz/assets/available-outputs")
      params := url.Values{}
      params.Add("inputs[]", "usd")
      params.Add("outputs[]", "base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913")
      params.Add("outputs[]", "avalanche:0x")
      baseURL.RawQuery = params.Encode()
      
      req, _ := http.NewRequest("GET", baseURL.String(), nil)
      req.Header.Set("Authorization", "Bearer pk_HALLIDAY_API_KEY_HERE")
      
      client := &http.Client{}
      resp, _ := client.Do(req)
      defer resp.Body.Close()
      
      body, _ := io.ReadAll(resp.Body)
  }
  ```

  ```java Java theme={null}
  import java.net.HttpURLConnection;
  import java.net.URL;

  String params = "inputs[]=usd&outputs[]=base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913&outputs[]=avalanche:0x";
  String urlString = "https://v2.prod.halliday.xyz/assets/available-outputs?" + params;

  URL url = new URL(urlString);
  HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  conn.setRequestProperty("Authorization", "Bearer pk_HALLIDAY_API_KEY_HERE");
  conn.setRequestMethod("GET");

  int status = conn.getResponseCode();
  ```

  ```ruby Ruby theme={null}
  require 'net/http'
  require 'uri'

  uri = URI('https://v2.prod.halliday.xyz/assets/available-outputs')
  params = [
    ['inputs[]', 'usd'],
    ['outputs[]', 'base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913'],
    ['outputs[]', 'avalanche:0x']
  ]
  uri.query = URI.encode_www_form(params)

  request = Net::HTTP::Get.new(uri)
  request['Authorization'] = 'Bearer pk_HALLIDAY_API_KEY_HERE'

  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
    http.request(request)
  end
  ```
</CodeGroup>

The response confirms that USDC on Base and AVAX on Avalanche can be obtained from USD (fiat).

```json theme={null}
{
  "usd": {
    "fiats": [],
    "tokens": [
      "base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
      "avalanche:0x"
    ]
  }
}
```

## Fiat-to-Crypto Onramping with the API

Halliday supports fiat onramp providers like Stripe, Moonpay, Transak and cryptocurrency exchanges (CEX) to enable users to compliantly onramp using fiat in their region with the best price available.

This enables end users to pay with credit or debit card, Apple Pay, Google Pay, or a centralized exchange balance with one simple interface.

### Get a collection of quotes for an onramp

When a user is ready to onramp, make an API request to generate quotes based on the amount of fiat the user desires to spend.

The Halliday API queries onramp providers to create quotes for the delivery of tokens onchain. The API intelligently surfaces providers based on the detected or provided IP address.

Additional checks are performed to identify if bridge and DEX calls are necessary to deliver the desired output token. All quoted routes are returned to the client with fees included.

This data can then be displayed in a UI so the user can select their preferred provider and quote.

All quotes can be requested through the unified `POST /payments/quotes` endpoint.

#### Example: Quoting fiat-to-crypto onramp

Request a quote for purchasing USDC on Base with USD:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://v2.prod.halliday.xyz/payments/quotes" \
    -H "Authorization: Bearer pk_HALLIDAY_API_KEY_HERE" \
    -H "Content-Type: application/json" \
    -d '{
      "request": {
        "kind": "FIXED_INPUT",
        "fixed_input_amount": {
          "asset": "USD",
          "amount": "100"
        },
        "output_asset": "base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
      },
      "price_currency": "USD",
      "onramps": [ "MOONPAY", "TRANSAK", "STRIPE" ],
      "onramp_methods": [ "CREDIT_CARD", "ACH" ],
      "customer_ip_address": "<your_customer_ip_address>"
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://v2.prod.halliday.xyz/payments/quotes', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer pk_HALLIDAY_API_KEY_HERE',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      request: {
        kind: 'FIXED_INPUT',
        fixed_input_amount: {
          asset: 'USD',
          amount: '100'
        },
        output_asset: 'base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913'
      },
      price_currency: 'USD',
      onramps: ['MOONPAY', 'TRANSAK', 'STRIPE'],
      onramp_methods: ['CREDIT_CARD', 'ACH'],
      customer_ip_address: '<your_customer_ip_address>'
    })
  });
  const data = await response.json();
  ```

  ```python Python theme={null}
  import urllib.request
  import json

  data = {
      'request': {
          'kind': 'FIXED_INPUT',
          'fixed_input_amount': {
              'asset': 'USD',
              'amount': '100'
          },
          'output_asset': 'base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913'
      },
      'price_currency': 'USD',
      'onramps': ['MOONPAY', 'TRANSAK', 'STRIPE'],
      'onramp_methods': ['CREDIT_CARD', 'ACH'],
      'customer_ip_address': '<your_customer_ip_address>'
  }

  request = urllib.request.Request(
      'https://v2.prod.halliday.xyz/payments/quotes',
      data=json.dumps(data).encode('utf-8'),
      headers={
          'Authorization': 'Bearer pk_HALLIDAY_API_KEY_HERE',
          'Content-Type': 'application/json'
      },
      method='POST'
  )
  response = urllib.request.urlopen(request)
  result = response.read()
  ```

  ```php PHP theme={null}
  <?php
  $data = [
      'request' => [
          'kind' => 'FIXED_INPUT',
          'fixed_input_amount' => [
              'asset' => 'USD',
              'amount' => '100'
          ],
          'output_asset' => 'base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913'
      ],
      'price_currency' => 'USD',
      'onramps' => ['MOONPAY', 'TRANSAK', 'STRIPE'],
      'onramp_methods' => ['CREDIT_CARD', 'ACH'],
      'customer_ip_address' => '<your_customer_ip_address>'
  ];

  $ch = curl_init('https://v2.prod.halliday.xyz/payments/quotes');
  curl_setopt($ch, CURLOPT_POST, true);
  curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'Authorization: Bearer pk_HALLIDAY_API_KEY_HERE',
      'Content-Type: application/json'
  ]);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $response = curl_exec($ch);
  curl_close($ch);
  ?>
  ```

  ```go Go theme={null}
  package main

  import (
      "net/http"
      "bytes"
      "encoding/json"
      "io"
  )

  func main() {
      data := map[string]interface{}{
          "request": map[string]interface{}{
              "kind": "FIXED_INPUT",
              "fixed_input_amount": map[string]string{
                  "asset": "USD",
                  "amount": "100",
              },
              "output_asset": "base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
          },
          "price_currency": "USD",
          "onramps": []string{"MOONPAY", "TRANSAK", "STRIPE"},
          "onramp_methods": []string{"CREDIT_CARD", "ACH"},
          "customer_ip_address": "<your_customer_ip_address>",
      }
      
      jsonData, _ := json.Marshal(data)
      req, _ := http.NewRequest("POST", "https://v2.prod.halliday.xyz/payments/quotes", bytes.NewBuffer(jsonData))
      req.Header.Set("Authorization", "Bearer pk_HALLIDAY_API_KEY_HERE")
      req.Header.Set("Content-Type", "application/json")
      
      client := &http.Client{}
      resp, _ := client.Do(req)
      defer resp.Body.Close()
      
      body, _ := io.ReadAll(resp.Body)
  }
  ```

  ```java Java theme={null}
  import java.net.HttpURLConnection;
  import java.net.URL;
  import java.io.OutputStream;

  URL url = new URL("https://v2.prod.halliday.xyz/payments/quotes");
  HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  conn.setRequestMethod("POST");
  conn.setRequestProperty("Authorization", "Bearer pk_HALLIDAY_API_KEY_HERE");
  conn.setRequestProperty("Content-Type", "application/json");
  conn.setDoOutput(true);

  String jsonData = "{\"request\":{\"kind\":\"FIXED_INPUT\",\"fixed_input_amount\":{\"asset\":\"USD\",\"amount\":\"100\"},\"output_asset\":\"base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913\"},\"price_currency\":\"USD\",\"onramps\":[\"MOONPAY\",\"TRANSAK\",\"STRIPE\"],\"onramp_methods\":[\"CREDIT_CARD\",\"ACH\"],\"customer_ip_address\":\"<your_customer_ip_address>\"}";

  try (OutputStream os = conn.getOutputStream()) {
      os.write(jsonData.getBytes());
  }

  int status = conn.getResponseCode();
  ```

  ```ruby Ruby theme={null}
  require 'net/http'
  require 'uri'
  require 'json'

  uri = URI('https://v2.prod.halliday.xyz/payments/quotes')
  request = Net::HTTP::Post.new(uri)
  request['Authorization'] = 'Bearer pk_HALLIDAY_API_KEY_HERE'
  request['Content-Type'] = 'application/json'
  request.body = {
    request: {
      kind: 'FIXED_INPUT',
      fixed_input_amount: {
        asset: 'USD',
        amount: '100'
      },
      output_asset: 'base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913'
    },
    price_currency: 'USD',
    onramps: ['MOONPAY', 'TRANSAK', 'STRIPE'],
    onramp_methods: ['CREDIT_CARD', 'ACH'],
    customer_ip_address: '<your_customer_ip_address>'
  }.to_json

  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
    http.request(request)
  end
  ```
</CodeGroup>

**Response:**

```json theme={null}
{
  "quote_request": {
    "request": {
      "kind": "FIXED_INPUT",
      "fixed_input_amount": {
        "asset": "usd",
        "amount": "100"
      },
      "output_asset": "base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
    },
    "price_currency": "usd",
    "onramps": [
      "moonpay",
      "transak",
      "stripe"
    ],
    "onramp_methods": [
      "CREDIT_CARD",
      "ACH"
    ]
  },
  "quotes": [
    {
      "payment_id": "v4uuid...",
      "onramp": "stripe",
      "onramp_method": "CREDIT_CARD",
      "output_amount": {
        "asset": "base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
        "amount": "96.11"
      },
      "fees": {
        "total_fees": "3.90941422",
        "conversion_fees": "3.90941422",
        "network_fees": "0",
        "business_fees": "0",
        "currency_symbol": "usd"
      },
      "route": [...]
    }
  ],
  "current_prices": {
    "usd": "1.0",
    "base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913": "0.999798",
    "base:0x": "4058.01",
    "avalanche:0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e": "0.999798",
    "avalanche:0x": "19.8",
    "arbitrum:0xaf88d065e77c8cc2239327c5edb3a432268e5831": "0.999798",
    "arbitrum:0x": "4058.01"
  },
  "price_currency": "usd",
  "state_token": "H4sIAAA...",
  "quoted_at": "2025-10-28T19:29:18.351Z",
  "accept_by": "2025-10-28T19:34:18.351Z",
  "failures": []
}
```

### Confirm and fund the onramp

Once the user selects a quote, the onramp payment needs to be confirmed with the API before the quote expires.

Using the `payment_id` and `state_token` returned in the quote, a request is made to confirm the quote and specify the owner and destination. The owner and destination wallet addresses can be different.

The private key of the owner address is the **sole controller** of the one-time wallet. In the scenario that a recovery is needed, the owner address will need to sign transactions. See [API Recoveries & Errors](/pages/api-error-recovery-withdrawal) for more details.

This request (`POST /payments/confirm`) returns the same response body that the payment status endpoint (`GET /payments`) returns.

If the payment output is >= \$300 USD and the owner address has not been verified, the response will have a status of `UNCONFIRMED` with a `USER_VERIFY` next instruction instead. The user must sign the verification payloads and submit them back to `POST /payments/confirm` as a `ContinueConfirmPaymentRequest` before the payment can proceed. See [Owner verification](/pages/api-status#owner-verification-user_verify) for details.

Within the `next_instruction` response body object, there is a `funding_page_url` string. This URL navigates the user to the payment page where they input their fiat payment information with the onramp provider. Developers are expected to display this web page to the user.

In the use case that a user does not fund the onramp directly, the `deposit_address` (OTW) can be funded by anyone with the `deposit_amount` to initiate the onramp.

#### Example: Confirm fiat-to-crypto onramp

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://v2.prod.halliday.xyz/payments/confirm" \
    -H "Authorization: Bearer pk_HALLIDAY_API_KEY_HERE" \
    -H "Content-Type: application/json" \
    -d '{
      "payment_id": "v4uuid...",
      "state_token": "H4sIAAA...",
      "owner_address": "0xowner...",
      "destination_address": "0xdestination..."
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://v2.prod.halliday.xyz/payments/confirm', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer pk_HALLIDAY_API_KEY_HERE',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      payment_id: 'v4uuid...',
      state_token: 'H4sIAAA...',
      owner_address: '0xowner...',
      destination_address: '0xdestination...'
    })
  });
  const data = await response.json();
  ```

  ```python Python theme={null}
  import urllib.request
  import json

  data = {
      'payment_id': 'v4uuid...',
      'state_token': 'H4sIAAA...',
      'owner_address': '0xowner...',
      'destination_address': '0xdestination...'
  }

  request = urllib.request.Request(
      'https://v2.prod.halliday.xyz/payments/confirm',
      data=json.dumps(data).encode('utf-8'),
      headers={
          'Authorization': 'Bearer pk_HALLIDAY_API_KEY_HERE',
          'Content-Type': 'application/json'
      },
      method='POST'
  )
  response = urllib.request.urlopen(request)
  result = response.read()
  ```

  ```php PHP theme={null}
  <?php
  $data = [
      'payment_id' => 'v4uuid...',
      'state_token' => 'H4sIAAA...',
      'owner_address' => '0xowner...',
      'destination_address' => '0xdestination...'
  ];

  $ch = curl_init('https://v2.prod.halliday.xyz/payments/confirm');
  curl_setopt($ch, CURLOPT_POST, true);
  curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'Authorization: Bearer pk_HALLIDAY_API_KEY_HERE',
      'Content-Type: application/json'
  ]);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $response = curl_exec($ch);
  curl_close($ch);
  ?>
  ```

  ```go Go theme={null}
  package main

  import (
      "net/http"
      "bytes"
      "encoding/json"
      "io"
  )

  func main() {
      data := map[string]string{
          "payment_id": "v4uuid...",
          "state_token": "H4sIAAA...",
          "owner_address": "0xowner...",
          "destination_address": "0xdestination...",
      }
      
      jsonData, _ := json.Marshal(data)
      req, _ := http.NewRequest("POST", "https://v2.prod.halliday.xyz/payments/confirm", bytes.NewBuffer(jsonData))
      req.Header.Set("Authorization", "Bearer pk_HALLIDAY_API_KEY_HERE")
      req.Header.Set("Content-Type", "application/json")
      
      client := &http.Client{}
      resp, _ := client.Do(req)
      defer resp.Body.Close()
      
      body, _ := io.ReadAll(resp.Body)
  }
  ```

  ```java Java theme={null}
  import java.net.HttpURLConnection;
  import java.net.URL;
  import java.io.OutputStream;

  URL url = new URL("https://v2.prod.halliday.xyz/payments/confirm");
  HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  conn.setRequestMethod("POST");
  conn.setRequestProperty("Authorization", "Bearer pk_HALLIDAY_API_KEY_HERE");
  conn.setRequestProperty("Content-Type", "application/json");
  conn.setDoOutput(true);

  String jsonData = "{\"payment_id\":\"v4uuid...\",\"state_token\":\"H4sIAAA...\",\"owner_address\":\"0xowner...\",\"destination_address\":\"0xdestination...\"}";

  try (OutputStream os = conn.getOutputStream()) {
      os.write(jsonData.getBytes());
  }

  int status = conn.getResponseCode();
  ```

  ```ruby Ruby theme={null}
  require 'net/http'
  require 'uri'
  require 'json'

  uri = URI('https://v2.prod.halliday.xyz/payments/confirm')
  request = Net::HTTP::Post.new(uri)
  request['Authorization'] = 'Bearer pk_HALLIDAY_API_KEY_HERE'
  request['Content-Type'] = 'application/json'
  request.body = {
    payment_id: 'v4uuid...',
    state_token: 'H4sIAAA...',
    owner_address: '0xowner...',
    destination_address: '0xdestination...'
  }.to_json

  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
    http.request(request)
  end
  ```
</CodeGroup>

**Response:**

```json theme={null}
{
  "payment_id": "v4uuid...",
  "status": "PENDING",
  "funded": false,
  "created_at": "2025-10-28T19:30:12.818Z",
  "updated_at": "2025-10-28T19:30:12.818Z",
  "initiate_fund_by": "2025-10-28T19:40:12.818Z",
  "quote_request": {
    "request": {
      "kind": "FIXED_INPUT",
      "fixed_input_amount": {
        "asset": "usd",
        "amount": "100"
      },
      "output_asset": "base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
    },
    "price_currency": "usd",
    "onramps": [ "moonpay", "transak", "stripe" ],
    "onramp_methods": [ "CREDIT_CARD", "ACH" ]
  },
  "quoted": {
    "onramp": "stripe",
    "onramp_method": "CREDIT_CARD",
    "output_amount": {
      "asset": "base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
      "amount": "96.11"
    },
    "fees": {
      "total_fees": "3.90941422",
      "conversion_fees": "3.90941422",
      "network_fees": "0",
      "business_fees": "0",
      "currency_symbol": "usd"
    },
    "route": []
  },
  "fulfilled": {
    "onramp": "stripe",
    "onramp_method": "CREDIT_CARD",
    "route": [...]
  },
  "current_prices": {...},
  "price_currency": "usd",
  "processing_addresses": [],
  "owner_address": "0xowner...",
  "destination_address": "0xdestination...",
  "next_instruction": {
    "type": "ONRAMP",
    "payment_id": "v4uuid...",
    "funding_page_url": "https://app.halliday.xyz/funding/_payment_id_goes_here_?token=_token_",
    "deposit_info": [
      {
        "deposit_token": "base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
        "deposit_amount": "96.11",
        "deposit_address": "0xdeposit...",
        "deposit_chain": "base"
      }
    ]
  }
}
```

## Cross-chain swaps with the API

Halliday brings together bridges, DEXs, and cross-chain routing logic to enable users to seamlessly swap to any token on any chain.

### Get a collection of quotes for a swap

When a user is ready to swap tokens, make an API request to generate quotes based on the amount the user desires to swap.

The Halliday API queries all available cross-chain routes supported by the protocol, optimizing current bridge, DEX, and transfer fees. All quoted onchain routes are returned to the client, including all fees.

This data can then be displayed in a UI so the user can select their preferred route and quote.

All quotes are requested through the unified `POST /payments/quotes` endpoint.

#### Example: Quote a crypto-to-crypto swap

Request a quote for swapping AVAX on Avalanche to USDC on Base:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://v2.prod.halliday.xyz/payments/quotes" \
    -H "Authorization: Bearer pk_HALLIDAY_API_KEY_HERE" \
    -H "Content-Type: application/json" \
    -d '{
      "request": {
        "kind": "FIXED_INPUT",
        "fixed_input_amount": {
          "asset": "avalanche:0x",
          "amount": "50"
        },
        "output_asset": "base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
      },
      "price_currency": "USD"
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://v2.prod.halliday.xyz/payments/quotes', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer pk_HALLIDAY_API_KEY_HERE',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      request: {
        kind: 'FIXED_INPUT',
        fixed_input_amount: {
          asset: 'avalanche:0x',
          amount: '50'
        },
        output_asset: 'base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913'
      },
      price_currency: 'USD'
    })
  });
  const data = await response.json();
  ```

  ```python Python theme={null}
  import urllib.request
  import json

  data = {
      'request': {
          'kind': 'FIXED_INPUT',
          'fixed_input_amount': {
              'asset': 'avalanche:0x',
              'amount': '50'
          },
          'output_asset': 'base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913'
      },
      'price_currency': 'USD'
  }

  request = urllib.request.Request(
      'https://v2.prod.halliday.xyz/payments/quotes',
      data=json.dumps(data).encode('utf-8'),
      headers={
          'Authorization': 'Bearer pk_HALLIDAY_API_KEY_HERE',
          'Content-Type': 'application/json'
      },
      method='POST'
  )
  response = urllib.request.urlopen(request)
  result = response.read()
  ```

  ```php PHP theme={null}
  <?php
  $data = [
      'request' => [
          'kind' => 'FIXED_INPUT',
          'fixed_input_amount' => [
              'asset' => 'avalanche:0x',
              'amount' => '50'
          ],
          'output_asset' => 'base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913'
      ],
      'price_currency' => 'USD'
  ];

  $ch = curl_init('https://v2.prod.halliday.xyz/payments/quotes');
  curl_setopt($ch, CURLOPT_POST, true);
  curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'Authorization: Bearer pk_HALLIDAY_API_KEY_HERE',
      'Content-Type: application/json'
  ]);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $response = curl_exec($ch);
  curl_close($ch);
  ?>
  ```

  ```go Go theme={null}
  package main

  import (
      "net/http"
      "bytes"
      "encoding/json"
      "io"
  )

  func main() {
      data := map[string]interface{}{
          "request": map[string]interface{}{
              "kind": "FIXED_INPUT",
              "fixed_input_amount": map[string]string{
                  "asset": "avalanche:0x",
                  "amount": "50",
              },
              "output_asset": "base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
          },
          "price_currency": "USD",
      }
      
      jsonData, _ := json.Marshal(data)
      req, _ := http.NewRequest("POST", "https://v2.prod.halliday.xyz/payments/quotes", bytes.NewBuffer(jsonData))
      req.Header.Set("Authorization", "Bearer pk_HALLIDAY_API_KEY_HERE")
      req.Header.Set("Content-Type", "application/json")
      
      client := &http.Client{}
      resp, _ := client.Do(req)
      defer resp.Body.Close()
      
      body, _ := io.ReadAll(resp.Body)
  }
  ```

  ```java Java theme={null}
  import java.net.HttpURLConnection;
  import java.net.URL;
  import java.io.OutputStream;

  URL url = new URL("https://v2.prod.halliday.xyz/payments/quotes");
  HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  conn.setRequestMethod("POST");
  conn.setRequestProperty("Authorization", "Bearer pk_HALLIDAY_API_KEY_HERE");
  conn.setRequestProperty("Content-Type", "application/json");
  conn.setDoOutput(true);

  String jsonData = "{\"request\":{\"kind\":\"FIXED_INPUT\",\"fixed_input_amount\":{\"asset\":\"avalanche:0x\",\"amount\":\"50\"},\"output_asset\":\"base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913\"},\"price_currency\":\"USD\"}";

  try (OutputStream os = conn.getOutputStream()) {
      os.write(jsonData.getBytes());
  }

  int status = conn.getResponseCode();
  ```

  ```ruby Ruby theme={null}
  require 'net/http'
  require 'uri'
  require 'json'

  uri = URI('https://v2.prod.halliday.xyz/payments/quotes')
  request = Net::HTTP::Post.new(uri)
  request['Authorization'] = 'Bearer pk_HALLIDAY_API_KEY_HERE'
  request['Content-Type'] = 'application/json'
  request.body = {
    request: {
      kind: 'FIXED_INPUT',
      fixed_input_amount: {
        asset: 'avalanche:0x',
        amount: '50'
      },
      output_asset: 'base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913'
    },
    price_currency: 'USD'
  }.to_json

  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
    http.request(request)
  end
  ```
</CodeGroup>

**Response:**

```json theme={null}
{
  "quote_request": {
    "request": {
      "kind": "FIXED_INPUT",
      "fixed_input_amount": {
        "asset": "avalanche:0x",
        "amount": "50"
      },
      "output_asset": "base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
    },
    "price_currency": "usd"
  },
  "quotes": [
    {
      "payment_id": "v4uuid...",
      "output_amount": {
        "asset": "base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
        "amount": "963.20128"
      },
      "fees": {
        "total_fees": "1.89407692672",
        "conversion_fees": "1.89407692672",
        "network_fees": "0",
        "business_fees": "0",
        "currency_symbol": "usd"
      },
      "route": [...]
    }
  ],
  "current_prices": {
    "usd": "1.0",
    "avalanche:0x": "19.3",
    "avalanche:0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e": "0.999901",
    "base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913": "0.999901",
    "base:0x": "3956.77",
    "arbitrum:0xaf88d065e77c8cc2239327c5edb3a432268e5831": "0.999901",
    "arbitrum:0x": "3956.77",
    "base:0x4200000000000000000000000000000000000006": "3957.08",
    "base:0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b": "1.45",
    "arbitrum:0x7f9fbf9bdd3f4105c478b996b648fe6e828a1e98": "0.416717"
  },
  "price_currency": "usd",
  "state_token": "H4sIAAA...",
  "quoted_at": "2025-10-28T21:27:54.994Z",
  "accept_by": "2025-10-28T21:32:54.994Z",
  "failures": []
}
```

### Confirm and fund the swap

Once the user selects a quote, the swap needs to be confirmed with the API before the quote expires.

Using the `payment_id` and `state_token` returned in the quote, a request is made to confirm the quote and specify the owner and destination. The owner and destination wallet addresses can be different.

The private key of the owner address is the **sole controller** of the one-time wallet. In the scenario that a recovery is needed, the owner address will need to sign transactions. See [API Recoveries & Errors](/pages/api-error-recovery-withdrawal) for more details.

This request (`POST /payments/confirm`) returns the same response body that the payment status endpoint (`GET /payments`) returns.

If the payment output is >= \$300 USD and the owner address has not been verified, the response will have a status of `UNCONFIRMED` with a `USER_VERIFY` next instruction instead. The user must sign the verification payloads and submit them back to `POST /payments/confirm` as a `ContinueConfirmPaymentRequest` before the payment can proceed. See [Owner verification](/pages/api-status#owner-verification-user_verify) for details.

Within the `next_instruction` response body object, the `type` will be `TRANSFER_IN` string. This means that the `deposit_address` can be funded by anyone with the `deposit_amount` on the source chain. Transferring tokens to this OTW address initiates the swap.

#### Example: Confirm crypto-to-crypto swap

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://v2.prod.halliday.xyz/payments/confirm" \
    -H "Authorization: Bearer pk_HALLIDAY_API_KEY_HERE" \
    -H "Content-Type: application/json" \
    -d '{
      "payment_id": "v4uuid...",
      "state_token": "H4sIAAA...",
      "owner_address": "0xowner...",
      "destination_address": "0xdestination..."
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://v2.prod.halliday.xyz/payments/confirm', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer pk_HALLIDAY_API_KEY_HERE',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      payment_id: 'v4uuid...',
      state_token: 'H4sIAAA...',
      owner_address: '0xowner...',
      destination_address: '0xdestination...'
    })
  });
  const data = await response.json();
  ```

  ```python Python theme={null}
  import urllib.request
  import json

  data = {
      'payment_id': 'v4uuid...',
      'state_token': 'H4sIAAA...',
      'owner_address': '0xowner...',
      'destination_address': '0xdestination...'
  }

  request = urllib.request.Request(
      'https://v2.prod.halliday.xyz/payments/confirm',
      data=json.dumps(data).encode('utf-8'),
      headers={
          'Authorization': 'Bearer pk_HALLIDAY_API_KEY_HERE',
          'Content-Type': 'application/json'
      },
      method='POST'
  )
  response = urllib.request.urlopen(request)
  result = response.read()
  ```

  ```php PHP theme={null}
  <?php
  $data = [
      'payment_id' => 'v4uuid...',
      'state_token' => 'H4sIAAA...',
      'owner_address' => '0xowner...',
      'destination_address' => '0xdestination...'
  ];

  $ch = curl_init('https://v2.prod.halliday.xyz/payments/confirm');
  curl_setopt($ch, CURLOPT_POST, true);
  curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'Authorization: Bearer pk_HALLIDAY_API_KEY_HERE',
      'Content-Type: application/json'
  ]);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $response = curl_exec($ch);
  curl_close($ch);
  ?>
  ```

  ```go Go theme={null}
  package main

  import (
      "net/http"
      "bytes"
      "encoding/json"
      "io"
  )

  func main() {
      data := map[string]string{
          "payment_id": "v4uuid...",
          "state_token": "H4sIAAA...",
          "owner_address": "0xowner...",
          "destination_address": "0xdestination...",
      }
      
      jsonData, _ := json.Marshal(data)
      req, _ := http.NewRequest("POST", "https://v2.prod.halliday.xyz/payments/confirm", bytes.NewBuffer(jsonData))
      req.Header.Set("Authorization", "Bearer pk_HALLIDAY_API_KEY_HERE")
      req.Header.Set("Content-Type", "application/json")
      
      client := &http.Client{}
      resp, _ := client.Do(req)
      defer resp.Body.Close()
      
      body, _ := io.ReadAll(resp.Body)
  }
  ```

  ```java Java theme={null}
  import java.net.HttpURLConnection;
  import java.net.URL;
  import java.io.OutputStream;

  URL url = new URL("https://v2.prod.halliday.xyz/payments/confirm");
  HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  conn.setRequestMethod("POST");
  conn.setRequestProperty("Authorization", "Bearer pk_HALLIDAY_API_KEY_HERE");
  conn.setRequestProperty("Content-Type", "application/json");
  conn.setDoOutput(true);

  String jsonData = "{\"payment_id\":\"v4uuid...\",\"state_token\":\"H4sIAAA...\",\"owner_address\":\"0xowner...\",\"destination_address\":\"0xdestination...\"}";

  try (OutputStream os = conn.getOutputStream()) {
      os.write(jsonData.getBytes());
  }

  int status = conn.getResponseCode();
  ```

  ```ruby Ruby theme={null}
  require 'net/http'
  require 'uri'
  require 'json'

  uri = URI('https://v2.prod.halliday.xyz/payments/confirm')
  request = Net::HTTP::Post.new(uri)
  request['Authorization'] = 'Bearer pk_HALLIDAY_API_KEY_HERE'
  request['Content-Type'] = 'application/json'
  request.body = {
    payment_id: 'v4uuid...',
    state_token: 'H4sIAAA...',
    owner_address: '0xowner...',
    destination_address: '0xdestination...'
  }.to_json

  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
    http.request(request)
  end
  ```
</CodeGroup>

**Response:**

```json theme={null}
{
  "payment_id": "v4uuid...",
  "status": "PENDING",
  "funded": false,
  "created_at": "2025-10-28T21:28:46.034Z",
  "updated_at": "2025-10-28T21:28:46.872Z",
  "initiate_fund_by": "2025-10-28T21:38:45.902Z",
  "quote_request": {
    "request": {
      "kind": "FIXED_INPUT",
      "fixed_input_amount": {
        "asset": "avalanche:0x",
        "amount": "50"
      },
      "output_asset": "base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
    },
    "price_currency": "usd"
  },
  "quoted": {
    "output_amount": {
      "asset": "base:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
      "amount": "963.20128"
    },
    "fees": {
      "total_fees": "1.89407692672",
      "conversion_fees": "1.89407692672",
      "network_fees": "0",
      "business_fees": "0",
      "currency_symbol": "usd"
    },
    "route": [...]
  },
  "fulfilled": {...},
  "current_prices": {...},
  "price_currency": "usd",
  "processing_addresses": [...],
  "owner_address": "0xowner...",
  "destination_address": "0xdestination...",
  "next_instruction": {
    "type": "TRANSFER_IN",
    "payment_id": "v4uuid...",
    "funding_page_url": "https://app.halliday.xyz/funding/_payment_id_goes_here_?token=_token_",
    "deposit_info": [
      {
        "deposit_token": "avalanche:0x",
        "deposit_amount": "50",
        "deposit_address": "0xdeposit...",
        "deposit_chain": "avalanche"
      }
    ]
  }
}
```

The response includes the full payment status with confirmation details, tracking information, and funding status.

## Tracking Payment Status

Poll `GET /payments?payment_id={payment_id}` to get the latest **status** of the payment.

The `payment_id` parameter is from the chosen quote returned from the quotes endpoint and also the confirm endpoint. Note that the response from `POST /payments/confirm` is the same as the response body returned from `GET /payments`.

For more information on the API's payment statuses and depositing to initialize a payment workflow see [Status API](/pages/api-status).

## Error Handling and Recoveries

For information on payment errors, recovering failed payments, and withdrawing assets from an OTW, see [Payment API Errors & Recoveries](/pages/api-error-recovery-withdrawal).

In addition to the option of creating an API integration for recoveries, payments can be recovered on the following page by connecting the payment's **owner address** wallet `https://app.halliday.xyz/funding/${payment_id}`.
