---
title: "Get a stuck remittance moving"
sidebarTitle: "Unstick a remittance"
description: "Tell which of five situations a paused, failed or pending payout is in, and take the one action that fixes each."
---
Use this recipe to find out which of five situations a paused, failed or pending payout is in, and take the one action that fixes it.

**Who it is for.** Any developer whose payout returned `paused`, sent a `failed` webhook, or sits in `pending`.

**Status.** Available.

## Money example

1. Ada sends 3,000.00 USD to her brother in Lagos.
2. Her daily limit is 5,000.00 USD and she has already sent 3,750.00 USD today, so 1,250.00 USD is left.
3. Gravv accepts the transfer but returns it `paused`, with `breached_window` `daily` and `remaining_usd` `1250.00`. Gravv pays nothing out.
4. You either resume it with proof of a higher verification level, or let it expire and send 1,250.00 USD now and the rest tomorrow.

## Before you start

Store the `reference` and `transaction_id` of every transfer. Every check below starts from them.

## Find out what is required

1. **The sender's limits.** [`GET /v1/customers/{customer_id}/limits`](/api-reference/limits/get-v1-customers-customer-id-limits) shows the caps and what is left, in USD (see [Transfer limits](/platform/transfers/transfer-limits)). A 404 `not found: no limits policy applies` means your business has no limits policy.
2. **Senders you verify yourself.** [`GET /v1/limits/remitters/{remitter_id}`](/api-reference/limits/get-v1-limits-remitters-remitter-id) returns the same shape.
3. **The pause itself.** A paused transfer lists what it needs in `requirements[]`.

## Steps

Find your situation below and follow it.

### 1. Paused for a limit

The response has `pause_reason` set to `per_transfer`, `daily`, `weekly`, `monthly` or `annual`:

```json title="Response (paused)"
{
  "data": {
    "amount": "3000",
    "amount_currency": "USD",
    "reference": "7d3f5b9e-2a4c-4e6f-8b0d-1c3e5a7b9d42",
    "date_created": "2026-10-13T16:01:00.000Z",
    "transfer_status": "paused",
    "transaction_id": "4e6a8c0e-2f4b-4d6f-9a1c-3e5b7d9f1a53",
    "fee": "1.5",
    "fee_currency": "USD",
    "requires_approval": false,
    "status": "awaiting_remitter_information",
    "park_reason": "remitter_information",
    "pause_reason": "daily",
    "requirements": [
      {
        "type": "remitter_verification",
        "tier": "T2",
        "levels": ["enhanced"],
        "description": "resume with remitter_verification at one of these levels"
      }
    ],
    "breached_window": "daily",
    "remaining_usd": "1250.00",
    "expires_at": "2026-10-16T16:01:00Z"
  },
  "error": null
}
```

For `remitter_verification`, resume with your verification at one of the listed `levels`, and a new `Idempotency-Key`. Send only checks that you ran.

