Skip to content

Developer documentation

bitPay REST API

Integrate checkout and wallet transfers into your application. All endpoints use JSON and require a dashboard access token plus project API keys.

Base URL: https://api.bitpaygh.com/api/v1

Overview

A typical integration flow:

  1. Create a BitPay account and a project with callback and webhook URLs.
  2. Generate a personal API access token under API access in your dashboard.
  3. Generate project API keys on the project's API keys page.
  4. Call POST /payments/initialize and redirect the customer to authorization_url.
  5. On return, call GET /payments/verify/{reference} or handle signed webhooks.

Supported currencies: GHS.

For wallet payouts to customers, see Transfers.

Authentication

Every payment request requires three credentials. Access tokens are created only from your dashboard — there is no public token API.

Header Description
Authorization Bearer {access_token} from dashboard → API access
X-Project-Public-Key Project public key (pk_…)
X-Project-Secret-Key Project secret key (sk_…)

Also send Accept: application/json and Content-Type: application/json on POST requests.

Create an account or sign in to generate credentials.

Initialize payment

POST https://api.bitpaygh.com/api/v1/payments/initialize

Creates a transaction and returns a checkout URL. Store data.reference for verify and webhooks.

Request body

JSON
{
  "amount": 100.00,
  "currency": "GHS",
  "email": "customer@example.com",
  "customer_name": "John Doe",
  "callback_url": "https://your-app.com/payments/callback",
  "metadata": {
    "order_id": "ORD-2026-001"
  }
}
FieldRequiredNotes
amountYesNumber > 0
currencyYesGHS
emailYesCustomer email
customer_nameNo
callback_urlNoOverrides project default; live requires HTTPS
metadataNoReturned in webhooks

Optional: Idempotency-Key — same key and body within 24h returns the original response.

Success response (201)

JSON
{
  "status": true,
  "message": "Payment initialized successfully",
  "data": {
    "reference": "PGW_TXN_01JXYZABCDEF123456",
    "authorization_url": "https://checkout.example.com/...",
    "access_code": "access_code_abc123",
    "amount": "100.00",
    "currency": "GHS",
    "status": "pending"
  }
}

Example

cURL
curl -X POST "https://api.bitpaygh.com/api/v1/payments/initialize" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "X-Project-Public-Key: pk_test_xxxxxxxx" \
  -H "X-Project-Secret-Key: sk_test_xxxxxxxx" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"amount":100,"currency":"GHS","email":"customer@example.com"}'

USSD/mobile money collection

POST https://api.bitpaygh.com/api/v1/payments/ussd/collect

Creates a mobile money payment request for USSD or server-side flows where the customer cannot be redirected to a checkout link. This endpoint does not return an authorization URL. Use the verify endpoint or webhooks to track the final status.

Required header: Idempotency-Key. Reusing the same key and body returns the original response.

Request body

JSON
{
  "amount": 50,
  "currency": "GHS",
  "customer_name": "John Doe",
  "customer_phone": "233597990630",
  "network": "MTN",
  "description": "Payment for order ORD-12345",
  "metadata": {
    "order_id": "ORD-12345"
  }
}
FieldRequiredNotes
amountYesNumber >= 0.01
currencyYesGHS only
customer_nameYesCustomer name shown in your records
customer_phoneYesMobile money number
networkYesMTN, AT, or TELECEL
descriptionNoCustomer-facing payment description
metadataNoReturned in webhooks

Success response (201)

JSON
{
  "status": true,
  "message": "Payment request created successfully",
  "data": {
    "reference": "PGW_TXN_01JXYZABCDEF123456",
    "status": "pending",
    "amount": 50,
    "currency": "GHS",
    "customer": {
      "name": "John Doe",
      "phone": "233597990630"
    },
    "network": "MTN",
    "instructions": "Complete the payment prompt on your phone."
  }
}

Example

cURL
curl -X POST "https://api.bitpaygh.com/api/v1/payments/ussd/collect" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "X-Project-Public-Key: pk_test_xxxxxxxx" \
  -H "X-Project-Secret-Key: sk_test_xxxxxxxx" \
  -H "Idempotency-Key: unique-request-key" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"amount":50,"currency":"GHS","customer_name":"John Doe","customer_phone":"233597990630","network":"MTN"}'

