Link a Debit Card

This document describes how to integrate the Debit Card On-Ramp feature via the OwlPay Harbor API, allowing your end users to purchase cryptocurrency directly using a linked debit card.

Prerequisites

  1. Your Application has the DEBIT_CARD payment method enabled
  2. The Customer has completed KYC verification (status: VERIFIED)
  3. The Customer has completed bank compliance onboarding (CRB Bank Onboarding status: ONBOARDED)

Flow Overview

1. Bind Card       → User completes card binding via the binding page
2. List Cards      → Retrieve the list of linked cards
3. Get Quote       → Get a DEBIT_CARD quote
4. Create Transfer → Submit a transfer with the card ID
5. Email Notification → OwlPay sends a notification email to the cardholder

Important: When a Debit Card transfer is created, OwlPay will send a notification email to the cardholder informing them of the upcoming charge. Please ensure the user is aware of this process.


Step 1: Bind Card

Generate a card binding URL for the customer and redirect them to complete card authorization and binding.

Request

curl --location --request POST 'https://harbor-sandbox.owlpay.com/api/v1/customers/{{CUSTOMER_UUID}}/cards' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-API-KEY: {{API_KEY}}' \
--data-raw '{
    "redirect_url": {
        "success": "https://yourapp.com/card/success",
        "failed": "https://yourapp.com/card/failed"
    }
}'
ParameterTypeRequiredDescription
redirect_url.successstringNoRedirect URL after successful binding. Supports web links or app deep links
redirect_url.failedstringNoRedirect URL on binding failure. Supports web links or app deep links

Response (201)

{
    "data": {
        "uuid": "card_abc123",
        "object": "customer_card",
        "customer_uuid": "cus_1234567890",
        "status": "pending",
        "last4_digits": null,
        "card_company": null,
        "pull_enabled": false,
        "push_enabled": false,
        "first_name": null,
        "last_name": null,
        "country_code": null,
        "issuer_country_code": null,
        "enabled": false,
        "restricted": false,
        "url": "https://card-binding.example.com/bind/card_abc123",
        "created_at": "2026-03-21T10:00:00+00:00",
        "updated_at": "2026-03-21T10:00:00+00:00"
    }
}

Integration

Present the returned url to the user via redirect or iframe. After the user completes card information entry on the binding page, the card status will change from pending to active, with enabled set to true, making it ready for transactions.

Note: When status is pending, the returned url is the card binding page link. Once the card is activated, url becomes null.


Step 2: List Cards

After the user completes card binding, call this API to retrieve the list of linked cards. The system automatically syncs the latest card status from upstream.

Request

curl --location --request GET 'https://harbor-sandbox.owlpay.com/api/v1/customers/{{CUSTOMER_UUID}}/cards' \
--header 'Accept: application/json' \
--header 'X-API-KEY: {{API_KEY}}'

Response (200)

{
    "data": [
        {
            "uuid": "card_abc123",
            "object": "customer_card",
            "customer_uuid": "cus_1234567890",
            "status": "active",
            "last4_digits": "4242",
            "card_company": "Visa",
            "pull_enabled": true,
            "push_enabled": false,
            "first_name": "JOHN",
            "last_name": "DOE",
            "country_code": "US",
            "issuer_country_code": "US",
            "enabled": true,
            "restricted": false,
            "url": null,
            "created_at": "2026-03-21T10:00:00+00:00",
            "updated_at": "2026-03-21T10:05:00+00:00"
        }
    ]
}
FieldDescription
uuidUnique card identifier, prefixed with card_. This ID is required in Step 4 when creating a transfer
statusCard status: pending, active, expired, disabled, restricted
last4_digitsLast 4 digits of the card number
card_companyCard network (e.g., Visa, Mastercard)
pull_enabledWhether the card supports pull (debit) transactions
enabledWhether the card is enabled for transactions. Only cards with true can be used
restrictedWhether the card is restricted. If true, the card cannot be used

Only cards with status: active, enabled: true, and restricted: false can be used to initiate transactions.


Step 3: Get Debit Card Quote

