<!-- ZenPays documentation · https://docs.zenpayz.com/docs/rest-api/endpoints/ramp/sell-payout -->

# Submit Sell Payout (Phase 2b)

Phase 2 of the two-phase off-ramp (sell) flow. Submit the customer's beneficiary details to trigger the fiat payout. The system obtains an FX quote (if cross-currency), maps the beneficiary fields to the payout provider, and initiates the bank transfer.

On success, the ramp intent automatically transitions to `completed`.

<EndpointHeader verb="POST" path="/payment/api/v1/off-ramp/sell/payout" />

## Request

### Headers

<ParamTable
  label="Header"
  rows={[
    { name: "Content-Type", required: true, desc: "`application/json`" },
    { name: "x-request-id", desc: "Custom request ID for tracing" },
  ]}
/>

:::note Public Endpoint
This endpoint does not require authentication headers.
:::

### Body Parameters

<ParamTable
  rows={[
    { name: "intentId", type: "string", required: true, desc: "Ramp intent ID" },
    { name: "cryptoDepositId", type: "string", required: true, desc: "Deposit ID from [Phase 1](/docs/rest-api/endpoints/ramp/sell-deposit) response" },
    { name: "fiatCurrency", type: "string", required: true, desc: "Target fiat currency (e.g. `USD`)" },
    { name: "payoutType", type: "string", desc: "Payout method (default: `bank_transfer`)" },
    { name: "country", type: "string", desc: "ISO country code (default: `US`)" },
    { name: "selectedTsp", type: "string", desc: "Payout provider from [Payout Preview](/docs/rest-api/endpoints/ramp/payout-preview) response (e.g. `zenpay_routing`). When omitted, the system selects the best provider automatically." },
    { name: "beneficiaryDetails", type: "object", required: true, desc: "Dynamic fields from [Payout Preview](/docs/rest-api/endpoints/ramp/payout-preview)" },
  ]}
/>

### beneficiaryDetails Object

The fields in this object are **dynamic** — they come from the `requiredFields` and `optionalFields` returned by the [Payout Preview](/docs/rest-api/endpoints/ramp/payout-preview) endpoint. Always call payout-preview first to discover which fields are needed for the target currency and country.

**Common fields include:**

<ParamTable
  rows={[
    { name: "receiverFirstName", type: "string", desc: "Beneficiary first name" },
    { name: "receiverLastName", type: "string", desc: "Beneficiary last name" },
    { name: "receiverAccountNumber", type: "string", desc: "Bank account number" },
    { name: "receiverBankName", type: "string", desc: "Bank name" },
    { name: "receiverBankCode", type: "string", desc: "SWIFT/BIC code" },
    { name: "receiverCountry", type: "string", desc: "ISO country code" },
    { name: "receiverAddressLine1", type: "string", desc: "Street address" },
    { name: "receiverCity", type: "string", desc: "City" },
    { name: "receiverState", type: "string", desc: "State/province" },
    { name: "receiverPinCode", type: "string", desc: "Postal/ZIP code" },
    { name: "receiverEmail", type: "string", desc: "Email address" },
    { name: "receiverPhone", type: "string", desc: "Phone number" },
    { name: "remittancePurpose", type: "string", desc: "Purpose of remittance (e.g. `PAYP001 - Family Support`)" },
    { name: "sourceOfFund", type: "string", desc: "Source of funds (e.g. `PAYF001 - Salary`)" },
    { name: "relationship", type: "string", desc: "Relationship to sender (e.g. `PAYR001 - Self`)" },
  ]}
/>

:::caution Field Requirements Vary
The required fields change based on the destination currency, country, and payout type. **Always use the payout-preview response** to determine which fields to collect. Do not hard-code field assumptions.
:::

## Response

### Success (200 OK)

```json
{
  "success": true,
  "data": {
    "payoutId": "po_abc123def456",
    "status": "processing",
    "amount": 19.78,
    "currency": "USD",
    "intentId": "ri_1710345678000_a1b2c3d4e5f6g7h8"
  },
  "message": "Sell payout submitted"
}
```

### Response Fields

<ParamTable
  rows={[
    { name: "payoutId", type: "string", desc: "Payout tracking ID" },
    { name: "status", type: "string", desc: "Payout status (`processing`)" },
    { name: "amount", type: "number", desc: "Fiat payout amount" },
    { name: "currency", type: "string", desc: "Fiat currency" },
    { name: "intentId", type: "string", desc: "The ramp intent ID" },
  ]}
/>

### Automatic Behaviors

| Condition | Action |
|-----------|--------|
| Received crypto matches expected (±5%) | Uses locked fiat amount from quote |
| Received crypto differs >5% from expected | Recalculates fiat amount proportionally with fee scaling |
| Same-currency payout (e.g. USD → USD) | Skips FX quotation step |
| Cross-currency payout | Obtains live FX quote from provider |
| Payout succeeds | Intent auto-transitions to `completed` |
| Payout fails | Intent receives `payout_failed` metadata |

### Error Responses

