Webhooks
Webhooks drive the Transfer SNS lifecycle. After POST /v1/transfer/sns is accepted, Gravv sends HTTPS POST events to your configured webhook endpoint as the transfer status changes.
Transfer SNS event types
Transfer status events:
transfer.status.pendingtransfer.status.completedtransfer.status.failed
completed and failed are terminal.
Event payload
{
"event_category": "transfer",
"event_type": "transfer.status.completed",
"event_group_id": "7c9e6679-3333-3333-3333-333333333333",
"timestamp": "2025-10-27T10:11:05Z",
"event_data": {
"reference": "5f8d0a11-4444-4444-4444-444444444444",
"status": "completed",
"amount": "100.00",
"client_reference": "wave-2026-06-18-001",
"remark": null,
"tx_hash": null,
"client_metadata": null
}
}
Field reference
| Field | Type | Description |
|---|---|---|
| event_category | string | Event group. For this flow it is transfer. |
| event_type | string | Transition event (pending, completed, failed). |
| event_group_id | string | Correlates all events for a single transfer lifecycle. |
| timestamp | string | ISO 8601 event timestamp. |
| event_data | object | Transfer event payload (reference, status, amount, etc). |
Verify webhook signatures
Gravv signs all webhook requests with HMAC-SHA256 to verify authenticity and prevent tampering. You should verify the signature before processing webhook events.
How signature verification works
Gravv generates a signature using:
- Your webhook secret key from the Dashboard
- The webhook payload
- HMAC-SHA256 algorithm
The signature appears in the X-Signature header of each webhook request.
Verification steps
To verify a webhook signature:
- Extract the signature from the
X-Signatureheader - Generate a signature using your secret key and the raw request body
- Compare the generated signature with the received signature
- Process the webhook only if the signatures match
Signature generation
The following code sample in Go shows how to generate webhook signatures for verification:
func generateSignature(secretKey string, payload []byte) (string, error) {
// 1. Initialize HMAC with SHA-256 and the Secret Key
mac := hmac.New(sha256.New, []byte(secretKey))
// 2. Hash the Payload
_, err := mac.Write(payload)
if err != nil {
return "", fmt.Errorf("failed to write to hmac: %w", err)
}
// 3. Compute the signature (HMAC digest) and encode it as a hexadecimal string
return hex.EncodeToString(mac.Sum(nil)), nil
}
Delivery behavior
Return 2xx after successful processing. Non-2xx responses are retried, so webhook handlers should be idempotent.