Verify payment

GET https://api.bitpaygh.com/api/v1/payments/verify/{reference}

Confirms status with the gateway and updates the transaction. Use on your callback page; prefer webhooks for server-side finality.

Success response (200)

JSON
{
  "status": true,
  "message": "Payment verified successfully",
  "data": {
    "reference": "PGW_TXN_01JXYZABCDEF123456",
    "status": "success",
    "amount": "100.00",
    "currency": "GHS",
    "fee": "2.50",
    "net_amount": "97.50",
    "paid_at": "2026-05-16T14:30:00+00:00",
    "customer": {
      "email": "customer@example.com",
      "name": "John Doe"
    }
  }
}

data.status may also be pending, failed, or abandoned.

Transfers

Send money from your project wallet to a customer bank account or mobile money wallet. Transfers are processed automatically — no manual approval is required. The recipient receives the full requested amount; the transfer fee is charged separately from your wallet (total_debit = amount + fee).

Typical flow: list supported providers → create a recipient → create a transfer (optionally confirm OTP) → poll status or handle webhooks.

List supported banks and providers

GET https://api.bitpaygh.com/api/v1/transfers/banks

Optional query: type=bank or type=mobile_money.

Success response (200)
{
  "status": true,
  "message": "Supported banks retrieved.",
  "data": {
    "banks": [
      {
        "name": "GT Bank",
        "code": "058",
        "type": "bank",
        "currency": "GHS"
      }
    ]
  }
}

Create transfer recipient

POST https://api.bitpaygh.com/api/v1/transfers/recipients

Validates the account with BitPay and returns a TRP_ reference. Store this reference for transfer requests.

Request body
{
  "recipient_type": "bank",
  "account_number": "0123456789",
  "account_name": "John Doe",
  "bank_code": "058",
  "currency": "GHS"
}
FieldRequiredNotes
recipient_typeYesbank or mobile_money
account_numberYesRecipient account or wallet number
bank_codeYesCode from GET /transfers/banks
account_nameNoResolved automatically when possible
currencyNoDefaults to GHS
Success response (201)
{
  "status": true,
  "message": "Transfer recipient created.",
  "data": {
    "reference": "TRP_01JXYZABCDEF123456",
    "recipient_type": "bank",
    "account_name": "JOHN DOE",
    "account_number": "••••••6789",
    "bank_code": "058",
    "bank_name": "GT Bank",
    "currency": "GHS",
    "status": "verified",
    "verified_at": "2026-06-15T12:00:00+00:00",
    "created_at": "2026-06-15T12:00:00+00:00"
  }
}

List transfer recipients

GET https://api.bitpaygh.com/api/v1/transfers/recipients

Optional query: per_page (max 100, default 25).

Create transfer

POST https://api.bitpaygh.com/api/v1/transfers

Debits your project wallet by total_debit and sends amount to the recipient. If OTP is enabled on the project, the transfer enters awaiting_otp until confirmed.

Request body
{
  "recipient": "TRP_01JXYZABCDEF123456",
  "amount": 100.00,
  "currency": "GHS",
  "metadata": {
    "payout_id": "PAY-2026-001"
  }
}
FieldRequiredNotes
recipientYesTRP_ reference owned by this project
amountYesNumber > 0; amount sent to recipient
currencyNoGHS only
metadataNoReturned in webhooks

Optional: Idempotency-Key — same key and body within 24h returns the original response; same key with a different body returns 409.

Success response (201)
{
  "status": true,
  "message": "Transfer created.",
  "data": {
    "reference": "TRF_01JXYZABCDEF123456",
    "amount": "100.00",
    "fee": "8.50",
    "total_debit": "108.50",
    "currency": "GHS",
    "status": "processing",
    "requires_otp": false,
    "otp_verified_at": null,
    "failure_reason": null,
    "recipient": {
      "reference": "TRP_01JXYZABCDEF123456",
      "recipient_type": "bank",
      "account_name": "JOHN DOE",
      "account_number": "••••••6789",
      "bank_name": "GT Bank"
    },
    "initiated_at": "2026-06-15T12:05:00+00:00",
    "processed_at": "2026-06-15T12:05:01+00:00",
    "completed_at": null,
    "created_at": "2026-06-15T12:05:00+00:00"
  }
}

