---
title: "Stay within transfer limits"
description: "Check a sender's headroom before you send, and answer a transfer that paused for a limit, for screening or for sender details."
---
Use this recipe to check a sender's headroom before you send, and to answer a transfer that paused for a limit, for screening or for sender details.

**Who it is for.** A remittance platform whose business runs an enforced limits policy, with daily, weekly and monthly caps in USD per sender.

**Status.** Available if your business has a limits policy.

## Money example

1. Sender Maria Lopez has a weekly cap of 2,500.00 USD and has sent 2,200.00 USD this week.
2. You send 1,500.00 USD by ACH. That makes 3,700 USD this week, which is over the 2,500 USD cap. Gravv **pauses** the transfer instead of refusing it, with `breached_window` `weekly` and `remaining_usd` `300.00`.
3. You can resume the transfer with stronger sender verification, if `requirements` offers it. You can also wait for Gravv to review it, send 300.00 USD or less instead, or let the pause expire after 72 hours. If the pause expires, Gravv refunds the fee.

## Before you start

Find out whether your business has a limits policy. [`GET /v1/customers/{customer_id}/limits`](/api-reference/limits/get-v1-customers-customer-id-limits) returns `mode` `off`, `shadow` or `enforce`. In `shadow` mode, no transfer ever pauses. A 404 `not found: no limits policy applies` means there is no policy.

## Find out what is required

### Headroom before you send