<ErrorTable
  rows={[
    { code: "BAD_REQUEST", status: "400", message: "Deposit not confirmed, invalid amount, or FX quotation failed" },
    { code: "NOT_FOUND", status: "404", message: "Intent or deposit not found" },
    { code: "SELL_PAYOUT_FAILED", status: "500", message: "Payout submission failed" },
  ]}
/>

<CodeRail>

## Examples

<Tabs groupId="language">
  <TabItem value="curl" label="cURL" default>

```bash
curl -X POST https://api.zenpayz.com/payment/api/v1/off-ramp/sell/payout \
  -H "Content-Type: application/json" \
  -d '{
    "intentId": "ri_1710345678000_a1b2c3d4e5f6g7h8",
    "cryptoDepositId": "d4e5f6a7-b8c9-1234-5678-abcdef012345",
    "fiatCurrency": "USD",
    "country": "US",
    "selectedTsp": "zenpay_routing",
    "beneficiaryDetails": {
      "receiverFirstName": "John",
      "receiverLastName": "Doe",
      "receiverCountry": "US",
      "receiverAccountNumber": "123456789",
      "receiverBankName": "Bank of America",
      "receiverBankCode": "BOFAUS3N",
      "receiverAddressLine1": "123 Main St",
      "receiverCity": "New York",
      "receiverState": "NY",
      "receiverPinCode": "10001",
      "remittancePurpose": "PAYP001 - Family Support",
      "sourceOfFund": "PAYF001 - Salary",
      "relationship": "PAYR001 - Self"
    }
  }'
```

  </TabItem>
  <TabItem value="javascript" label="JavaScript">

```javascript
const response = await fetch(
  'https://api.zenpayz.com/payment/api/v1/off-ramp/sell/payout',
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      intentId: 'ri_1710345678000_a1b2c3d4e5f6g7h8',
      cryptoDepositId: 'd4e5f6a7-b8c9-1234-5678-abcdef012345',
      fiatCurrency: 'USD',
      country: 'US',
      selectedTsp: 'zenpay_routing',
      beneficiaryDetails: {
        receiverFirstName: 'John',
        receiverLastName: 'Doe',
        receiverCountry: 'US',
        receiverAccountNumber: '123456789',
        receiverBankName: 'Bank of America',
        receiverBankCode: 'BOFAUS3N',
        receiverAddressLine1: '123 Main St',
        receiverCity: 'New York',
        receiverState: 'NY',
        receiverPinCode: '10001',
        remittancePurpose: 'PAYP001 - Family Support',
        sourceOfFund: 'PAYF001 - Salary',
        relationship: 'PAYR001 - Self',
      },
    }),
  }
);

const { data } = await response.json();

console.log(`Payout ID: ${data.payoutId}`);
console.log(`Status: ${data.status}`);
console.log(`Amount: ${data.amount} ${data.currency}`);

// The intent is now completed — redirect user
window.location.href = '/success';
```

  </TabItem>
  <TabItem value="python" label="Python">

```python
import requests

response = requests.post(
    "https://api.zenpayz.com/payment/api/v1/off-ramp/sell/payout",
    json={
        "intentId": "ri_1710345678000_a1b2c3d4e5f6g7h8",
        "cryptoDepositId": "d4e5f6a7-b8c9-1234-5678-abcdef012345",
        "fiatCurrency": "USD",
        "country": "US",
        "selectedTsp": "zenpay_routing",
        "beneficiaryDetails": {
            "receiverFirstName": "John",
            "receiverLastName": "Doe",
            "receiverCountry": "US",
            "receiverAccountNumber": "123456789",
            "receiverBankName": "Bank of America",
            "receiverBankCode": "BOFAUS3N",
            "receiverAddressLine1": "123 Main St",
            "receiverCity": "New York",
            "receiverState": "NY",
            "receiverPinCode": "10001",
            "remittancePurpose": "PAYP001 - Family Support",
            "sourceOfFund": "PAYF001 - Salary",
            "relationship": "PAYR001 - Self",
        },
    },
)

data = response.json()["data"]
print(f"Payout ID: {data['payoutId']}")
print(f"Status: {data['status']}")
print(f"Amount: {data['amount']} {data['currency']}")
```

  </TabItem>
</Tabs>

</CodeRail>

## Complete Two-Phase Flow

```
Phase 1: Deposit                    Phase 2: Payout
─────────────────                   ──────────────────────
1. POST sell/checkout               4. GET  payout-preview
   → Get deposit address               → Get required fields

2. Customer sends crypto            5. Render beneficiary form

3. Wait for confirmation            6. POST sell/payout
   (deposit → confirmed)               → Trigger fiat transfer
                                        → Intent → completed
```

## Next Steps

- [Get Payout Preview](/docs/rest-api/endpoints/ramp/payout-preview) — Discover required fields (Phase 2a)
- [Create Sell Deposit](/docs/rest-api/endpoints/ramp/sell-deposit) — Generate deposit address (Phase 1)
- [Transaction Status](/docs/rest-api/endpoints/ramp/transaction-status) — Poll transaction progress
- [Off-Ramp Flow](/docs/examples/off-ramp-flow) — Complete sell integration guide