<CodeGroup>
```bash title="cURL"
curl --request POST \
     --url https://api.gravv.xyz/v1/transfer/7d3f5b9e-2a4c-4e6f-8b0d-1c3e5a7b9d42/resume \
     --header 'Api-Key: <API_KEY>' \
     --header 'Content-Type: application/json' \
     --header 'Idempotency-Key: c775b886-915c-4b84-9973-861a6513bc1d' \
     --data '
{
  "remitter_verification": {
    "remitter_id": "rem_ada_bello_01",
    "level": "enhanced",
    "provider": "in_house_kyc",
    "provider_reference": "chk_88213",
    "verified_at": "2026-10-13T15:30:00Z",
    "screening": {
      "sanctions": "clear",
      "pep": "clear",
      "adverse_media": "clear"
    }
  }
}
'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v1/transfer/7d3f5b9e-2a4c-4e6f-8b0d-1c3e5a7b9d42/resume", {
  method: "POST",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": "c775b886-915c-4b84-9973-861a6513bc1d",
  },
  body: JSON.stringify({
    "remitter_verification": {
      "remitter_id": "rem_ada_bello_01",
      "level": "enhanced",
      "provider": "in_house_kyc",
      "provider_reference": "chk_88213",
      "verified_at": "2026-10-13T15:30:00Z",
      "screening": {
        "sanctions": "clear",
        "pep": "clear",
        "adverse_media": "clear"
      }
    }
  }),
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.post(
    "https://api.gravv.xyz/v1/transfer/7d3f5b9e-2a4c-4e6f-8b0d-1c3e5a7b9d42/resume",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
        "Content-Type": "application/json",
        "Idempotency-Key": "c775b886-915c-4b84-9973-861a6513bc1d",
    },
    json={
        "remitter_verification": {
            "remitter_id": "rem_ada_bello_01",
            "level": "enhanced",
            "provider": "in_house_kyc",
            "provider_reference": "chk_88213",
            "verified_at": "2026-10-13T15:30:00Z",
            "screening": {
                "sanctions": "clear",
                "pep": "clear",
                "adverse_media": "clear",
            },
        },
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

```json title="Response"
{
  "data": {
    "transfer_id": "7d3f5b9e-2a4c-4e6f-8b0d-1c3e5a7b9d42",
    "status": "approved_awaiting_execution",
    "transfer_status": "pending",
    "resumed": true
  },
  "error": null
}
```

For the other requirement types:

- `admin_review`: only Gravv can release the transfer. Contact support with the `reference`, or let it expire and send within `remaining_usd`.
- `screening_clear`: resume with a `remitter_verification` whose `screening` values are all `clear`.

### 2. Paused for sender details

If `pause_reason` is `sender_details_required`, resume with `additional_information`, as in [Send money to Ghana](/recipes/remittance-and-crypto/send-money-to-ghana#4-resume-it).

### 3. Failed

The `transfer.status.failed` webhook arrives, and the transaction status is `failed`. Gravv has refunded the amount and the fee, as a `transfer_refund` row.

1. Read `event_data.reason` if it is set, for example `limit_pause_expired` or `sender_details_expired`.
2. Fix the cause.
3. Send a **new** transfer with a new `Idempotency-Key` and a new `client_reference`.

### 4. Manual review

The `transfer.status.failed` webhook arrives, but the transaction status is still `pending`. The outcome was unclear, so a person checks whether the money left before Gravv refunds anything. **Do not send the transfer again.** You could pay twice. Contact support with the `reference`.

### 5. A long `pending`

The route still reports "processing". Gravv never fails a transfer only because time has passed. If the transfer has been pending for an unusually long time for that country, contact support with the `reference`.

### Missed webhooks

If your endpoint missed a webhook, find the deliveries for the transaction:

<CodeGroup>
```bash title="cURL"
curl --request GET \
     --url 'https://api.gravv.xyz/v1/webhooks/history?transaction_id=4e6a8c0e-2f4b-4d6f-9a1c-3e5b7d9f1a53&event_type=transfer' \
     --header 'Api-Key: <API_KEY>'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v1/webhooks/history?transaction_id=4e6a8c0e-2f4b-4d6f-9a1c-3e5b7d9f1a53&event_type=transfer", {
  method: "GET",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
  },
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.get(
    "https://api.gravv.xyz/v1/webhooks/history?transaction_id=4e6a8c0e-2f4b-4d6f-9a1c-3e5b7d9f1a53&event_type=transfer",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

```json title="Response"
{
  "data": {
    "items": [
      {
        "id": "4e8a2c6f-1b3d-4f5a-9c7e-2d4f6a8c0e1b",
        "transaction_id": "4e6a8c0e-2f4b-4d6f-9a1c-3e5b7d9f1a53",
        "event_type": "transfer",
        "status": "failed",
        "path": "/hooks/gravv",
        "http_method": "POST",
        "response_status_code": 500,
        "created_at": "2026-10-13T09:40:12.004Z",
        "last_attempt": "2026-10-13T10:12:40.551Z"
      }
    ],
    "items_per_page": 50,
    "page": 1,
    "total_items": 1,
    "total_pages": 1
  },
  "error": null
}
```

Here `event_type` is the category (`transfer`). One transfer has one row per event.

- `GET /v1/webhooks/event/{id}` returns the attempts, the payload Gravv sent and your endpoint's response.
- `GET /v1/webhooks/calls/{id}` lists each attempt.

After you fix your endpoint, resend the event:

<CodeGroup>
```bash title="cURL"
curl --request POST \
     --url https://api.gravv.xyz/v1/webhooks/event/send/4e8a2c6f-1b3d-4f5a-9c7e-2d4f6a8c0e1b \
     --header 'Api-Key: <API_KEY>'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v1/webhooks/event/send/4e8a2c6f-1b3d-4f5a-9c7e-2d4f6a8c0e1b", {
  method: "POST",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
  },
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.post(
    "https://api.gravv.xyz/v1/webhooks/event/send/4e8a2c6f-1b3d-4f5a-9c7e-2d4f6a8c0e1b",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

Gravv does **not sign** the resend. An endpoint that requires the signature rejects it, so read the transaction to confirm its status.

## What can go wrong

| Response | Why | Fix |
| :-- | :-- | :-- |
| 200 `resumed: false` with new `requirements` | The transfer is still over the limit, or the level is not accepted | Send what `requirements` lists, or wait for the limit window to reset |
| 400 `the remitter information does not match the sender this transfer was paused for; resume with the same remitter_id, or initiate a new transfer` | You sent a different `remitter_id` | Use the same `remitter_id` |
| 400 `remitter_verification.remitter_id is required` | A verification without an id | Add `remitter_id` |
| 422 `transfers are not permitted for this sender` | The sender is blocked | Contact support. Do not retry |
| 503 `transfer limits are temporarily unavailable, please retry` | Gravv cannot check limits right now | Retry |
| 409 `the paused transfer changed while this request ran; please retry` | Two resume requests ran at once | Retry once with a new idempotency key |
| 404 `transfer workflow not found` | You used the `transaction_id` as the id | Use the `reference` |
| 400 `this transfer is not paused`, with a top-level `transfer_status` | The transfer was already resumed, was never paused, or has finished | Read the transaction |
| 410 `ERROR_CODE_TRANSFER_PAUSE_EXPIRED` or `ERROR_CODE_TRANSFER_PAUSE_CANCELLED` | The pause has ended | Send a new transfer |
| 409 `client_reference has already been used for a different transfer …` | Your limits policy is enforced, and you reused a `client_reference` with a different body | Use a new `client_reference` |
| 409 `a transfer with this client_reference is still being processed; retry shortly`, with `Retry-After: 1` | You retried too soon | Wait one second and retry |
| 409 `idempotency_in_flight` or 422 `idempotency_key_reused` | A request with the same key is still running, or you used the key with a different body | Wait, or use a new key |
| 400 `transaction not found` | The id is wrong, or it is from the other environment | Check the id and the API key |
| 400 `event not found` (webhooks) | The id is not a history row's `id`, or it is from the other environment | Use the history row's `id` |

## Webhooks you will receive

- `transfer.requires_information` when a transfer pauses. It carries `reason`, and `limit_details` with `breached_window`, `cap_usd`, `used_usd`, `remaining_usd` and `requirements`.
- `transfer.resumed` after a resume. Its `reason` is `resumed`, or `released` when Gravv releases the transfer.
- Then the usual `pending`, `completed` or `failed` events.
- When a pause ends, `transfer.status.failed` with `reason` `limit_pause_expired`, `limit_pause_cancelled`, `sender_details_expired` or `supporting_document_expired`.

## Testing in sandbox

A new sandbox customer's limits policy is in `shadow` mode. You cannot produce a limit pause unless support sets a test policy, and sandbox transfers do not count toward usage.

Webhook history and the resend work in sandbox. Their data is separate from live.

## Reference

- [Paused transfers](/platform/transfers/paused-transfers)
- [Transfer limits](/platform/transfers/transfer-limits)
- [Follow a payout to completion](/recipes/bank-payouts/follow-a-payout-to-completion)
