Gravv docs
Docs Agent Sign in Get API keys

Create a transfer

Sends money from a customer's account, crypto wallet or card to an account, crypto wallet, crypto address, card or external account. The call returns once the transfer is recorded; it then runs in the background. Follow it with GET /v1/transactions/{transaction_id} or the transfer.status.* webhooks.

A 200 can still be transfer_status: paused. Read requirements, upload documents with POST /v1/transfer/documents if asked, then call POST /v1/transfer/{id}/resume. Put rail inside destination. Send at most 2 decimal places. Most errors are a 400 with no code.

POSThttps://api.gravv.xyz/v1/transfer

Headers

1 to 255 printable ASCII characters. A retry with the same key within 24 hours returns the first response, including a 400. After you correct a request, send a new key.

Request Body

A card source can only be withdrawn to its own card account.

Possible values: internal_account, internal_crypto_wallet, card

ID of the account, crypto wallet or card (UUID version 4)

Accepted but ignored. The transfer uses the source's own currency.

Possible values: USD, EUR, GBP

Asset sent, such as USDC or USDT. Defaults to the network's usual asset.

One of the following variants:

Possible values: external_account

An active external account

Payout rail. ach, wire and rtp are US domestic rails; wire is not an international wire. Leave it out, or send globalbank, for local bank and mobile money payouts.

Possible values: ach, wire, rtp, sepa, swift, fps, globalbank

Possible values: internal_account

An active account. For a card source, its own card account.

Possible values: internal_crypto_wallet

Asset received. Between two wallets of the same customer, USDT to USDC or back converts the asset.

Possible values: USDC, USDT

Possible values: crypto_wallet

Must match the source's network, except for USDC moved between networks

Possible values: avalanche, ethereum, polygon, solana, stellar, tron, sui, base

Memo for addresses that need one, such as some stellar exchange addresses

Possible values: card

ID of the card to fund

Positive amount in major units of the source currency, with at most 2 decimal places. Send it as a string; a number is accepted but can lose precision.

The customer sending the transfer (UUID version 4)

Optional note for your records

Your unique reference. On tenants whose limits are enforced, a reused reference returns the first transfer, or a 409 when the request differs. Send it so that a retry does not pay out twice.

Your data, often JSON text, returned on the transaction

Note the receiving bank shows the payee. Plain ASCII, at most 140 characters. ACH allows 10 letters, digits or spaces. A US wire allows 35 letters, digits, spaces and / - ? : ( ) . , ' +. Other rails ignore it.

Up to 5 IDs from POST /v1/transfer/documents for this customer, each unused. Needed when a payout requires a document; otherwise the transfer pauses for one.

Sender details. Some payout routes need them; without them the transfer pauses with sender_details_required.

Alpha-2 code

YYYY-MM-DD

YYYY-MM-DD

YYYY-MM-DD, not in the future

Alpha-2 code

Your record of how you verified the sender. When your limits policy uses attested tiers, the transfer counts against this remitter instead of the customer.

Your ID for the sender

Who verified the sender

Each value must be clear

Request Examples

