Setup
Configure the URLs we'll deliver payments to.
Requirements
- Open Collections enabled on your account (contact sales)
- Every SIM assigned to one of your numbers (below)
- A publicly reachable HTTPS endpoint
- A secret API key (
hpn_live_sk_*) with thewebhooks:managescope, or a dashboard session
Step 1: say which SIM each number is in
Set it in Payment methods, or on the number:
POST /v1/payment-methods
Content-Type: application/json
{
"provider": "momo_mtn",
"phone_number": "0201234567",
"account_name": "Adom Ventures",
"sim": { "device_id": "550e8400-e29b-41d4-a716-446655440000", "slot_index": 0 }
}PATCH /v1/payment-methods/{id} takes the same field. Omit it to leave the SIM alone, null to take the number out, a value to move it. Moving clears the old SIM; one that already holds another number is refused.
GET /v1/payment-methods/sims lists the phones and slots to choose from.
Endpoint creation is refused until every SIM has a number:
GET /v1/open-collections/lines{
"success": true,
"data": {
"ready": false,
"unplaced_payments": 3,
"phones": [
{
"device_id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Shop counter",
"reads_payments": true,
"sims": [
{
"slot_index": 0,
"network": "MTN",
"unplaced_payments": 0,
"line": {
"payment_method_id": "8f14e45f-ceea-467a-9d0f-2b6f0e4c4b71",
"phone_number": "0201234567",
"provider": "MTN MoMo",
"account_name": "Adom Ventures",
"merchant_code": "554120"
}
},
{ "slot_index": 1, "network": "Telecel", "unplaced_payments": 3, "line": null }
]
}
]
}
}ready is true when every SIM has a number. A phone that has never read a payment message does not hold it up.
unplaced_payments counts payments from the last 30 days that matched none of your numbers. Non-zero while ready is true usually means a SIM moved phones, and those payments went to your catch-all rule.
Step 2: create an endpoint
POST /v1/open-collections/endpoints
Authorization: Bearer hpn_live_sk_xxxxxxxxxxxxx
Content-Type: application/json
{
"url": "https://your-server.com/harpoon/payments",
"description": "Shop orders"
}{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://your-server.com/harpoon/payments",
"description": "Shop orders",
"is_active": true,
"is_sandbox": false,
"auto_paused_at": null,
"last_success_at": null,
"last_failure_at": null,
"consecutive_failures": 0,
"created_at": "2026-09-04T12:00:00Z",
"updated_at": "2026-09-04T12:00:00Z"
},
"secret": "whsec_xxxxxxxxxxxxxxxxxxxxx"
}The secret is returned here and on rotation only. Reveal it later from the dashboard.
A new endpoint takes any payment no other rule claims. Narrow it in Routing.
Set "is_sandbox": true for an endpoint that only receives test payments.
Step 3: test it
POST /v1/open-collections/endpoints/{id}/test
Authorization: Bearer hpn_live_sk_xxxxxxxxxxxxx{
"success": true,
"status_code": 200,
"response_body": "{"received": true}",
"message": "Endpoint accepted the test delivery."
}Same delivery path and signature as a real event, with made-up data and "test": true in the payload.