Webhooks
Events
Every event type Harpoon emits.
Event types
| Event | Triggered by |
|---|---|
transaction.completed | SUCCESS, PARTIAL, or OVERPAID |
transaction.failed | FAILED |
transaction.expired | EXPIRED |
transaction.refunded | REFUNDED |
transaction.cancelled | CANCELLED |
transaction.disputed | MANUAL_REVIEW |
SUCCESS, PARTIAL, and OVERPAID all share the transaction.completed event. Read data.status and data.difference to disambiguate.
Envelope
JSON
{
"event": "transaction.completed",
"webhook_id": "whk_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"timestamp": "2026-04-29T12:05:32Z",
"data": { /* event-specific */ }
}| Field | Description |
|---|---|
event | Event type |
webhook_id | Stable ID for this delivery. Use it as your idempotency key. |
timestamp | ISO 8601, UTC |
data | Transaction payload |
Payload (data)
JSON
{
"reference": "hpn_trx_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"hpn_code": "K7M3F6",
"status": "SUCCESS",
"expected_amount": "150.00",
"actual_amount": "150.00",
"difference": null,
"difference_type": "EXACT",
"currency": "GHS",
"payer_phone": "0244123456",
"telco_provider": "mtn",
"telco_transaction_id": "73012849466",
"meta": { "order_id": "1234" },
"client_reference": "order_1234"
}| Field | Type | Notes |
|---|---|---|
reference | string | Harpoon ID |
hpn_code | string | |
status | string | New state of the payment |
expected_amount | string | |
actual_amount | string | null | null for expired / cancelled |
difference | string | null | Signed: actual - expected |
difference_type | string | null | EXACT, UNDERPAID, or OVERPAID |
payer_phone | string | null | Display format 0XXXXXXXXX |
telco_provider | string | null | mtn, telecel, or at |
telco_transaction_id | string | null | |
meta | object | Whatever you sent on initialize |
client_reference | string | null | Whatever you sent on initialize |
expired, cancelled, failed, refunded, and disputed use the same shape with actual_amount, difference, difference_type, telco_* set to null where they don’t apply.