---
title: "Pay a person abroad by SWIFT"
description: "Send a USD SWIFT payout to an individual, and answer a document pause by uploading a document and resuming the transfer."
---
Use this recipe to send a USD SWIFT payout to an individual. If the bank asks for a supporting document, the transfer pauses, and you upload the document and resume the transfer.

**Who it is for.** This recipe is for a US relocation company that pays an individual contractor's USD account in Abuja, where the bank asks for supporting documents.

**Status.** Available to Nigeria (NG) from bank-held USD accounts. Not yet available elsewhere.

## Money example

1. The company's bank-held USD account holds 9,000.00 USD.
2. The company sends 4,800.00 USD to Chiamaka Eze with no document. In this example, the fee on its plan is 25.00 USD.
3. The API returns HTTP 200 with `transfer_status` `paused`. Nothing is paid out yet.
4. The company uploads the service contract and resumes the transfer. The payout goes ahead, and Gravv does not charge the fee again. If the company had done nothing for 72 hours, the transfer would have failed, and Gravv would have returned the amount and the fee.

## Before you start

- Everything in [Pay a supplier abroad by SWIFT](/recipes/bank-payouts/pay-a-supplier-abroad-by-swift#before-you-start).
- The person's home address. On the payee, set `account_owner_type` to `individual`.

## Find out what is required

The paused response tells you what to send. It carries `requirements[].type` `supporting_document` and `expires_at`. The `transfer.requires_information` webhook carries the same details.

A pause can also start **later**, shortly before the payout, if the bank's requirement changed. In that case you get only the webhook. [Paused transfers](/platform/transfers/paused-transfers) explains every field of a pause.

## Steps

### 1. Save the payee

Send the same request as [for a supplier](/recipes/bank-payouts/pay-a-supplier-abroad-by-swift#1-save-the-swift-payee), but for a person and without an intermediary bank:

<CodeGroup>
```bash title="cURL"
curl --request POST \
     --url https://api.gravv.xyz/v1/external-accounts \
     --header 'Api-Key: <API_KEY>' \
     --header 'Content-Type: application/json' \
     --header 'Idempotency-Key: eab06884-22f2-4dd1-92a1-9289d8bd6a71' \
     --data '
{
  "customer_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "payee_type": "swift",
  "currency": "USD",
  "account_name": "Chiamaka Eze",
  "account_number": "2034567891",
  "bic": "ZEIBNGLA",
  "account_owner_type": "individual",
  "internal_account_id": "c56a4180-65aa-42ec-a945-5fd21dec0538",
  "bank_address_line_1": "Plot 84 Ajose Adeogun Street",
  "bank_city": "Lagos",
  "bank_state": "Lagos",
  "bank_postal_code": "101241",
  "bank_country": "NG",
  "address": {
    "address_line1": "14 Aminu Kano Crescent",
    "city": "Abuja",
    "state": "FCT",
    "postal_code": "900288",
    "country": "NG"
  }
}
'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v1/external-accounts", {
  method: "POST",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": "eab06884-22f2-4dd1-92a1-9289d8bd6a71",
  },
  body: JSON.stringify({
    "customer_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "payee_type": "swift",
    "currency": "USD",
    "account_name": "Chiamaka Eze",
    "account_number": "2034567891",
    "bic": "ZEIBNGLA",
    "account_owner_type": "individual",
    "internal_account_id": "c56a4180-65aa-42ec-a945-5fd21dec0538",
    "bank_address_line_1": "Plot 84 Ajose Adeogun Street",
    "bank_city": "Lagos",
    "bank_state": "Lagos",
    "bank_postal_code": "101241",
    "bank_country": "NG",
    "address": {
      "address_line1": "14 Aminu Kano Crescent",
      "city": "Abuja",
      "state": "FCT",
      "postal_code": "900288",
      "country": "NG"
    }
  }),
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.post(
    "https://api.gravv.xyz/v1/external-accounts",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
        "Content-Type": "application/json",
        "Idempotency-Key": "eab06884-22f2-4dd1-92a1-9289d8bd6a71",
    },
    json={
        "customer_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
        "payee_type": "swift",
        "currency": "USD",
        "account_name": "Chiamaka Eze",
        "account_number": "2034567891",
        "bic": "ZEIBNGLA",
        "account_owner_type": "individual",
        "internal_account_id": "c56a4180-65aa-42ec-a945-5fd21dec0538",
        "bank_address_line_1": "Plot 84 Ajose Adeogun Street",
        "bank_city": "Lagos",
        "bank_state": "Lagos",
        "bank_postal_code": "101241",
        "bank_country": "NG",
        "address": {
            "address_line1": "14 Aminu Kano Crescent",
            "city": "Abuja",
            "state": "FCT",
            "postal_code": "900288",
            "country": "NG",
        },
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

The response is HTTP 202 with `status` `pending` and the payee `id`, here `a8098c1a-f86e-41c6-8b1e-4f2c3d7e9b10`. Wait for the `external_account.active` webhook.

### 2. Send the payout

<CodeGroup>
```bash title="cURL"
curl --request POST \
     --url https://api.gravv.xyz/v1/transfer \
     --header 'Api-Key: <API_KEY>' \
     --header 'Content-Type: application/json' \
     --header 'Idempotency-Key: edf0bad7-f72f-4936-94fe-2bf05229ce54' \
     --data '
{
  "customer_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "source": {
    "source_type": "internal_account",
    "id": "c56a4180-65aa-42ec-a945-5fd21dec0538"
  },
  "destination": {
    "destination_type": "external_account",
    "id": "a8098c1a-f86e-41c6-8b1e-4f2c3d7e9b10",
    "rail": "swift"
  },
  "amount": "4800.00",
  "description": "Consulting services September 2026",
  "client_reference": "contractor-cz-0926"
}
'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v1/transfer", {
  method: "POST",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": "edf0bad7-f72f-4936-94fe-2bf05229ce54",
  },
  body: JSON.stringify({
    "customer_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "source": {
      "source_type": "internal_account",
      "id": "c56a4180-65aa-42ec-a945-5fd21dec0538"
    },
    "destination": {
      "destination_type": "external_account",
      "id": "a8098c1a-f86e-41c6-8b1e-4f2c3d7e9b10",
      "rail": "swift"
    },
    "amount": "4800.00",
    "description": "Consulting services September 2026",
    "client_reference": "contractor-cz-0926"
  }),
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.post(
    "https://api.gravv.xyz/v1/transfer",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
        "Content-Type": "application/json",
        "Idempotency-Key": "edf0bad7-f72f-4936-94fe-2bf05229ce54",
    },
    json={
        "customer_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
        "source": {
            "source_type": "internal_account",
            "id": "c56a4180-65aa-42ec-a945-5fd21dec0538",
        },
        "destination": {
            "destination_type": "external_account",
            "id": "a8098c1a-f86e-41c6-8b1e-4f2c3d7e9b10",
            "rail": "swift",
        },
        "amount": "4800.00",
        "description": "Consulting services September 2026",
        "client_reference": "contractor-cz-0926",
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

```json title="Response (paused)"
{
  "data": {
    "amount": "4800",
    "amount_currency": "USD",
    "reference": "c1a2b3c4-d5e6-4f70-8a91-b2c3d4e5f607",
    "date_created": "2026-10-13T10:02:11.000Z",
    "transfer_status": "paused",
    "transaction_id": "0e1f2a3b-4c5d-4e6f-8a7b-9c0d1e2f3a4b",
    "local_amount": null,
    "local_currency": null,
    "fee": "25",
    "fee_currency": "USD",
    "requires_approval": false,
    "status": "awaiting_supporting_document",
    "park_reason": "supporting_document",
    "pause_reason": "supporting_document_required",
    "requirements": [
      {
        "type": "supporting_document",
        "description": "resume with supporting_document_ids: at least one supporting document for this payout"
      }
    ],
    "expires_at": "2026-10-16T10:02:11Z"
  },
  "error": null
}
```

Store `reference`. It is the id you use to resume the transfer. [`GET /v1/transactions/0e1f2a3b-4c5d-4e6f-8a7b-9c0d1e2f3a4b`](/api-reference/transactions/get-v1-transactions-transaction-id) shows `status` `paused`. Do not tell your user the payment has gone.

### 3. Upload the document

Upload the document for the customer who sends the transfer. The file can be at most 700 KB. In sandbox, keep it under 500 KB.

<CodeGroup>
```bash title="cURL"
curl --request POST \
     --url https://api.gravv.xyz/v1/transfer/documents \
     --header 'Api-Key: <API_KEY>' \
     --header 'Content-Type: application/json' \
     --header 'Idempotency-Key: 8384f8ff-17ac-45b2-9f82-0ab2878b627f' \
     --data '
{
  "customer_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "document_type": "contract",
  "description": "Consulting contract September 2026",
  "file_name": "contract-cz-2026.pdf",
  "content_type": "application/pdf",
  "content": "JVBERi0xLjQKJcfsj6IKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c+PgplbmRvYmoK"
}
'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v1/transfer/documents", {
  method: "POST",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": "8384f8ff-17ac-45b2-9f82-0ab2878b627f",
  },
  body: JSON.stringify({
    "customer_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "document_type": "contract",
    "description": "Consulting contract September 2026",
    "file_name": "contract-cz-2026.pdf",
    "content_type": "application/pdf",
    "content": "JVBERi0xLjQKJcfsj6IKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c+PgplbmRvYmoK"
  }),
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.post(
    "https://api.gravv.xyz/v1/transfer/documents",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
        "Content-Type": "application/json",
        "Idempotency-Key": "8384f8ff-17ac-45b2-9f82-0ab2878b627f",
    },
    json={
        "customer_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
        "document_type": "contract",
        "description": "Consulting contract September 2026",
        "file_name": "contract-cz-2026.pdf",
        "content_type": "application/pdf",
        "content": "JVBERi0xLjQKJcfsj6IKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c+PgplbmRvYmoK",
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

The response carries the document `id`, here `9f4c2e1a-7b3d-4c5e-8f6a-1b2c3d4e5f60`.

### 4. Resume the transfer

A document pause accepts **only** `supporting_document_ids`. Use a **new `Idempotency-Key` on every attempt**.

<CodeGroup>
```bash title="cURL"
curl --request POST \
     --url https://api.gravv.xyz/v1/transfer/c1a2b3c4-d5e6-4f70-8a91-b2c3d4e5f607/resume \
     --header 'Api-Key: <API_KEY>' \
     --header 'Content-Type: application/json' \
     --header 'Idempotency-Key: 807402b0-ff02-44d3-81a0-a3bfc7c606f8' \
     --data '
{
  "supporting_document_ids": [
    "9f4c2e1a-7b3d-4c5e-8f6a-1b2c3d4e5f60"
  ]
}
'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v1/transfer/c1a2b3c4-d5e6-4f70-8a91-b2c3d4e5f607/resume", {
  method: "POST",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": "807402b0-ff02-44d3-81a0-a3bfc7c606f8",
  },
  body: JSON.stringify({
    "supporting_document_ids": [
      "9f4c2e1a-7b3d-4c5e-8f6a-1b2c3d4e5f60"
    ]
  }),
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.post(
    "https://api.gravv.xyz/v1/transfer/c1a2b3c4-d5e6-4f70-8a91-b2c3d4e5f607/resume",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
        "Content-Type": "application/json",
        "Idempotency-Key": "807402b0-ff02-44d3-81a0-a3bfc7c606f8",
    },
    json={
        "supporting_document_ids": [
            "9f4c2e1a-7b3d-4c5e-8f6a-1b2c3d4e5f60",
        ],
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

```json title="Response"
{
  "data": {
    "transfer_id": "c1a2b3c4-d5e6-4f70-8a91-b2c3d4e5f607",
    "status": "approved_awaiting_execution",
    "transfer_status": "pending",
    "resumed": true
  },
  "error": null
}
```

`resumed: true` means the transfer goes ahead. Follow it as usual. `status` names an internal step, so do not build logic on it.

## What can go wrong

| Response | Why | Fix |
| :-- | :-- | :-- |
| 400 `this transfer is paused for a supporting document: resume it with supporting_document_ids only, without remitter_verification or additional_information` | The resume body has extra fields | Send only `supporting_document_ids` |
| 400 `invalid request body: json: unknown field "documents"` | The resume endpoint rejects unknown fields, unlike the transfer endpoint | Use the exact field name |
| 422 `ERROR_CODE_SUPPORTING_DOCUMENT_REQUIRED` | The resume sent an empty list | Upload the document, then resume with its id |
| 400 `supporting_document_ids: each id must be a document uploaded for this customer and not used by another transfer` | The document belongs to another customer, is already used, or has expired | Upload a new one for the sending customer |
| 400 `this transfer is not paused` | The transfer is already resumed, failed or completed | Read the transaction |
| 400 `content: the file is 812 KB (831488 bytes); the largest accepted is 700 KB (716800 bytes). …` | The file is over 700 KB | Compress it to 700 KB or less |
| 413 `the uploaded file is too large — the maximum is 10MB` | In sandbox, a request of about 600 KB or more, whatever the message says. In live, a file over about 10 MB | Compress it to under 500 KB in sandbox, or to 700 KB or less in live |
| 410 `ERROR_CODE_TRANSFER_PAUSE_EXPIRED` | The pause passed `expires_at`. The transfer failed, and Gravv returned the fee | Send a new transfer with the document attached |
| 410 `ERROR_CODE_TRANSFER_PAUSE_CANCELLED` | Gravv cancelled the pause | Send a new transfer |
| 404 `transfer workflow not found` | The id is the `transaction_id`, or another business's transfer | Resume with `data.reference` |
| 409 `the paused transfer changed while this request ran; please retry` | Two resume requests ran at the same time | Retry once with a new key |
| 400 `insufficient balance` on resume | The balance was spent while the transfer was paused | Fund the account, then resume with a new key |
| The same response as last time | You reused the `Idempotency-Key`. Gravv replays the first response for 24 hours | Use a new key for each attempt |

## Webhooks you will receive

1. `transfer.requires_information`, with `event_data.status` `paused`, `reason` `supporting_document_required` and the requirements in `limit_details.requirements`.
2. After you resume the transfer, `transfer.resumed`, then several `transfer.status.pending` events, then `transfer.status.completed`.
3. If the pause ends without a resume, `transfer.status.failed` with `reason` `supporting_document_expired` (or `supporting_document_cancelled`).

```json title="transfer.requires_information"
{
  "tenant_id": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
  "transaction_id": "0e1f2a3b-4c5d-4e6f-8a7b-9c0d1e2f3a4b",
  "event_category": "transfer",
  "event_type": "transfer.requires_information",
  "event_group_id": "c1a2b3c4-d5e6-4f70-8a91-b2c3d4e5f607",
  "timestamp": "2026-10-13T10:02:11.402Z",
  "event_data": {
    "amount": "4800",
    "associated_transactions": [
      "0e1f2a3b-4c5d-4e6f-8a7b-9c0d1e2f3a4b"
    ],
    "client_reference": "contractor-cz-0926",
    "destination_id": "a8098c1a-f86e-41c6-8b1e-4f2c3d7e9b10",
    "limit_details": {
      "amount_usd": "4800.00",
      "reason": "supporting_document_required",
      "requirements": [
        {
          "description": "resume with supporting_document_ids: at least one supporting document for this payout",
          "type": "supporting_document"
        }
      ]
    },
    "reason": "supporting_document_required",
    "reference": "c1a2b3c4-d5e6-4f70-8a91-b2c3d4e5f607",
    "status": "paused",
    "transfer_status": "awaiting_supporting_document",
    "workflow_type": "swift_transfer"
  },
  "event_id": "6b9d1c2e-3f4a-4b5c-8d6e-7f8091a2b3c4",
  "idempotency_key": "c1a2b3c4-d5e6-4f70-8a91-b2c3d4e5f607:2"
}
```

## Testing in sandbox

You can test the upload and the resume checks (wrong ids, extra fields) in sandbox. A real document pause depends on the bank's setting for the account. You cannot force one with a test value.

## Reference

- [Upload a supporting document](/api-reference/transfer/post-v1-transfer-documents)
- [Resume a paused transfer](/api-reference/transfer/post-v1-transfer-id-resume)
- [Paused transfers](/platform/transfers/paused-transfers)
