Reference

API reference

Every endpoint, object and convention of the Harbour API v1.

JSON in, JSON out, UTF-8. Money is always integer kobo; times are ISO 8601 in UTC. If you write PHP, Harbour Connect wraps all of this.

EnvironmentBase URL
Productionhttps://api.harbour.africa/v1
Staginghttps://api.staging.harbour.africa/v1
Localhttp://api.harbour.localhost:8080/v1 or http://localhost/harbour-api/v1

Authentication

HTTP
GET /v1/transactions?limit=25 HTTP/1.1
Host: api.harbour.africa
Authorization: Bearer hb_sk_live_your_key
Accept: application/json
CredentialFormatUsed by
Secret keyhb_sk_live_… / hb_sk_test_…Your server. Never a browser or an app.
Public keyhb_pk_live_… / hb_pk_test_…harbour.js, to create a checkout only.
User tokenhat_… access, hrt_… refreshThe Harbour mobile app and first-party clients.

Every response carries X-Harbour-Tenant: ten_…, and every list body repeats it as tenant_id. Keys are made in the dashboard under Settings → Developers; see Keys and modes.

Conventions

  • Idempotency. Every POST that creates something accepts Idempotency-Key (1 to 120 printable ASCII). The same key with the same body replays the first answer; with a different body it is 409 idempotency_conflict.
  • Pagination. Lists take limit (default 25, max 100) and cursor.
  • Errors. Always the same shape, with a plain-language message.
  • Modes. A key only sees its own mode's data, end to end.
JSON A list response
{
  "data": [ { "object": "transaction", "id": "txn_…" } ],
  "next_cursor": "eyJvIjoiMjAyNi0wOS0xM1QxNDozMjowN1oiLCJpIjoidHhuXzAxayJ9",
  "tenant_id": "ten_01m2e48qxzwqrwz0axj3r20jpv"
}
JSON An error response
{
  "error": {
    "code": "validation_failed",
    "message": "Amount must be at least ₦100.",
    "fields": { "amount_kobo": "Amount must be at least ₦100." }
  }
}

Charges

EndpointBody / queryAnswers
POST /v1/charges amount_kobo*, email*, name, phone, reference, description, return_url, provider (auto|paystack|flutterwave|opay), metadata 201 Checkout
GET /v1/charges/{reference} The Harbour HB-… reference Checkout, with transaction set once paid. Asks the provider when not yet final.
GET /v1/charges merchant_reference=order-1043 The charge for your own reference, or 404.
HTTP
POST /v1/charges
Authorization: Bearer hb_sk_live_…
Content-Type: application/json
Idempotency-Key: order-1043-attempt-1

{
  "amount_kobo": 1200000,
  "email": "funke@example.ng",
  "name": "Funke Adeyemi",
  "phone": "+2348031234567",
  "reference": "order-1043",
  "description": "Aso-ebi order, 12 yards",
  "return_url": "https://shop.example.ng/thanks",
  "provider": "auto",
  "metadata": { "order_id": "1043" }
}
JSON Checkout
{
  "object": "checkout",
  "id": "ct_01k5m0r3w9a2c8n7xq4v6t1b0d",
  "reference": "HB-88413",
  "merchant_reference": "order-1043",
  "status": "open",
  "amount_kobo": 1200000,
  "currency": "NGN",
  "checkout_url": "https://pay.harbour.africa/c/ct_01k5m0r3w9a2c8n7xq4v6t1b0d",
  "checkout_token": "ct_01k5m0r3w9a2c8n7xq4v6t1b0d",
  "provider": "paystack",
  "expires_at": "2026-09-13T15:32:07Z",
  "mode": "live",
  "transaction": null
}

Refunds

EndpointBodyAnswers
POST /v1/refundsreference*, amount_kobo (default: all of it), reason*202 Refund, status requested
GET /v1/refunds/{id}Refund
HTTP
POST /v1/refunds
{
  "reference": "HB-88402",
  "amount_kobo": 250000,
  "reason": "Two metres short on delivery"
}
→ 202 Accepted
{ "object": "refund", "id": "ref_…", "status": "requested", "amount_kobo": 250000 }

Refunds wait for an Owner to approve them; see Refunds.

EndpointBodyAnswers
POST /v1/payment-linksamount_kobo*, description*, customer, expires_in_days (1–90, default 7), provider201 Payment link
GET /v1/payment-links/{id}Payment link
HTTP
POST /v1/payment-links
{
  "amount_kobo": 1200000,
  "description": "Aso-ebi order, 12 yards",
  "customer": { "name": "Funke Adeyemi", "phone": "+2348031234567" },
  "expires_in_days": 7,
  "provider": "auto"
}
→ 201 Created
{ "object": "payment_link", "id": "lnk_…", "url": "https://pay.harbour.africa/okafor-textiles/k2m9x", "status": "active" }

Transactions

EndpointQuery
GET /v1/transactions status, provider, channel, from, to (ISO dates, local days), customer (email), limit, cursor
GET /v1/transactions/{id} A txn_… id or an HB-… reference

Objects

ObjectFields worth knowing
transactionid, reference, merchant_reference, type, status, provider, provider_reference, amount_kobo, fee_kobo, net_kobo, refunded_kobo, currency, channel, customer, failure_message, occurred_at, settled_at, settlement_id, recovered, metadata, mode
checkoutreference, status, checkout_url, checkout_token, amount_kobo, expires_at, transaction
refundid, reference, transaction_id, amount_kobo, reason, status
payment_linkid, url, amount_kobo, description, status, expires_at, transaction_id
settlementid, provider, status, expected_date, expected_amount_kobo, landed_amount_kobo, shortfall_kobo, fee_kobo, transaction_count, bank, landed_at

Statuses: a charge is initiatedpendingsucceededsettled, or failed; later it may become refunded or disputed. Channels are card, bank_transfer, ussd, qr, mobile_money and other.

Rate limits

  • 300 requests a minute per secret key.
  • 600 a minute per user token.
  • Over the limit is 429 rate_limited with Retry-After. The SDK waits and retries twice.

Webhook deliveries to your server are not rate limited by you; they retry at 1m, 5m, 30m, 2h, 6h and 12h for up to 24 hours. See Webhooks.