Before initiating a transfer, get a quote to confirm the exchange rate, fees, and settlement amount.

Request

curl --location --request POST 'https://harbor-sandbox.owlpay.com/api/v2/transfers/quotes' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-API-KEY: {{API_KEY}}' \
--data-raw '{
    "on_behalf_of": "{{CUSTOMER_UUID}}",
    "source_amount": "100.00",
    "source_currency": "USD",
    "destination_currency": "USDC"
}'

Response (200)

The response includes quotes for multiple payment methods. Select the item where payment_method is DEBIT_CARD.

[
    {
        "id": "quote_f03AirZ3ZY...KBtFQfZtPDYv",
        "payment_method": "DEBIT_CARD",
        "source_amount": "100.00",
        "source_currency": "USD",
        "destination_amount": "99.50",
        "destination_currency": "USDC",
        "fee": "0.50",
        "quote_expire_date": "2026-03-21T09:30:00Z",
        "customer_limits": {
            "per_transaction_limit": "500.00",
            "daily_limit": "5000.00",
            "weekly_limit": "20000.00",
            "monthly_limit": "100000.00",
            "daily_spent": "0.00",
            "daily_remaining": "5000.00",
            "weekly_spent": "0.00",
            "weekly_remaining": "20000.00",
            "monthly_spent": "0.00",
            "monthly_remaining": "100000.00",
            "current_remaining": "500.00"
        }
    }
]

Save the id (i.e., quote_id) from the DEBIT_CARD quote — it is required in the next step to create the transfer. When on_behalf_of is provided, the response includes customer_limits which can be used to display the user's transaction limits on the frontend.


Step 4: Create Debit Card Transfer

Use the card_ ID from Step 2 and the quote_id from Step 3 to create the transfer.

Request

curl --location --request POST 'https://harbor-sandbox.owlpay.com/api/v2/transfers' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-API-KEY: {{API_KEY}}' \
--header 'Idempotency-Key: {{Idempotency-Key}}' \
--data-raw '{
    "on_behalf_of": "{{CUSTOMER_UUID}}",
    "quote_id": "{{QUOTE_ID}}",
    "application_transfer_uuid": "{{YOUR_ORDER_ID}}",
    "source_linked_card_id": "card_abc123",
    "destination": {
        "beneficiary_info": {
            "...": "Fill according to quote requirements"
        },
        "payout_instrument": {
            "address": "0x1234567890abcdef1234567890abcdef12345678"
        },
        "transfer_purpose": "SALARY",
        "is_self_transfer": true
    }
}'
ParameterTypeRequiredDescription
on_behalf_ofstringYesCustomer UUID
quote_idstringYesQuote ID obtained in Step 3
application_transfer_uuidstringYesYour order ID (used for idempotency)
source_linked_card_idstringYesCard ID from Step 2 (prefixed with card_)
destinationobjectYesDestination information (payout address, beneficiary details, etc.)

Response (201 Created)

On success, returns the full Transfer object with an initial status of PENDING_CUSTOMER_TRANSFER_START.

Error Scenarios

HTTP StatusReason
422Card does not exist or does not belong to this Application
422Card status is not active
422Card is not enabled (enabled is false)
422Card is restricted (restricted is true)
422Card has been blocklisted
422Transaction limit exceeded (per-transaction / daily / weekly / monthly)
404Customer not found or does not belong to this Application

Email Notification

When a Debit Card transfer is successfully created, OwlPay will automatically send a notification email to the cardholder containing:

  • Charge amount and currency
  • Transaction details

This email ensures the cardholder is aware of the upcoming charge, in compliance with regulatory requirements. No additional action is needed from the Application side for this notification.


Complete Flow Diagram


Transfer Status Flow

StatusDescription
PENDING_CUSTOMER_TRANSFER_STARTTransfer created, awaiting processing
PENDING_HARBORBeing processed
COMPLETEDTransfer complete, funds settled
REJECTTransfer rejected
ON_HOLDTransfer temporarily frozen (may require additional review)

We recommend listening for transfer status changes via Webhooks to keep your system updated in real time.