Status values: pending, awaiting_otp, processing, success, failed, reversed, cancelled.

Confirm transfer OTP

POST https://api.bitpaygh.com/api/v1/transfers/{reference}/confirm-otp

Required when the project has Require OTP for transfers enabled. A one-time code is sent to the project owner by email (and SMS when configured).

Request body
{
  "otp": "123456"
}
Success response (200)
{
  "status": true,
  "message": "Transfer confirmed and queued for processing.",
  "data": {
    "reference": "TRF_01JXYZABCDEF123456",
    "status": "pending",
    "otp_verified_at": "2026-06-15T12:06:00+00:00"
  }
}

Get transfer

GET https://api.bitpaygh.com/api/v1/transfers/{reference}

Returns the same transfer object shape as the create response.

Transfer webhooks

When your project webhook URL is set, BitPay sends signed POST requests for transfer lifecycle events using the same headers as payment webhooks.

  • transfer.pending
  • transfer.awaiting_otp
  • transfer.processing
  • transfer.success
  • transfer.failed
  • transfer.reversed
Payload example
{
  "event": "transfer.success",
  "data": {
    "reference": "TRF_01JXYZABCDEF123456",
    "status": "success",
    "amount": "100.00",
    "fee": "8.50",
    "total_debit": "108.50",
    "currency": "GHS",
    "recipient": {
      "reference": "TRP_01JXYZABCDEF123456",
      "type": "bank",
      "account_name": "JOHN DOE",
      "account_number": "••••••6789",
      "bank_name": "GT Bank"
    },
    "failure_reason": null,
    "metadata": {
      "payout_id": "PAY-2026-001"
    },
    "completed_at": "2026-06-15T12:05:30+00:00"
  }
}

Example

cURL
curl -X POST "https://api.bitpaygh.com/api/v1/transfers" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "X-Project-Public-Key: pk_test_xxxxxxxx" \
  -H "X-Project-Secret-Key: sk_test_xxxxxxxx" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: transfer-ord-001" \
  -d '{"recipient":"TRP_01JXYZABCDEF123456","amount":100,"currency":"GHS"}'

Webhooks

Set a webhook URL on your project. BitPay sends a signed POST when a payment or transfer reaches a notable state.

Payment events

  • payment.success
  • payment.failed
  • payment.abandoned
  • payment.reversed
  • payment.refunded
  • payment.disputed

Transfer events

  • transfer.pending
  • transfer.awaiting_otp
  • transfer.processing
  • transfer.success
  • transfer.failed
  • transfer.reversed

See Transfers for request/response shapes and a transfer webhook payload example.

Headers

  • X-Platform-Event — event name
  • X-Platform-Timestamp — Unix timestamp
  • X-Platform-Signature — HMAC-SHA256 hex digest

Verify with your project webhook signing secret (whsec_… from the project dashboard): signature = HMAC_SHA256(secret, timestamp + "." + raw_json_body)

Payload example

JSON
{
  "event": "payment.success",
  "data": {
    "reference": "PGW_TXN_01JXYZABCDEF123456",
    "status": "success",
    "amount": "120.00",
    "currency": "GHS",
    "fee": "6.00",
    "net_amount": "114.00",
    "customer": {
      "email": "customer@example.com",
      "name": "John Doe"
    },
    "metadata": {
      "order_id": "ORD-2026-001"
    },
    "paid_at": "2026-05-16T14:30:00+00:00"
  }
}

Respond with 2xx quickly. Failed deliveries are retried with backoff.

Errors

Errors return status: false and an HTTP status code.

Validation (422)
{
  "status": false,
  "message": "The given data was invalid.",
  "errors": {
    "amount": ["The amount field is required."]
  }
}
HTTPMeaning
401Missing or invalid authentication
403No project access or inactive business/project
404Resource not found (e.g. payment or transfer reference)
409Idempotency conflict
422Validation failed
429Rate limit exceeded
502Payment gateway error