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

# Payins

> Move fiat into your client's Portal wallet with Due payins: quote a transfer, fund it, and settle onchain.

## Overview

A Due payin moves fiat into your client's Portal wallet: the client sends a bank transfer,
and Due settles the equivalent crypto onchain. Unlike a single-call payin, Due uses a
quote-then-transfer model: discover a channel, create the crypto recipient, quote the
transfer, create it, then show the client the funding instructions.

<Note>
  Portal proxies these endpoints to Due and returns Due's responses verbatim. Example
  responses below are illustrative; exact fields come from Due and may evolve.
</Note>

For a persistent bank account that auto-converts every deposit (rather than one transfer at
a time), see [Virtual accounts](/integrations/On-Off-Ramp/due-virtual-accounts).

## 1. Prerequisite: approved KYC

Transfers require a provisioned Due customer with approved KYC. Some channels additionally
require endorsements. See [KYC onboarding](/integrations/On-Off-Ramp/due-kyc).

## 2. Discover a channel

`GET /channels` lists the rails and currencies available to the customer. Pass
`onlyAvailable=true` to filter to channels the customer can use today. Pick a fiat source
rail (for example `ach` or `sepa`) and a crypto destination rail and currency (for example
`base-sepolia` and `USDC`).

```bash theme={null}
curl --request GET \
  --url 'https://api.portalhq.io/api/v3/clients/me/integrations/due/channels?onlyAvailable=true' \
  --header 'Authorization: Bearer [token]'
```

## 3. Create the destination recipient

The recipient is where the crypto settles: your client's Portal wallet. Call
`POST /recipients`. Portal maps a CAIP-2 `chainId` to the Due recipient schema and defaults
the address to the client's Portal wallet, so you can omit the address entirely.

```bash theme={null}
curl --request POST \
  --url https://api.portalhq.io/api/v3/clients/me/integrations/due/recipients \
  --header 'Authorization: Bearer [token]' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: [unique-key]' \
  --data '{
    "name": "Jane Doe",
    "chainId": "eip155:8453",
    "details": {}
  }'
```

* Pass a CAIP-2 `chainId` and leave `details.address` unset to default both the schema and
  the address to the client's Portal wallet. Supported namespaces map as `eip155:* -> evm`,
  `solana:* -> solana`, `tron:* -> tron`.
* To send to an address the client does not custody in Portal, set an explicit
  `details.address` and `isExternal: true`. External recipients are never defaulted.
* `details.address` is the chain's native address string: `0x`-hex for EVM, base58 for
  Solana (for example `Bxi9R9ckm4qQZdha85LGY66xyhoo2eaAzfsSpjQJQJ4G`), base58check for Tron.
* The `Idempotency-Key` header is optional and deduplicates retries.

Example response:

```json theme={null}
{
  "data": {
    "id": "rcp_123",
    "isActive": true,
    "isValid": true
  }
}
```

## 4. Quote the transfer

`POST /transfers/quote` prices the transfer. `source` is the fiat side and `destination` is
the crypto side. Set the destination `amount` to `"0"` to let Due compute it from the source
amount.

```bash theme={null}
curl --request POST \
  --url https://api.portalhq.io/api/v3/clients/me/integrations/due/transfers/quote \
  --header 'Authorization: Bearer [token]' \
  --header 'Content-Type: application/json' \
  --data '{
    "source": { "rail": "ach", "currency": "USD", "amount": "100" },
    "destination": { "rail": "base-sepolia", "currency": "USDC", "amount": "0" }
  }'
```

<Note>
  Application fees are enforced by the custodian's Portal Dashboard configuration and injected
  server-side. Any `applicationFeeBps` or `applicationFeeAmount` you send is stripped, so
  clients cannot override them. See [Application fees](/integrations/On-Off-Ramp/due-fees).
</Note>

Example response:

```json theme={null}
{
  "data": {
    "token": "qte_abc",
    "fxRate": 1,
    "fxMarkup": 0,
    "expiresAt": "2026-07-06T18:05:00Z",
    "source": { "rail": "ach", "currency": "USD", "amount": "100", "fee": "0.50", "totalFee": "0.50" },
    "destination": { "rail": "base-sepolia", "currency": "USDC", "amount": "99.50", "fee": "0", "totalFee": "0" }
  }
}
```

## 5. Create the transfer

`POST /transfers` turns a quote into a transfer. Pass the quote `token` and the recipient
`id`.

```bash theme={null}
curl --request POST \
  --url https://api.portalhq.io/api/v3/clients/me/integrations/due/transfers \
  --header 'Authorization: Bearer [token]' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: [unique-key]' \
  --data '{
    "quote": "qte_abc",
    "recipient": "rcp_123"
  }'
```

* `memo` and `purposeCode` are optional and forwarded to Due.
* The `Idempotency-Key` header is optional and deduplicates retries.

Example response:

```json theme={null}
{
  "data": {
    "id": "trf_789",
    "status": "awaiting_funds",
    "expiresAt": "2026-07-06T18:10:00Z"
  }
}
```

## 6. Get funding instructions

While the transfer is `awaiting_funds`, call `POST /transfers/{transferId}/funding-address`
to get the fiat payment details. Show `details` to your client so they can send the bank
transfer.

```bash theme={null}
curl --request POST \
  --url https://api.portalhq.io/api/v3/clients/me/integrations/due/transfers/{transferId}/funding-address \
  --header 'Authorization: Bearer [token]'
```

Example response:

```json theme={null}
{
  "data": {
    "kind": "bank_account",
    "details": {
      "accountHolderName": "Due Payments",
      "accountNumber": "900366164700",
      "routingNumber": "101019644",
      "bankName": "Lead Bank",
      "reference": "trf_789"
    }
  }
}
```

## 7. Track settlement

After the client sends funds, subscribe to [webhooks](/integrations/On-Off-Ramp/due-webhooks)
to track settlement. The status progresses `awaiting_funds` -> `funds_received` -> `approved`
-> `payment_submitted` -> `payment_processed`.

`GET /transfers/{transferId}` returns a transfer's current state whenever you need to read it
directly.

```bash theme={null}
curl --request GET \
  --url https://api.portalhq.io/api/v3/clients/me/integrations/due/transfers/{transferId} \
  --header 'Authorization: Bearer [token]'
```

<Tip>
  Acknowledge webhook deliveries quickly with a `2XX`, then process events asynchronously.
</Tip>

## Next steps

* [Payouts](/integrations/On-Off-Ramp/due-payouts)
* [Virtual accounts](/integrations/On-Off-Ramp/due-virtual-accounts)
* [Webhooks](/integrations/On-Off-Ramp/due-webhooks)
