Event catalogue
Every webhook event Gravv sends, when it is sent, its fields and an example.
This page lists every event Gravv sends to your webhook URL, grouped by what it is about. Each section gives the event types, when each one is sent, the fields in event_data and an example. The fields every event shares are on Webhook payload structure.
If an event type is not on this page, Gravv does not send it.
Example. Ada pays a supplier 100.50 USD. You receive transfer.status.pending three times, one for each step, with idempotency_key ending in :1, :2 and :3. Then transfer.status.completed arrives with :4, and you mark the invoice paid. If the :3 event reaches you after the :4 one, you ignore it, because its lower number means it is older.
How to read an event
event_typeis the full type, such astransfer.status.completed.event_categoryis the first part, such astransfer. Webhook history filters by it.event_group_idis the same for every event about one thing: one transfer, deposit, card or customer. Each section says which id it is.- Deduplicate on the body's
event_id. It stays the same when an event is retried or resent. - Keys in
event_dataarrive in alphabetical order. Gravv can add new keys at any time, so ignore the ones you do not know. - Amounts in
event_dataare strings, such as"100.5"or"1500.00", unless a section says otherwise. - Gravv compares events that carry no
idempotency_keyby their content. It does not send an event identical to an earlier one for the same object. When a card goesactive, thenfreeze, thenactiveagain, Gravv does not send the secondactive. Read the object after a change you made yourself. - Events can arrive in any order. Each section says how to tell which is newer.
All event types
| Event type | Category | Sent when |
|---|---|---|
transfer.status.pending |
transfer |
A transfer moved to a new step. Several arrive for one transfer |
transfer.status.completed |
transfer |
The transfer was paid |
transfer.status.failed |
transfer |
The transfer failed, or a person must check it |
transfer.status.returned |
transfer |
A completed payout came back and the money was returned |
transfer.requires_information |
transfer |
The transfer paused and waits for you |
transfer.resumed |
transfer |
A paused transfer resumed |
transfer.inflow.deposit_received |
transfer |
A deposit in another currency is being converted |
transfer.inflow.awaiting_approval |
transfer |
A deposit is held for approval |
transfer.inflow.completed |
transfer |
A deposit was credited to the account |
transfer.inflow.deposit_settled |
transfer |
A deposit was credited and settled on-chain |
transfer.inflow.payout_failed |
transfer |
Crediting a deposit failed |
transfer.inflow.compensated |
transfer |
A failed deposit was reversed |
inflow.onchain.completed |
inflow |
Stablecoins arrived at a wallet address |
inflow.onchain.failed |
inflow |
An incoming on-chain transfer failed on chain |
account.activated |
account |
A bank account Gravv registered for you is ready |
applications.accounts.pending |
applications.accounts |
A v2 account application was accepted |
applications.accounts.blocked |
applications.accounts |
A v2 account application waits on you or the customer |
applications.accounts.completed |
applications.accounts |
The v2 account is active |
applications.accounts.failed |
applications.accounts |
The v2 account application ended without an account |
external_account.created |
external_account |
An external account was added |
external_account.active |
external_account |
An external account's setup succeeded |
external_account.setup_failed |
external_account |
An external account's setup did not succeed |
cards.application.update |
cards |
A card application changed status |
cards.status.update |
cards |
A card changed status |
cards.transaction.payment |
cards |
A card payment was recorded or changed status |
cards.transaction.funding |
cards |
Money was added to a card |
cards.transaction.withdraw |
cards |
Money was taken off a card |
cards.dispute.submitted |
cards |
A dispute on a card payment was filed |
cards.dispute.updated |
cards |
A dispute is under review |
cards.dispute.resolved |
cards |
A dispute ended |
collection.status.pending |
collection |
A collection is in progress, expired or was cancelled |
collection.status.awaiting_capture |
collection |
A card payment is authorized and waits for capture |
collection.status.provider_success |
collection |
The payment succeeded and settlement has not finished |
collection.status.completed |
collection |
The money reached your destination |
collection.status.failed |
collection |
The payment failed, was abandoned, or a refund failed |
collection.status.provider_voided |
collection |
A card authorization was voided |
collection.status.refund_pending |
collection |
A refund started |
collection.status.refunded |
collection |
The payment was refunded |
collection.status.disputed |
collection |
The payer opened a chargeback, or one was lost |
customer.created |
customer |
A customer or an associated person was added |
customer.kyc.status.pending |
customer |
Verification started or is still under way |
customer.kyc.status.completed |
customer |
Verification passed |
customer.kyc.status.failed |
customer |
Verification did not pass |
feature_access.requested |
feature_access |
You asked for access to a feature |
feature_access.action_required |
feature_access |
Gravv needs more information about your request |
feature_access.approved |
feature_access |
Your request was approved |
feature_access.rejected |
feature_access |
Your request was rejected |
feature_access.suspended |
feature_access |
Gravv suspended a feature for your business |
Transfers
Gravv sends these events for every transfer: payouts, transfers between accounts and wallets, card funding and withdrawal, and conversions. The category is transfer.
event_group_idis the transfer'sreference.idempotency_keyis<reference>:<sequence>. The sequence goes up by one with each change. Apply an event only when its sequence is higher than the last one you applied for that transfer.- The top-level
transaction_idis the first id inassociated_transactions.
| Event type | status |
Sent when |
|---|---|---|
transfer.status.pending |
pending |
The transfer moved to a new internal step. Gravv sends several for one transfer. Treat them all as "still moving" |
transfer.status.completed |
completed |
The transfer was paid |
transfer.status.failed |
failed |
The transfer failed or was rejected, and Gravv returned the money and fee to the source. Gravv also sends it when transfer_status is manual_processing_required. In that case a person is checking the transfer, it is not final, and you must not send it again. Gravv also sends it, with reason, when a pause expired or was cancelled |
transfer.status.returned |
returned |
Sent only after completed. The payout came back, and Gravv returned the money to the source |
transfer.requires_information |
paused |
The transfer paused for a limit, a document or the sender's details. See Paused transfers |
transfer.resumed |
pending |
A pause ended because you resumed it or Gravv released it |
| Field | Type | Description |
|---|---|---|
amount |
string | The transfer amount, without trailing zeros, such as "100.5". There is no currency field, so read the transaction |
associated_transactions |
array of strings | Your transaction ids for this transfer. Left out when there are none |
client_reference |
string | Your client_reference from the request. Left out when you sent none |
destination_id |
string | The destination.id from the request. Left out when there was none |
reference |
string | The transfer's reference, the id you resume a paused transfer with |
status |
string | pending, completed, failed, paused or returned |
transfer_status |
string | Gravv's internal step. Its values can change, so build on status. Read it only on transfer.status.failed, to tell manual_processing_required from a final failure |
reason |
string | On transfer.requires_information, why it paused. On transfer.resumed, resumed or released. On a pause that ended without resuming: limit_pause_expired, limit_pause_cancelled, supporting_document_expired, supporting_document_cancelled, sender_details_expired or sender_details_cancelled |
limit_details |
object | Only on a pause: reason, breached_window, cap_usd, used_usd, remaining_usd, amount_usd, fee_usd, tier, subject_type, subject_id, policy_id, policy_version, and requirements (a list of type, tier, levels, description) |
tx_hash |
string or null | The on-chain hash, when the transfer has one |
workflow_type |
string | The kind of transfer: crypto_transfer, internal_transfer, auto_sweep, card_funding, card_withdrawal, global_remittance, us_bank_transfer, sepa_transfer, fps_transfer, swift_transfer, chaps_transfer, cross_chain_self_transfer or stablecoin_conversion |
send_asset, amount_sent, receive_asset, receive_amount, min_receive_amount, provider_fee, refunded_amount, refund_asset |
string | Only on a stablecoin conversion: what was sent, what was or will be received, and the conversion fee. When a conversion failed and was refunded, receive_amount is left out and refunded_amount and refund_asset are set |
{
"tenant_id": "dfa96ede-fc13-43cf-8328-8b910d1cd1d2",
"transaction_id": "5d1e7a3b-2c4f-4b8e-9d6a-1f0e3c2b7a95",
"event_category": "transfer",
"event_type": "transfer.status.completed",
"event_group_id": "9a7c1e52-6b3d-4f0a-8e21-7d5c4b3a2f10",
"timestamp": "2026-10-13T09:15:02.481263Z",
"event_data": {
"amount": "100.5",
"associated_transactions": ["5d1e7a3b-2c4f-4b8e-9d6a-1f0e3c2b7a95"],
"client_reference": "inv-2291",
"destination_id": "c2f5a0b1-8d3e-4a6f-9b7c-0e1d2f3a4b5c",
"reference": "9a7c1e52-6b3d-4f0a-8e21-7d5c4b3a2f10",
"status": "completed",
"transfer_status": "completed",
"workflow_type": "global_remittance"
},
"event_id": "e4b1c0d2-3f5a-4e6b-8c7d-9a0b1c2d3e4f",
"idempotency_key": "9a7c1e52-6b3d-4f0a-8e21-7d5c4b3a2f10:4"
}
Deposits into accounts
Gravv sends these events when money arrives in one of your customers' accounts. The category is transfer, so webhook history returns them with the transfer events.
event_group_idis the deposit'stransaction_id. Ontransfer.inflow.payout_failedit is Gravv's own id for the deposit.- Gravv sends each type at most once for one deposit.
- Gravv credits the deposit in full and charges any deposit fee separately. See Add up a month of fees.
| Event type | status |
Sent when | Fields besides the sender fields |
|---|---|---|---|
transfer.inflow.deposit_received |
deposit_received |
A deposit in another currency is being converted to the account's currency. Not sent when the currencies match | transaction_id, account_id, amount (the estimated amount after conversion), currency (the account's), reference, remark, tx_hash (the conversion's reference, not a chain hash), source and destination |
transfer.inflow.awaiting_approval |
awaiting_approval |
The deposit is held for approval before it is credited | transaction_id, account_id, amount, currency |
transfer.inflow.completed |
completed |
The deposit was credited | transaction_id, account_id, amount, currency, and settlement_event_group_id when it was also settled on-chain |
transfer.inflow.deposit_settled |
settled |
The deposit was credited and settled on-chain to the account's wallet | The same as completed, plus tokenized true |
transfer.inflow.payout_failed |
failed |
Crediting the deposit failed | account_id, error, failed_at, and transaction_id "" |
transfer.inflow.compensated |
compensated |
A failed deposit was reversed | account_id, deposit_id, failure_reason |
Every type also carries the sender fields sender_name, sender_account_number, sender_bank, session_id and narration. They are strings and can be empty.
For a credited deposit, Gravv sends transfer.inflow.completed or transfer.inflow.deposit_settled, depending on the account. Treat either one as credited. When the deposit was also settled on-chain, Gravv sends a second event, inflow.onchain.completed, for the same money. Its event_group_id equals settlement_event_group_id here. Credit your customer once.
{
"tenant_id": "dfa96ede-fc13-43cf-8328-8b910d1cd1d2",
"transaction_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"event_category": "transfer",
"event_type": "transfer.inflow.completed",
"event_group_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"timestamp": "2026-10-13T08:02:11Z",
"event_data": {
"account_id": "4f3e2d1c-0b9a-4876-9543-210fedcba987",
"amount": "2500",
"currency": "USD",
"narration": "Invoice 4471",
"sender_account_number": "0123456789",
"sender_bank": "Example Bank",
"sender_name": "Jane Doe",
"session_id": "",
"status": "completed",
"transaction_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
},
"event_id": "c0ffee00-1234-4abc-9def-001122334455",
"idempotency_key": "6c1f0a9e2b7d4c3a8e5f1b0d9c7a6e4f2d3b1a0c9e8f7d6c5b4a3f2e1d0c9b8a"
}
On-chain deposits
Gravv sends these events when USDC or USDT arrives at a wallet address, or at the wallet address of an account. The category is inflow.
| Event type | Sent when |
|---|---|
inflow.onchain.completed |
An incoming on-chain transfer was detected. Sent once |
inflow.onchain.failed |
An incoming on-chain transfer failed on chain |
- Gravv derives
event_group_idfrom the transaction hash, so both events for one deposit share it (see Deposits into accounts). - There is no
idempotency_key.
| Field | Type | Description |
|---|---|---|
amount |
string | The amount received |
asset |
string | The token received. It can be the token's contract address, so read asset_symbol when it is present |
asset_symbol |
string | The token's symbol, such as USDC. Left out when unknown |
balance_before, balance_after |
string or null | The wallet's balance before and after, when known |
blockchain_network |
string | The network, such as polygon |
confirmations |
number | Confirmations at detection |
destination |
object | wallet_address, blockchain_network, blockchain_memo (always null), and the wallet or account id and destination_type when known |
source |
object | The sender's wallet_address and blockchain_network, and blockchain_memo (always null) |
remark |
string | Crypto Funding, or inbound transaction failed on chain |
status |
string | COMPLETED or FAILED, in upper case |
transaction_id |
string | The chain transaction as <network>:<hash>. It is not the id of a transaction row. Find the row by tx_hash |
tx_hash |
string | The transaction hash |
{
"tenant_id": "dfa96ede-fc13-43cf-8328-8b910d1cd1d2",
"transaction_id": "polygon:0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060",
"event_category": "inflow",
"event_type": "inflow.onchain.completed",
"event_group_id": "3b8f1d2e-4c5a-5b6d-8e7f-9a0b1c2d3e4f",
"timestamp": "2026-10-13T07:58:40Z",
"event_data": {
"amount": "250.00",
"asset": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
"asset_symbol": "USDC",
"balance_after": "1250.00",
"balance_before": "1000.00",
"blockchain_network": "polygon",
"confirmations": 12,
"destination": {
"blockchain_memo": null,
"blockchain_network": "polygon",
"destination_type": "wallet",
"id": "0d9c8b7a-6f5e-4d3c-2b1a-0f9e8d7c6b5a",
"wallet_address": "0x8ba1f109551bD432803012645Ac136ddd64DBA72"
},
"remark": "Crypto Funding",
"source": {
"blockchain_memo": null,
"blockchain_network": "polygon",
"wallet_address": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"
},
"status": "COMPLETED",
"transaction_id": "polygon:0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060",
"tx_hash": "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060"
},
"event_id": "aa11bb22-cc33-4dd4-8ee5-ff6600778899"
}
Gravv also sends events for other tokens that land on an address, such as a spam token. Credit only USDC and USDT.
Account applications
Gravv sends these events as a v2 account you open with POST /v2/accounts moves from creation to active. The category is applications.accounts, and event_group_id is the application id.
| Event type | Sent when |
|---|---|
applications.accounts.pending |
The application was accepted. Gravv can skip this event, so do not depend on it |
applications.accounts.blocked |
The application waits on you or the customer: kyc_pending, tos_pending, awaiting_customer_document or awaiting_customer_data |
applications.accounts.completed |
The account is active. It carries no bank details, so read the account |
applications.accounts.failed |
The application ended without an account |
Gravv sends no event for a block that waits on nobody, such as the bank's own review. Gravv sends each type once for one application, so it sends nothing for a second block of another kind. After you act on a block, read the application again instead of waiting for another event.
| Field | Type | Description |
|---|---|---|
application_id |
string | The application |
account_id |
string | The account it opens |
customer_id |
string | The customer |
tenant_id |
string | Your business |
type |
string | The event type again, such as applications.accounts.blocked |
status |
string | pending, blocked, completed or failed |
created_at, updated_at |
string | Timestamps. The envelope timestamp is updated_at |
metadata |
object or null | null on pending and completed. On blocked: block_reason, action_url (for tos_pending and kyc_pending), and additional_requirements with fields and documents. On failed: fail_reason, one of verification_rejected, identity_document_mismatch, information_expired, region_not_supported, request_cancelled, review_declined or verification_declined |
{
"tenant_id": "5d0c9b8a-7f6e-4d5c-8b4a-3f2e1d0c9b8a",
"transaction_id": "",
"event_category": "applications.accounts",
"event_type": "applications.accounts.blocked",
"event_group_id": "5e7a9c1b-2d4f-4a6b-8c0d-e1f2a3b4c5d6",
"timestamp": "2026-10-13T09:20:02Z",
"event_data": {
"account_id": "6f1c2a9e-3b1d-4f7a-9c62-0d8e4b7a5c11",
"application_id": "5e7a9c1b-2d4f-4a6b-8c0d-e1f2a3b4c5d6",
"created_at": "2026-10-13T09:12:44Z",
"customer_id": "0b6f3c1e-2a4d-4c8e-9f10-7d2a6b5e4c33",
"metadata": {
"additional_requirements": {
"documents": ["proof_of_address"],
"fields": []
},
"block_reason": "awaiting_customer_document"
},
"status": "blocked",
"tenant_id": "5d0c9b8a-7f6e-4d5c-8b4a-3f2e1d0c9b8a",
"type": "applications.accounts.blocked",
"updated_at": "2026-10-13T09:20:02Z"
},
"event_id": "2f4a6c8e-0b1d-4f3a-8c5e-7a9b1d3f5a7c",
"idempotency_key": "4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b"
}
On completed, read GET /v2/accounts/{id} for the bank details.
Accounts Gravv registers for you
Gravv sends account.activated once, when an existing bank account that Gravv registered for you becomes active. Gravv never sends it for a v2 account you open yourself, so wait for applications.accounts.completed instead. The category is account, and event_group_id is the account id.
| Field | Type | Description |
|---|---|---|
account_id |
string | The account |
customer_id |
string | The customer it belongs to |
status |
string | active |
currency |
string | The account currency |
bank_details |
object | Deposit details. USD: bank_name, account_name, account_number, routing_number, bank_address, beneficiary_address. EUR: bank_name, account_name, iban, bic, bank_address, beneficiary_address. GBP: the EUR keys plus account_number and sort_code. Other currencies: bank_name, account_name, account_number. Empty values are left out |
wallet_details |
object | address and network, only when the account settles to a wallet |
created_at |
string | When Gravv created the event |
{
"tenant_id": "dfa96ede-fc13-43cf-8328-8b910d1cd1d2",
"transaction_id": "",
"event_category": "account",
"event_type": "account.activated",
"event_group_id": "4f3e2d1c-0b9a-4876-9543-210fedcba987",
"timestamp": "2026-10-12T16:40:05Z",
"event_data": {
"account_id": "4f3e2d1c-0b9a-4876-9543-210fedcba987",
"bank_details": {
"account_name": "Acme Ltd",
"account_number": "9876543210",
"bank_name": "Example Bank",
"routing_number": "021000021"
},
"created_at": "2026-10-12T16:40:05.118Z",
"currency": "USD",
"customer_id": "0ff6cf9a-8da0-466d-a71c-714eb4bde248",
"status": "active"
},
"event_id": "5e4d3c2b-1a09-4f8e-a7d6-c5b4a3928170",
"idempotency_key": "0e9d8c7b6a5f4e3d2c1b0a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d"
}
External accounts
Gravv sends these events when you add an external account and when its setup finishes. The category is external_account. event_group_id is the external account's id, and idempotency_key is <id>:<event type>:<revision>.
A bank_account or mobile_money external account is active as soon as you add it, so Gravv sends only external_account.created for it. An ach_wire, sepa, swift or fps external account starts pending, and Gravv sets it up in the background.
| Event type | Sent when |
|---|---|
external_account.created |
An external account was added, with its status at that moment: pending or active |
external_account.active |
Setup of a pending external account succeeded. You can now send transfers to it |
external_account.setup_failed |
Setup did not succeed, and you can pay only an active external account. error_message says why. Correct the details with Update an external account |
event_data is the external account, with the fields that Get an external account returns. Gravv leaves out fields with no value. On setup_failed, error_message says why. Show your own message to your users, not this text.
{
"tenant_id": "dfa96ede-fc13-43cf-8328-8b910d1cd1d2",
"transaction_id": "",
"event_category": "external_account",
"event_type": "external_account.active",
"event_group_id": "c2f5a0b1-8d3e-4a6f-9b7c-0e1d2f3a4b5c",
"timestamp": "2026-10-12T14:05:51.204377Z",
"event_data": {
"account_name": "ANNA SCHMIDT",
"account_owner_type": "individual",
"currency": "EUR",
"customer_id": "0ff6cf9a-8da0-466d-a71c-714eb4bde248",
"date_created": "2026-10-12T14:02:10.551Z",
"iban": "DE89370400440532013000",
"id": "c2f5a0b1-8d3e-4a6f-9b7c-0e1d2f3a4b5c",
"institution_country_iso_code": "DE",
"payee_type": "sepa",
"status": "active"
},
"event_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"idempotency_key": "c2f5a0b1-8d3e-4a6f-9b7c-0e1d2f3a4b5c:external_account.active:2"
}
Cards
Gravv sends these events for cards you issue. The category is cards. event_group_id is the card id on every card event, including payments. There is no idempotency_key, so Gravv does not send an event identical to an earlier one again.
| Event type | Sent when | event_data |
|---|---|---|
cards.application.update |
The card application changed status | application: id (the card's id), status and customer_id. approved means the card can be issued. pending, manual_review, needs_information and needs_verification mean it is still in review. status is empty when none is set |
cards.status.update |
The card changed status | card (below) |
cards.transaction.payment |
A card payment was recorded or changed status | card and transaction (below) |
cards.transaction.funding |
Money was added to the card | card and transaction |
cards.transaction.withdraw |
Money was taken off the card | card and transaction |
card holds id, customer_id, name_on_card, last4, expiry, network and status, all strings. status is active, freeze, blocked or inactive.
transaction holds:
| Field | Type | Description |
|---|---|---|
id |
string | The card transaction |
transaction_type |
string | card_payment, card_funding or card_withdrawal |
status |
string | pending, completed or failed |
amount |
string | The amount, such as "18.75" |
currency |
string | USD |
client_reference |
string | Only on a funding you sent as a transfer: your client_reference |
reference |
string | On a funding you sent as a transfer, the transfer's reference. Do not build on it for other types |
tx_hash |
string | Only when the movement has an on-chain hash |
A payment event carries no merchant or cardholder details, and a declined payment carries no reason. See Card decline reasons. Do not wait for cards.transaction.funding to confirm a funding. Follow the transfer and the transaction instead, as Fund cards explains.
{
"tenant_id": "dfa96ede-fc13-43cf-8328-8b910d1cd1d2",
"transaction_id": "",
"event_category": "cards",
"event_type": "cards.transaction.payment",
"event_group_id": "6a5b4c3d-2e1f-4a0b-9c8d-7e6f5a4b3c2d",
"timestamp": "2026-10-13T10:22:31.902117Z",
"event_data": {
"card": {
"customer_id": "0ff6cf9a-8da0-466d-a71c-714eb4bde248",
"expiry": "09/29",
"id": "6a5b4c3d-2e1f-4a0b-9c8d-7e6f5a4b3c2d",
"last4": "4242",
"name_on_card": "JANE DOE",
"network": "visa",
"status": "active"
},
"transaction": {
"amount": "18.75",
"currency": "USD",
"id": "f1e2d3c4-b5a6-4978-8a6b-5c4d3e2f1a0b",
"status": "completed",
"transaction_type": "card_payment"
}
},
"event_id": "0f1e2d3c-4b5a-4697-8877-665544332211"
}
Card disputes
Gravv sends these events when a payment on one of your cards is disputed. These docs do not cover an API to open or respond to a dispute, so treat these events as notices for your records and your support team. The category is cards. event_group_id is the card id, and the top-level transaction_id is the disputed transaction.
| Event type | Sent when |
|---|---|
cards.dispute.submitted |
The dispute was filed |
cards.dispute.updated |
The dispute is under review, or it moved to a new stage |
cards.dispute.resolved |
The dispute ended: won, lost, withdrawn or expired |
| Field | Type | Description |
|---|---|---|
id |
string | The dispute |
card_id |
string | The card |
transaction_id |
string | The disputed card transaction |
status |
string | submitted, under_review, won, lost, withdrawn or expired |
reason |
string | fraud, billing_error, duplicate or not_received |
amount |
string | The disputed amount |
currency |
string | The currency |
stage |
string | representment, second_presentment, pre_arbitration or arbitration, when set |
resolution_amount |
string | The amount decided, once known |
evidence_due_by |
string | When evidence is due, when set |
resolved_at |
string | When the dispute ended |
{
"tenant_id": "dfa96ede-fc13-43cf-8328-8b910d1cd1d2",
"transaction_id": "f1e2d3c4-b5a6-4978-8a6b-5c4d3e2f1a0b",
"event_category": "cards",
"event_type": "cards.dispute.resolved",
"event_group_id": "6a5b4c3d-2e1f-4a0b-9c8d-7e6f5a4b3c2d",
"timestamp": "2026-10-13T15:10:44Z",
"event_data": {
"amount": "18.75",
"card_id": "6a5b4c3d-2e1f-4a0b-9c8d-7e6f5a4b3c2d",
"currency": "USD",
"id": "7b2e9c41-5d3a-4f8e-a1b6-0c9d8e7f6a5b",
"reason": "not_received",
"resolution_amount": "18.75",
"resolved_at": "2026-10-13T15:10:44Z",
"status": "won",
"transaction_id": "f1e2d3c4-b5a6-4978-8a6b-5c4d3e2f1a0b"
},
"event_id": "3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f"
}
Collections
Gravv sends these events as a payment collection moves from the payer's payment to the stablecoin reaching your destination. The category is collection. event_group_id and the top-level transaction_id are the collection's transaction_id.
On or after 31 October 2026, the values in onramp_status change and Gravv renames two event types. If you read new_onramp_status now, your integration keeps working through the change. See Status label change.
| Event type | Sent when |
|---|---|
collection.status.pending |
The collection is in progress. Also sent, with onramp_status expired or cancelled, when it expires or is cancelled |
collection.status.awaiting_capture |
A card payment is authorized and waits for capture |
collection.status.provider_success |
The payment succeeded and settlement has not finished |
collection.status.completed |
The money reached your destination |
collection.status.failed |
The payment failed, the payer abandoned it, or a refund failed |
collection.status.provider_voided |
A card authorization was voided. transaction_status is cancelled |
collection.status.refund_pending |
A refund has started |
collection.status.refunded |
The payment was refunded |
collection.status.disputed |
The payer opened a chargeback, or a chargeback was lost |
Gravv sends no event when you create a collection, and the first event can arrive several minutes later. Events can arrive out of order. The step where a payment succeeds can arrive as collection.status.pending with new_onramp_status payment_succeeded. So build on transaction_status and the status label, not on the event type or the order of arrival. Confirm the end state with GET /v1/collections/{id}.
| Field | Type | Description |
|---|---|---|
transaction_id |
string | The collection's id. Use it with GET /v1/collections/{id} |
transaction_status |
string | pending, completed, failed, refunded, refund_pending, disputed or cancelled |
onramp_status |
string | The step, with the label in use until the change. See Status label change |
new_onramp_status |
string | The step, with the new label. Sent until the change |
status_transition_deadline |
string | 2026-10-31T23:59:59Z. Sent until the change |
amount |
string | The amount collected, with two decimals |
asset, network |
string | The stablecoin you receive (USDC or USDT) and its network. Can be empty before settlement |
tx_hash |
string | The settlement hash, once the money has settled |
payment_method |
string | How the payer paid, such as card, when known |
payment_intent_id |
string | The card payment intent, for collections made through one |
payment_info |
object | For card payments: bin, exp_month, exp_year, card_brand, card_type, card_segment, last4, each when known |
error_code |
string | A failure code, such as INSUFFICIENT_FUNDS, CARD_EXPIRED or THREE_DS_FAILED, when known. A decline simulated in sandbox carries none |
error_message, remark |
string | A readable failure description, when known |
client_reference, reference |
string | Your reference from the collection request |
metadata |
object | The data you attached to the collection request |
customer_id |
string | The Gravv customer the collection belongs to |
client_customer_id |
string | Your own customer id, when you passed one |
tenant_id |
string | Your business |
Credit your customer once per transaction_id, never once per event. The same step can arrive twice with different event_id values. Treat a second event with the same transaction_id and status as a duplicate.
Status label change
Every collection event, and every collection the API returns, carries two status labels until 31 October 2026:
onramp_statuscarries the current label, kept for existing integrations.new_onramp_statuscarries the new label.status_transition_deadlineis2026-10-31T23:59:59Z, the date after whichonramp_statustakes the new labels.
On or after 31 October 2026:
onramp_statuscarries the new labels, the valuesnew_onramp_statuscarries today.- Gravv stops sending
new_onramp_statusandstatus_transition_deadline. collection.status.provider_successbecomescollection.status.payment_succeeded.collection.status.provider_voidedbecomescollection.status.payment_voided.
What to do now:
- Match on
new_onramp_statuswhen it is present, and ononramp_statuswhen it is not. - Accept both names of the two renamed event types.
- When you filter collections by
onramp_status, either label works.
Label in onramp_status until the change |
Label in new_onramp_status, and in onramp_status after it |
Meaning |
|---|---|---|
provider_initiated |
payment_initiated |
The payer started paying |
provider_success |
payment_succeeded |
The payment succeeded |
provider_failed |
payment_failed |
The payment failed |
provider_voided |
payment_voided |
A card authorization was voided |
transfer_queued |
settlement_queued |
Settlement to your destination is queued |
transfer_initialized |
settlement_initiated |
Settlement has started |
transfer_success |
settlement_succeeded |
Settlement succeeded |
transfer_failed |
settlement_failed |
Settlement failed. Gravv retries it and does not send this step as an event |
transfer_completed |
settlement_completed |
The money reached your destination |
These labels do not change: created, initialize, pending, awaiting_capture, abandoned, expired, cancelled, pending_review, suspected_fraud, chargeback_opened, chargeback_won, chargeback_lost, refund_initiated, refunded and refund_failed.
{
"tenant_id": "dfa96ede-fc13-43cf-8328-8b910d1cd1d2",
"transaction_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"event_category": "collection",
"event_type": "collection.status.failed",
"event_group_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"timestamp": "2026-10-13T11:04:27Z",
"event_data": {
"amount": "1500.00",
"asset": "",
"customer_id": "0ff6cf9a-8da0-466d-a71c-714eb4bde248",
"error_code": "INSUFFICIENT_FUNDS",
"error_message": "The card has insufficient funds",
"network": "",
"new_onramp_status": "payment_failed",
"onramp_status": "provider_failed",
"payment_info": {
"bin": "424242",
"card_brand": "visa",
"last4": "4242"
},
"payment_intent_id": "pi_7f6e5d4c3b2a",
"payment_method": "card",
"remark": "The card has insufficient funds",
"status_transition_deadline": "2026-10-31T23:59:59Z",
"tenant_id": "dfa96ede-fc13-43cf-8328-8b910d1cd1d2",
"transaction_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"transaction_status": "failed"
},
"event_id": "77665544-3322-4110-8ffe-ddccbbaa9988",
"idempotency_key": "8b7a6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b"
}
Customers and KYC
Gravv sends these events when you create a customer and when their verification changes. The category is customer. event_group_id is the customer id. There is no idempotency_key.
| Event type | Sent when |
|---|---|
customer.created |
A customer was created, or an associated person was added to a business, either at creation or by a later update. Gravv sends one event for each person |
customer.kyc.status.pending |
Verification started, or is still under way. It can arrive more than once |
customer.kyc.status.completed |
Verification passed |
customer.kyc.status.failed |
Verification did not pass |
Gravv sends the KYC events for people and for businesses. Gravv verifies a business only after each of its associated persons, so the persons' events come first.
customer.created carries id, type (individual, business, or a person's role such as ubo), status, email, address and created_at, and a kyc list with basic and intermediate, both not_started. It carries no names and no phone number. Read them with GET /v1/customers/{customer_id}.
The KYC events carry:
| Field | Type | Description |
|---|---|---|
customer_id |
string | The customer whose verification changed |
tenant_id |
string | Your business |
kyc_type |
string | The level, basic |
status |
string | pending, completed or failed |
customer_email |
string | The customer's email |
customer_type |
string | individual or business |
applicant_id |
string | An internal reference. Ignore it, and do not show it to users |
completed_at |
string | When verification passed. Only on completed |
failure_reason |
string | Only on failed. Show your own message, not this text |
business_name, business_type, registration_number |
string | Only for a business |
Gravv sends no pending event when the card KYC check (/v1/risk/*) starts. Its events can also carry country, city, state, postal_code and business_address.
Gravv does not guarantee the order. Before you act, read the customer. kyc[type=basic].status on GET /v1/customers/{customer_id} is the source of truth.
{
"tenant_id": "5d0c9b8a-7f6e-4d5c-8b4a-3f2e1d0c9b8a",
"transaction_id": "",
"event_category": "customer",
"event_type": "customer.kyc.status.completed",
"event_group_id": "3f6b2a1e-8c4d-4e7f-9a2b-5c1d0e9f8a71",
"timestamp": "2026-10-13T10:01:03Z",
"event_data": {
"applicant_id": "<internal reference>",
"completed_at": "2026-10-13T10:01:03Z",
"customer_email": "amara.nwosu@example.com",
"customer_id": "3f6b2a1e-8c4d-4e7f-9a2b-5c1d0e9f8a71",
"customer_type": "individual",
"kyc_type": "basic",
"status": "completed",
"tenant_id": "5d0c9b8a-7f6e-4d5c-8b4a-3f2e1d0c9b8a"
},
"event_id": "0b6e2f4a-8c1d-4e3f-9a5b-7c2d4e6f8a10"
}
Feature access
Gravv sends these events when your request for a feature, such as card issuing, moves forward. A request to a feature that is not enabled for you returns 403 with a feature_access_* code. See Feature access errors. These docs do not cover an API to request a feature or to respond to Gravv, so these events tell you when to look in the Dashboard. The category is feature_access.
| Event type | Sent when |
|---|---|
feature_access.requested |
You sent a request, or answered a request for information (then resubmitted is true) |
feature_access.action_required |
Gravv needs more information. requirements lists what it needs |
feature_access.approved |
The request was approved |
feature_access.rejected |
The request was rejected. reason says why, and reapply_after says when you can ask again |
feature_access.suspended |
Gravv suspended the feature for your business. reason says why |
event_group_idis the request's id, or your business id for a suspension.idempotency_keyis<request_id>:<event type>, or<business id>:<feature>:<event type>for a suspension. So Gravv does not send a second suspension of the same feature.
| Field | Type | Description |
|---|---|---|
feature |
string | card_issuance, card_acquiring or otc_trade_desk |
feature_name |
string | The display name, such as Card Issuance |
status |
string | The feature's new access status: locked, pending, action_required, rejected, enabled or suspended |
request_id |
string | Your request's reference, such as far_7k29pqvd. Left out for a suspension |
reason |
string | Only on rejected and suspended |
reapply_after |
string | The earliest time you can send a rejected request again, when there is a wait |
requirements |
array of strings | Only on action_required. Codes for what is needed. The Dashboard shows the full text |
resubmitted |
boolean | true when requested is your response to action_required |
occurred_at |
string | When it happened. The envelope timestamp is the same |
{
"tenant_id": "dfa96ede-fc13-43cf-8328-8b910d1cd1d2",
"transaction_id": "",
"event_category": "feature_access",
"event_type": "feature_access.action_required",
"event_group_id": "5d2c7a10-3b4e-4f6a-9c8d-1e2f3a4b5c6d",
"timestamp": "2026-10-13T09:41:07Z",
"event_data": {
"feature": "card_issuance",
"feature_name": "Card Issuance",
"occurred_at": "2026-10-13T09:41:07Z",
"request_id": "far_7k29pqvd",
"requirements": ["business_website", "expected_monthly_volume"],
"status": "action_required"
},
"event_id": "e4d3c2b1-a098-4f7e-8d6c-5b4a39281706",
"idempotency_key": "far_7k29pqvd:feature_access.action_required"
}