Button Checkout
Host a Payscribe checkout canvas on your site with a small embeddable button, and create each checkout session from your own server.
This page covers the server-to-server session endpoint. The canvas itself is hosted and browser-facing — you only need this one call to start a checkout.
Create Checkout Session
Create a Button payment session in the background and receive a hosted canvas URL to open for your customer.
This is a live-only endpoint — call it against api.payscribe.ng; there is no sandbox counterpart.
It is authenticated with your public key (ps_pk_live_...), and your server's IP address must be on the business IP whitelist (Settings > API Keys > IP Whitelist).
Headers
| Header | Value |
|---|---|
Authorization | Bearer $PAYSCRIBE_PUBLIC_KEY |
Content-Type | application/json |
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
amount | integer | No | Amount in naira, not kobo. Omit to let the customer enter the amount inside checkout. |
currency | string | No | One of NGN, USD, GBP, EUR, KES, GHS. Default NGN. |
reference | string | No | Your reference. A generated btn_... reference is used when omitted. |
email | string | No | Customer email, pre-filled in checkout. |
name | string | No | Customer name. |
phone | string | No | Customer phone. |
successUrl | string | No | Where the customer is redirected after a successful payment. Falls back to your Button widget setting. |
cancelUrl | string | No | Where the customer is redirected if they cancel. Falls back to your Button widget setting. |
origin | string | No | The checkout origin you configured for this environment. Required when an origin allowlist is set. |
service | string | No | Underlying product fulfilled after payment (e.g. pay). Defaults to pay. |
fundingSource | string | No | Who funds the fulfilment: customer or float. Defaults to your widget setting (or customer). |
metadata | object | No | Free-form data attached to the session. |
Request
POST /button/init
- cURL
- Node.js
- Python
- Go
curl -X POST https://api.payscribe.ng/api/v1/button/init \
-H "Authorization: Bearer $PAYSCRIBE_PUBLIC_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 5000,
"currency": "NGN",
"reference": "btn_order_123",
"email": "customer@example.com",
"name": "John Doe",
"successUrl": "https://example.com/success",
"service": "pay"
}'
const response = await fetch('https://api.payscribe.ng/api/v1/button/init', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_PUBLIC_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"amount": 5000,
"currency": "NGN",
"reference": "btn_order_123",
"email": "customer@example.com",
"name": "John Doe",
"successUrl": "https://example.com/success",
"service": "pay"
}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"amount": 5000,
"currency": "NGN",
"reference": "btn_order_123",
"email": "customer@example.com",
"name": "John Doe",
"successUrl": "https://example.com/success",
"service": "pay"
}''')
response = requests.post(
'https://api.payscribe.ng/api/v1/button/init',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_PUBLIC_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := bytes.NewBufferString(`{"amount":5000,"currency":"NGN","reference":"btn_order_123","email":"customer@example.com","name":"John Doe","successUrl":"https://example.com/success","service":"pay"}`)
request, err := http.NewRequest(http.MethodPost, "https://api.payscribe.ng/api/v1/button/init", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_PUBLIC_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
200 OK
{
"status": true,
"data": {
"token": "btn_0a1b2c3d4e5f67890abcdef1",
"canvas_url": "https://links.payscribe.co/canvas?token=btn_0a1b2c3d4e5f67890abcdef1",
"expires_at": "2026-09-18 12:00:00"
}
}
Open canvas_url in the customer's browser (or load the Button widget script, which reads the same token). The session expires 30 minutes after creation.
Note this endpoint returns its own shape {status, data} rather than the standard envelope. Errors return {status: false, description, status_code} — 400 for an invalid fundingSource, amount, or currency, and 403 when your server IP is not whitelisted or origin is not on the allowlist.
What happens next
After the customer completes checkout you'll receive Button webhook events you can listen for:
| Event | Meaning |
|---|---|
button.session.created | Session created (dispatched when you call /button/init). |
button.session.succeeded | Payment collected for the session. |
button.session.failed | Payment or session failed. |
button.service.fulfillment.started | Underlying service fulfilment started. |
button.service.fulfillment.succeeded | Underlying service fulfilment completed. |
button.service.fulfillment.failed | Underlying service fulfilment failed. |
Configure your webhook URL on the dashboard and verify event signatures the same way as other Payscribe webhooks.
Was this page helpful?