<CodeGroup>
```bash title="cURL"
curl --request GET \
     --url https://api.gravv.xyz/v1/customers/7c9e6679-7425-40de-944b-e07fc1f90ae7/limits \
     --header 'Api-Key: <API_KEY>'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v1/customers/7c9e6679-7425-40de-944b-e07fc1f90ae7/limits", {
  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/customers/7c9e6679-7425-40de-944b-e07fc1f90ae7/limits",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

The smallest `remaining_usd` across `windows` is the most this sender can send now. `cap_usd` `null` means that window has no cap. Weeks are ISO weeks, Monday to Sunday, in UTC. [Transfer limits](/platform/transfers/transfer-limits) explains every field in the response. When you send on behalf of end senders, read [`GET /v1/limits/remitters/{remitter_id}`](/api-reference/limits/get-v1-limits-remitters-remitter-id) instead.

### The paused response

The paused response carries `pause_reason`, `breached_window`, `remaining_usd`, `requirements[]` and `expires_at`. `pause_reason` is one of `per_transfer`, `daily`, `weekly`, `monthly`, `annual`, `annual_requires_approval`, `screening` or `sender_details_required`.

## Steps

### 1. Send, and read `transfer_status`

If the amount fits within the caps, send as usual. If the HTTP 200 response shows `transfer_status` `paused`, go to step 2.

```json title="Paused for a limit"
{
  "data": {
    "amount": "1500",
    "amount_currency": "USD",
    "reference": "d2c4e6f8-0a1b-4c3d-9e5f-7a8b9c0d1e2f",
    "date_created": "2026-10-13T12:00:00.000Z",
    "transfer_status": "paused",
    "transaction_id": "e3d5f7a9-1b2c-4d4e-8f60-8a9b0c1d2e3f",
    "local_amount": null,
    "local_currency": null,
    "fee": "1.5",
    "fee_currency": "USD",
    "requires_approval": false,
    "status": "awaiting_remitter_information",
    "park_reason": "remitter_information",
    "pause_reason": "weekly",
    "requirements": [
      {
        "type": "remitter_verification",
        "tier": "T2",
        "levels": ["enhanced"],
        "description": "resume with remitter_verification at one of these levels"
      }
    ],
    "breached_window": "weekly",
    "remaining_usd": "300.00",
    "expires_at": "2026-10-16T12:00:00Z"
  },
  "error": null
}
```

### 2. Answer what `requirements` asks for

**`remitter_verification`.** This applies to businesses that verify their own senders. Resume with a verification at one of the listed `levels`. Send only checks you ran.

<CodeGroup>
```bash title="cURL"
curl --request POST \
     --url https://api.gravv.xyz/v1/transfer/d2c4e6f8-0a1b-4c3d-9e5f-7a8b9c0d1e2f/resume \
     --header 'Api-Key: <API_KEY>' \
     --header 'Content-Type: application/json' \
     --header 'Idempotency-Key: 7ee1ac93-8eec-4f46-95e0-fbc08703469f' \
     --data '
{
  "remitter_verification": {
    "remitter_id": "rem-maria-lopez-01",
    "level": "enhanced",
    "provider": "acme-kyc",
    "provider_reference": "chk_558120",
    "verified_at": "2026-10-13T11:58:00Z",
    "screening": {
      "sanctions": "clear",
      "pep": "clear",
      "adverse_media": "clear"
    }
  }
}
'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v1/transfer/d2c4e6f8-0a1b-4c3d-9e5f-7a8b9c0d1e2f/resume", {
  method: "POST",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": "7ee1ac93-8eec-4f46-95e0-fbc08703469f",
  },
  body: JSON.stringify({
    "remitter_verification": {
      "remitter_id": "rem-maria-lopez-01",
      "level": "enhanced",
      "provider": "acme-kyc",
      "provider_reference": "chk_558120",
      "verified_at": "2026-10-13T11:58: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/d2c4e6f8-0a1b-4c3d-9e5f-7a8b9c0d1e2f/resume",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
        "Content-Type": "application/json",
        "Idempotency-Key": "7ee1ac93-8eec-4f46-95e0-fbc08703469f",
    },
    json={
        "remitter_verification": {
            "remitter_id": "rem-maria-lopez-01",
            "level": "enhanced",
            "provider": "acme-kyc",
            "provider_reference": "chk_558120",
            "verified_at": "2026-10-13T11:58:00Z",
            "screening": {
                "sanctions": "clear",
                "pep": "clear",
                "adverse_media": "clear",
            },
        },
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

```json title="Response"
{
  "data": {
    "transfer_id": "d2c4e6f8-0a1b-4c3d-9e5f-7a8b9c0d1e2f",
    "status": "approved_awaiting_execution",
    "transfer_status": "pending",
    "resumed": true
  },
  "error": null
}
```

If the transfer is still over a cap, the response is `resumed: false` with new `requirements`.

**`screening_clear`.** Resume with a `remitter_verification` whose `screening` values are all `clear`.

**`admin_review`.** Only Gravv can release the transfer. Contact support with the `reference`, or wait for the next window to start.

**Sender details.** Some payout routes need the sender's identity in `remitter_full_name`, `remitter_date_of_birth`, `remitter_address` and `remitter_country`. Resume with `additional_information`:

<CodeGroup>
```bash title="cURL"
curl --request POST \
     --url https://api.gravv.xyz/v1/transfer/d2c4e6f8-0a1b-4c3d-9e5f-7a8b9c0d1e2f/resume \
     --header 'Api-Key: <API_KEY>' \
     --header 'Content-Type: application/json' \
     --header 'Idempotency-Key: 17273857-9c44-4272-91e8-49bd4214e6f1' \
     --data '
{
  "additional_information": {
    "remitter_full_name": "Maria Lopez",
    "remitter_date_of_birth": "1988-04-17",
    "remitter_country": "US",
    "remitter_address": {
      "address_line1": "55 Water Street",
      "city": "Brooklyn",
      "postal_code": "11201",
      "country": "US"
    }
  }
}
'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v1/transfer/d2c4e6f8-0a1b-4c3d-9e5f-7a8b9c0d1e2f/resume", {
  method: "POST",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": "17273857-9c44-4272-91e8-49bd4214e6f1",
  },
  body: JSON.stringify({
    "additional_information": {
      "remitter_full_name": "Maria Lopez",
      "remitter_date_of_birth": "1988-04-17",
      "remitter_country": "US",
      "remitter_address": {
        "address_line1": "55 Water Street",
        "city": "Brooklyn",
        "postal_code": "11201",
        "country": "US"
      }
    }
  }),
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.post(
    "https://api.gravv.xyz/v1/transfer/d2c4e6f8-0a1b-4c3d-9e5f-7a8b9c0d1e2f/resume",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
        "Content-Type": "application/json",
        "Idempotency-Key": "17273857-9c44-4272-91e8-49bd4214e6f1",
    },
    json={
        "additional_information": {
            "remitter_full_name": "Maria Lopez",
            "remitter_date_of_birth": "1988-04-17",
            "remitter_country": "US",
            "remitter_address": {
                "address_line1": "55 Water Street",
                "city": "Brooklyn",
                "postal_code": "11201",
                "country": "US",
            },
        },
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

**`customer_email`.** Add an email to the customer with [`PUT /v1/customers/{customer_id}`](/api-reference/customers/put-v1-customers-customer-id), then resume with `{}`.

**If you do nothing.** At `expires_at`, the transfer fails with `reason` `limit_pause_expired` (or `sender_details_expired`), and Gravv refunds the fee.

Use a new `Idempotency-Key` for every resume attempt.

## What can go wrong

| Response | Why | Fix |
| :-- | :-- | :-- |
| 200 `transfer_status` `paused`, `pause_reason` `weekly` | The weekly cap is reached | Follow step 2, or send at most `remaining_usd` |
| 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 with backoff |
| 400 `remitter_verification.remitter_id is required` | The verification has no sender id | Add `remitter_id` |
| 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` | The resume has a different `remitter_id` | Use the same `remitter_id`, or send a new transfer |
| 400 `remitter_date_of_birth must be a date as YYYY-MM-DD` | The date has the wrong format, or is in the future | Send `1988-04-17` |
| 200 `resumed: false` with new `requirements` | The transfer is still over a cap, or a detail is still missing | Send what is listed |
| 410 `ERROR_CODE_TRANSFER_PAUSE_EXPIRED` or `ERROR_CODE_TRANSFER_PAUSE_CANCELLED` | The pause has ended | Send a new transfer |
| 409 `ERROR_CODE_TRANSFER_IN_PROGRESS` with `Retry-After: 1` | Gravv is still creating a transfer with the same `client_reference` | Wait a second and retry |
| 404 `not found: subject` on the limits read | The customer is not one of yours | Check the id |

## Webhooks you will receive

1. `transfer.requires_information`, with `event_data.status` `paused`, `reason` (the pause reason) and `limit_details` (`breached_window`, `cap_usd`, `used_usd`, `remaining_usd`, `amount_usd`, `requirements`).
2. After a resume, `transfer.resumed`, then the usual transfer events.
3. If the pause expires or is cancelled, `transfer.status.failed` with `reason` `limit_pause_expired`, `limit_pause_cancelled`, `sender_details_expired` or `sender_details_cancelled`.

## Testing in sandbox

A new sandbox customer's policy is in `shadow` mode. You cannot produce a limit pause there, and usage does not change. The limits read works, and a non-UUID id returns 400 `subject_id "not-a-uuid" is not a customer id`. Contact support for a test policy if you need to test a pause.

## Reference

- [Transfer limits](/platform/transfers/transfer-limits)
- [Paused transfers](/platform/transfers/paused-transfers)
- [Resume a paused transfer](/api-reference/transfer/post-v1-transfer-id-resume)