{
"source": {
"source_type": "internal_account",
"id": "b9c2cb0d-e09a-496f-8655-d8c4b7aaf2f8"
},
"destination": {
"destination_type": "external_account",
"id": "70c337ad-e3e8-4a90-9de7-7f3e6bc2aac8"
},
"amount": "250.00",
"customer_id": "bb6c6022-f1d8-492f-80f0-7547134bb42f",
"client_reference": "inv-114",
"description": "Invoice 114",
"additional_information": {
"remitter_full_name": "John Doe",
"remitter_country": "US",
"remitter_date_of_birth": "1990-06-25",
"remitter_address": {
  "address_line1": "123 Main Street",
  "city": "New York",
  "state": "NY",
  "postal_code": "10001",
  "country": "US"
}
}
}
{
"source": {
"source_type": "internal_account",
"id": "b9c2cb0d-e09a-496f-8655-d8c4b7aaf2f8"
},
"destination": {
"destination_type": "external_account",
"id": "70c337ad-e3e8-4a90-9de7-7f3e6bc2aac8",
"rail": "wire"
},
"amount": "1200.00",
"customer_id": "bb6c6022-f1d8-492f-80f0-7547134bb42f",
"client_reference": "inv-115",
"payment_rail_reference": "INV 115"
}
{
"source": {
"source_type": "internal_account",
"id": "b9c2cb0d-e09a-496f-8655-d8c4b7aaf2f8"
},
"destination": {
"destination_type": "internal_account",
"id": "915a6953-49a1-4a78-b29d-56d401d9f22a"
},
"amount": "10.00",
"customer_id": "bb6c6022-f1d8-492f-80f0-7547134bb42f",
"client_reference": "move-0042"
}
{
"source": {
"source_type": "internal_account",
"id": "b9c2cb0d-e09a-496f-8655-d8c4b7aaf2f8"
},
"destination": {
"destination_type": "crypto_wallet",
"blockchain_network": "polygon",
"wallet_address": "0x25a4c9853e761753ae18d6871731e305b8b0b2a4"
},
"amount": "2.00",
"customer_id": "bb6c6022-f1d8-492f-80f0-7547134bb42f",
"client_reference": "send-0043"
}
{
"source": {
"source_type": "internal_crypto_wallet",
"id": "67e169c0-547f-410d-8e18-f616b6822d70"
},
"destination": {
"destination_type": "card",
"id": "3397bab8-0f2b-4721-b15b-8c71860f01f9"
},
"amount": "30.00",
"customer_id": "04dfb2e5-1274-4214-b5fd-3415fde7dc18",
"client_reference": "card-fund-1234"
}

Responses

Status Description
200 The transfer is recorded, or paused
400 Invalid request, insufficient balance, or a source, destination or external account problem
409 client_reference was used for a different transfer, or the same one is still being set up (retry after 1 second)
422 The sender is blocked, a supporting document is required, or the Idempotency-Key was used with a different body
503 Limits cannot be checked right now. Retry.

200 response

Why the transfer paused

Possible values: per_transfer, daily, weekly, monthly, annual, annual_requires_approval, screening, sender_details_required, supporting_document_required

What to send on resume: remitter_verification, screening_clear, supporting_document, a sender detail such as remitter_full_name, or customer_email (update the customer, then resume). admin_review means only Gravv can release it.

Possible values: per_transfer, daily, weekly, monthly, annual

Allowance left in that window, in USD

When the pause expires, 72 hours by default. The transfer then fails.

{
"data": {
"amount": "250",
"amount_currency": "USD",
"reference": "3f0c6a8e-6b1d-4c1e-9a55-0d2f7b9e4a11",
"date_created": "2026-10-13T09:15:22.481Z",
"transfer_status": "pending",
"transaction_id": "8d4e2b6c-1f3a-4e7b-b0c9-5a6d7e8f9a01",
"local_amount": "382500",
"local_currency": "NGN",
"fee": "1.5",
"fee_currency": "USD",
"requires_approval": false
},
"error": null
}
{
"data": {
"amount": "5000",
"amount_currency": "USD",
"reference": "c1a2b3c4-d5e6-4f70-8a91-b2c3d4e5f607",
"date_created": "2026-10-13T09:15:22.481Z",
"transfer_status": "paused",
"transaction_id": "0e1f2a3b-4c5d-4e6f-8a7b-9c0d1e2f3a4b",
"local_amount": null,
"local_currency": null,
"fee": "0",
"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": "1200.00",
"expires_at": "2026-10-16T09:15:22Z"
},
"error": null
}

400 response

What went wrong

{
"data": null,
"error": "insufficient balance"
}
{
"data": null,
"error": "payee 70c337ad-e3e8-4a90-9de7-7f3e6bc2aac8 is not active: pending"
}

409 response

Such as ERROR_CODE_DUPLICATE_TRANSACTION

{
"data": null,
"error": "client_reference has already been used for a different transfer (the amount, currency, source, destination or remitter differ); use a new client_reference for a new transfer",
"code": "ERROR_CODE_DUPLICATE_TRANSACTION"
}

422 response

Such as ERROR_CODE_DUPLICATE_TRANSACTION

{
"data": null,
"error": "transfers are not permitted for this sender",
"code": "ERROR_CODE_SUBJECT_BLOCKED"
}

503 response

Such as ERROR_CODE_DUPLICATE_TRANSACTION

{
"data": null,
"error": "transfer limits are temporarily unavailable, please retry",
"code": "ERROR_CODE_LIMITS_UNAVAILABLE"
}

Authorization

  • ApiKeyAuth
Was this page helpful?
ESC