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

# POST /api/v1/payouts/sendPayoutRequest

> Create a transaction. Quote, limits check, and creation happen in one call.

```
POST /api/v1/payouts/sendPayoutRequest
```

## Headers

| Header          | Required             |
| --------------- | -------------------- |
| `x-api-key`     | yes                  |
| `x-timestamp`   | yes                  |
| `x-signature`   | yes                  |
| `authorization` | yes — `Bearer <JWT>` |
| `content-type`  | yes                  |

## Request body

```json theme={null}
{ "data": "<encrypted blob of the payload below>" }
```

Decrypted payload — top-level fields:

| Field                | Type    | Required | Notes                                                                                      |
| -------------------- | ------- | -------- | ------------------------------------------------------------------------------------------ |
| `idempotency_key`    | UUID v4 | yes      | Reusing the same key returns the original `transaction_id` instead of creating a duplicate |
| `country_code`       | string  | yes      | ISO 3166-1 alpha-2 (e.g. `IN`)                                                             |
| `currency_code`      | string  | yes      | Destination currency (e.g. `INR`)                                                          |
| `payment_method`     | string  | yes      | e.g. `imps`, `rtgs`, `neft`, etc. See [Recipient details](#recipient-details)              |
| `payment_network`    | string  | no       | Optional override for which payment network to route through                               |
| `source_amount`      | string  | yes      | Decimal as a string, e.g. `"125.00"`                                                       |
| `source_currency`    | string  | no       | Defaults to `USDT`                                                                         |
| `recipient_details`  | object  | yes      | Always requires `name`. Additional fields depend on `payment_method` — see below           |
| `purpose_of_payment` | string  | no       | Free text                                                                                  |
| `source_of_funds`    | string  | no       | Free text                                                                                  |

## Example request

<Info>
  All examples assume you've already encrypted the body and signed the request — see the [Quickstart](/quickstart) for the full helper in Node and Python.
</Info>

<CodeGroup>
  ```js Node theme={null}
  import { randomUUID } from 'node:crypto'

  const payout = await call('/api/v1/payouts/sendPayoutRequest', {
    idempotency_key: randomUUID(),
    country_code: 'IN',
    currency_code: 'INR',
    payment_method: 'imps',
    source_amount: '10.00',
    source_currency: 'USDT',
    recipient_details: {
      name: 'Jane Doe',
      account_number: '123456789012',
      ifsc: 'BANK0000001',
      customer_email: 'recipient@example.com',
      customer_phone: '+919999999999',
    },
  }, jwt)
  ```

  ```python Python theme={null}
  import uuid

  status, body = call('/api/v1/payouts/sendPayoutRequest', {
      'idempotency_key': str(uuid.uuid4()),
      'country_code': 'IN',
      'currency_code': 'INR',
      'payment_method': 'imps',
      'source_amount': '10.00',
      'source_currency': 'USDT',
      'recipient_details': {
          'name': 'Jane Doe',
          'account_number': '123456789012',
          'ifsc': 'BANK0000001',
          'customer_email': 'recipient@example.com',
          'customer_phone': '+919999999999',
      },
  }, jwt)
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.pontisglobe.com/api/v1/payouts/sendPayoutRequest" \
    -H "content-type: application/json" \
    -H "x-api-key: YOUR_API_KEY" \
    -H "x-timestamp: 1748023400" \
    -H "x-signature: 2f8a9b…" \
    -H "authorization: Bearer eyJhbGciOi…" \
    -d '{"data":"<encrypted blob>"}'
  ```

  ```go Go theme={null}
  req, _ := http.NewRequest("POST",
    "https://api.pontisglobe.com/api/v1/payouts/sendPayoutRequest",
    strings.NewReader(`{"data":"`+encryptedBody+`"}`))
  req.Header.Set("content-type", "application/json")
  req.Header.Set("x-api-key", apiKey)
  req.Header.Set("x-timestamp", timestamp)
  req.Header.Set("x-signature", signature)
  req.Header.Set("authorization", "Bearer "+jwt)
  res, _ := http.DefaultClient.Do(req)
  ```

  ```java Java theme={null}
  HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://api.pontisglobe.com/api/v1/payouts/sendPayoutRequest"))
    .header("content-type", "application/json")
    .header("x-api-key", apiKey)
    .header("x-timestamp", timestamp)
    .header("x-signature", signature)
    .header("authorization", "Bearer " + jwt)
    .POST(HttpRequest.BodyPublishers.ofString("{\"data\":\"" + encryptedBody + "\"}"))
    .build();
  HttpResponse<String> res = HttpClient.newHttpClient().send(req, BodyHandlers.ofString());
  ```

  ```kotlin Kotlin theme={null}
  val req = Request.Builder()
    .url("https://api.pontisglobe.com/api/v1/payouts/sendPayoutRequest")
    .post("""{"data":"$encryptedBody"}""".toRequestBody("application/json".toMediaType()))
    .addHeader("x-api-key", apiKey)
    .addHeader("x-timestamp", timestamp)
    .addHeader("x-signature", signature)
    .addHeader("authorization", "Bearer $jwt")
    .build()
  val res = OkHttpClient().newCall(req).execute()
  ```

  ```php PHP theme={null}
  $ch = curl_init('https://api.pontisglobe.com/api/v1/payouts/sendPayoutRequest');
  curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
      'content-type: application/json',
      "x-api-key: $apiKey",
      "x-timestamp: $timestamp",
      "x-signature: $signature",
      "authorization: Bearer $jwt",
    ],
    CURLOPT_POSTFIELDS => json_encode(['data' => $encryptedBody]),
    CURLOPT_RETURNTRANSFER => true,
  ]);
  $res = curl_exec($ch);
  ```

  ```ruby Ruby theme={null}
  uri = URI('https://api.pontisglobe.com/api/v1/payouts/sendPayoutRequest')
  req = Net::HTTP::Post.new(uri, {
    'content-type' => 'application/json',
    'x-api-key' => api_key,
    'x-timestamp' => timestamp,
    'x-signature' => signature,
    'authorization' => "Bearer #{jwt}",
  })
  req.body = { data: encrypted_body }.to_json
  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
  ```

  ```csharp C# theme={null}
  var req = new HttpRequestMessage(HttpMethod.Post,
    "https://api.pontisglobe.com/api/v1/payouts/sendPayoutRequest");
  req.Headers.Add("x-api-key", apiKey);
  req.Headers.Add("x-timestamp", timestamp);
  req.Headers.Add("x-signature", signature);
  req.Headers.Add("authorization", $"Bearer {jwt}");
  req.Content = new StringContent($"{{\"data\":\"{encryptedBody}\"}}",
    Encoding.UTF8, "application/json");
  var res = await new HttpClient().SendAsync(req);
  ```

  ```swift Swift theme={null}
  var req = URLRequest(url: URL(string: "https://api.pontisglobe.com/api/v1/payouts/sendPayoutRequest")!)
  req.httpMethod = "POST"
  req.setValue("application/json", forHTTPHeaderField: "content-type")
  req.setValue(apiKey, forHTTPHeaderField: "x-api-key")
  req.setValue(timestamp, forHTTPHeaderField: "x-timestamp")
  req.setValue(signature, forHTTPHeaderField: "x-signature")
  req.setValue("Bearer \(jwt)", forHTTPHeaderField: "authorization")
  req.httpBody = #"{"data":"\#(encryptedBody)"}"#.data(using: .utf8)
  let (data, _) = try await URLSession.shared.data(for: req)
  ```
</CodeGroup>

## Recipient details

The shape of `recipient_details` depends on `payment_method`. Every method
requires `name` (the recipient's full name, max 200 chars). Additional fields
per method are listed below.

### `imps` / `rtgs` / `neft` — India bank transfer (INR)

For `payment_method: "imps"`, send the following inside `recipient_details`.
The API picks the underlying rail (IMPS, RTGS, or NEFT) automatically based on
the amount and bank availability.

| Field            | Type             | Required | Notes                                                               |
| ---------------- | ---------------- | -------- | ------------------------------------------------------------------- |
| `name`           | string (max 200) | yes      | Recipient's full name as on the bank account                        |
| `account_number` | string           | yes      | 9–18 digits                                                         |
| `ifsc`           | string           | yes      | 11-char IFSC — uppercase; format `AAAA0BBBBBB` (e.g. `BANK0000001`) |
| `customer_email` | string           | yes      | Valid email address                                                 |
| `customer_phone` | string           | yes      | E.164 format (e.g. `+919999999999`)                                 |
| `bank_name`      | string           | no       | Free text, max 120 chars                                            |

Decrypted request body:

```json theme={null}
{
  "idempotency_key": "f0e9d8c7-b6a5-4321-9876-0123456789ab",
  "country_code": "IN",
  "currency_code": "INR",
  "payment_method": "imps",
  "source_amount": "100.00",
  "source_currency": "USDT",
  "recipient_details": {
    "name": "Jane Doe",
    "account_number": "123456789012",
    "ifsc": "BANK0000001",
    "customer_email": "recipient@example.com",
    "customer_phone": "+919999999999"
  }
}
```

If any required field is missing or malformed, the API responds with HTTP 400 and a
structured `validation_error` pointing at the offending field — for example:

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "validation_error",
    "message": "'recipient_details.customer_email' is required"
  }
}
```

## Responses

### 201 — Success

```json theme={null}
{
  "ok": true,
  "data": {
    "transaction_id": "029b2038-6166-4bea-80a9-f1a2425a85eb",
    "status": "pending"
  }
}
```

`status` is one of:

* `pending` — accepted and queued for processing
* `pending_approval` — exceeds your auto-approval limit; awaiting manual approval

### 400 — validation\_error

Schema mismatch — fix and retry.

### 401 — unauthorized / jwt\_expired

Bad/missing JWT — re-login.

### 403 — forbidden

KYC required or account suspended.

### 404 — no\_route

The requested country/currency/payment method combination is not available. Contact support to enable it.

### 422 — business rule failures

```json theme={null}
{
  "ok": false,
  "request_id": "01J9F5Z4X7Y3K2B1Q0M8N7V6T5",
  "error": { "code": "insufficient_funds", "message": "Insufficient available balance. Available: 5.00 USDT" }
}
```

| Code                     | When                                                                  |
| ------------------------ | --------------------------------------------------------------------- |
| `insufficient_funds`     | Wallet balance is too low                                             |
| `daily_limit_exceeded`   | Today's volume would exceed your daily cap                            |
| `monthly_limit_exceeded` | This month's volume would exceed your monthly cap                     |
| `fee_exceeds_amount`     | The fee exceeds the send amount — try a larger amount                 |
| `rate_moved`             | Exchange rate moved more than 0.5% since the quote was issued — retry |
| `screening_failed`       | Recipient failed sanctions screening                                  |

### 429 — rate\_limited

Tighten your client-side throttle.

Full code list in [Errors](/errors).

## Sandbox tip

In sandbox, the trailing cents of `source_amount` decide the eventual status — see [Sandbox](/sandbox).
