Direct Integration

If you have your own checkout or interface and want to do direct API integration, you can call the endpoints below to initiate a transfer request to the Pocket user. This does not require the SDK. Two variants are available depending on whether you authenticate with a secret key (server-side) or a public key (browser / checkout flow). They share the same request body and response pattern.

Initiate (API key)

POST/api/v1/pocket/transfer/initiate

Server-side initiation using your secret key. Use this when your backend is calling Piggyvest Business directly.

Request Parameters

NameTypeRequiredDescription
tagstringYesPocket user's tag.
amountnumberYesAmount in kobo. Minimum 5000 kobo.
narrationstringYesDescription of the payment request.
referencestringYesYour unique reference (short-lived deduplication on PVB).
wallet_idstringNoDestination PVB wallet (must be an Expense wallet). If omitted, the primary expense wallet is used.
product_namestringNoDisplay label / your application name or what the user is paying for.
metaobjectNoOpaque metadata. Returned back to you via webhooks.
simulate_forstringNo"success" or "failure" — non-production only. Forces the outcome in sandbox.
1
cURL Example
(cURL)
curl -X POST "https://api.piggyvest.business/api/v1/pocket/transfer/initiate" \
-H "Authorization: Bearer YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"tag": "mezigaboshi",
"amount": 500000,
"narration": "subscription Fee",
"reference": "your-unique-reference",
"product_name": "my-app",
"wallet_id": "6d086bce-58ea-46d0-b77e-e1c398d6de4d"
}'
2
Node.js Example
(JavaScript)
import axios from 'axios'
const secretKey = process.env.PVB_SECRET_KEY
const url = `${process.env.PVB_BASE_URL}/api/v1/pocket/transfer/initiate`
const payload = {
tag: "mazigraphql",
amount: 500000,
narration: "Pay with pocket test",
reference: "your-unique-reference",
product_name: "my-app",
wallet_id: "0b86bccc-5d6a-45a9-b27c-a2e86864e84d",
meta: {} // Anything you want to get back via webhooks
}
await axios.post(url, payload, {
headers: {
'Authorization': `Bearer ${secretKey}`,
},
})

Response

Unlike the rest of the API, the initiate endpoint returns the response directly — it is not wrapped in the standard status / message / data envelope. Additional fields may appear inside qr_data from the Pocket provider.

Initiate Response
(JSON)
{
"status": "<from-pocket-provider>",
"data": {
"response_code": "string",
"qr_data": {
"reference": "string"
}
}
}
Sandbox Note: In sandbox mode you cannot go to the Pocket app to accept or reject a payment after calling this endpoint. We internally simulate the success case. If you want to simulate an instance where the user rejects the request, add the key "simulate_for": "failure" to the request body to receive a failed webhook.

Initiate (checkout / public key)

POST/api/v1/pocket/transfer/initiate-checkout

Public-key initiation for browser-based checkout flows. The request body and response pattern are identical to the API-key variant above — only the URL and the authorization key change. Reuse the request body table and response shape from the section above; the difference is that the request is authorized with your YOUR_PUBLIC_KEY instead of a secret key.

1
cURL Example (public key)
(cURL)
curl -X POST "https://api.piggyvest.business/api/v1/pocket/transfer/initiate-checkout" \
-H "Authorization: Bearer YOUR_PUBLIC_KEY" \
-H "Content-Type: application/json" \
-d '{
"tag": "mezigaboshi",
"amount": 500000,
"narration": "subscription Fee",
"reference": "your-unique-reference",
"product_name": "my-app",
"wallet_id": "6d086bce-58ea-46d0-b77e-e1c398d6de4d"
}'

Webhook Structure

After the Pocket user responds to the payment request, a webhook will be sent to your configured webhook URL.

Success Webhook

When the Pocket user accepts the payment, they will be debited and your destination account will be credited. A webhook with the following structure is sent:

pocket-transfer.inflow.success
(JSON)
{
"eventId": "01JBXTNTHH9Y5NY01TVX6AJSQJ",
"customer_id": "55b71d43-2efa-4ad8-ab67-2addabaee1ad",
"eventType": "pocket-transfer.inflow.success",
"eventCategory": "inflow_transaction",
"eventData": {
"id": "183d87ab-5714-4c3b-b71e-81fb93f0c4c3",
"customer_id": "55b71d43-2efa-4ad8-ab67-2addabaee1ad",
"destination_wallet_id": "5fa805a5-f228-4cac-8d9f-b9b9c20e3fdd",
"type": "inter",
"category": "pocket_inflow",
"amount": 32000,
"currency": "NGN",
"narration": "Sent by mazigraphql",
"transaction_id": "b2ddaea4-2692-4394-af43-d0f6e882a6b4",
"timestamp": "2024-11-05T09:32:14.162Z",
"internal_reference": "01JBXTNS79TAQT3W0D09D148C0",
"status": "COMPLETED",
"provider": "pocket",
"third_party_reference": "pvfb_6c885641-b4e6-4431-a680-65fcefb223631730799101384",
"fee": 1.50,
"destination_wallet_balance": "741015",
"destination_wallet_ledger_balance": "741015"
},
"pvb_reference": "pvfb_6c885641-b4e6-4431-a680-65fcefb223631730799101384",
"pvb_wallet": "f335f21e-6e8e-43d5-8211-b7aa08a5fba5",
"pvb_destination_wallet": null,
"pvb_third_party_reference": "your-reference-when-instantiating-transfer",
"pvb_schedule_payment_id": null,
"pvb_meta": {}
}

Failure Webhook

If the Pocket user rejects the request, the following webhook is sent:

pocket-transfer.inflow.failed
(JSON)
{
"eventId": "01JB4WA9AJ457T4QT0VPCNXJQ5",
"customer_id": "c096507d-dc32-45d2-9c01-871a27abfd10",
"eventType": "pocket-transfer.inflow.failed",
"eventCategory": "inflow_transaction",
"eventData": {
"destination_wallet": "df5ec9e3-efa9-459c-b0b6-d88f9288b0ba",
"customer_id": "c096507d-dc32-45d2-9c01-871a27abfd10",
"amount": 400000,
"currency": "NGN",
"type": "inter",
"category": "pocket_inflow",
"status": "failed",
"service_type": "INSTANT_TRANSFER",
"initiator_reference": "pvfb_e0dc922d-4901-4708-acc8-fb156c0dce3c1729961447464",
"narration": "Pay with pocket"
},
"pvb_reference": "pvfb_e0dc922d-4901-4708-acc8-fb156c0dce3c1729961447464",
"pvb_wallet": "f335f21e-6e8e-43d5-8211-b7aa08a5fba5",
"pvb_destination_wallet": null,
"pvb_third_party_reference": "your-reference-when-instantiating-transfer",
"pvb_schedule_payment_id": null,
"pvb_meta": {}
}

Webhook Signature Validation

To validate that a webhook payload came from us, we send an x-pvb-signature in the request headers. Below is a sample Node.js Express snippet to validate the signature using HMAC SHA-512:

Signature Validation (Node.js)
(JavaScript)
import crypto from 'crypto'
const secretKey = process.env.PVB_SECRET_KEY
router.post('/my-webhook-url', function (request, response) {
const hash = crypto
.createHmac('sha512', secretKey)
.update(JSON.stringify(request.body))
.digest('hex')
if (hash !== request.headers['x-pvb-signature']) {
// Invalid signature — return early
return response.sendStatus(200);
}
// Validated — process the webhook
response.sendStatus(200);
})