Payment Links
Create hosted payment pages you can share with customers. Accept cards, bank transfers, and crypto.
Create Payment Link
POST /links
Headers
| Header | Value |
|---|---|
Authorization | Bearer $PAYSCRIBE_API_KEY |
Content-Type | application/json |
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
amount | number | No | Fixed amount. Omit it for a customer-entered amount. |
currency | string | Yes | NGN, USD |
title | string | Yes | Payment title |
description | string | No | Payment description |
redirect | string | No | URL to redirect after payment |
success_message | string | No | Message shown on the hosted page after payment (default: "Thank you for your payment.") |
slug | string | No | A custom slug for the hosted URL. Must be unique per business; a random slug is generated if omitted. |
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/links \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 15000,
"currency": "NGN",
"title": "Payment for Invoice #123",
"description": "Web development services",
"redirect": "https://example.com/thank-you"
}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/links', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"amount": 15000,
"currency": "NGN",
"title": "Payment for Invoice #123",
"description": "Web development services",
"redirect": "https://example.com/thank-you"
}),
});
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": 15000,
"currency": "NGN",
"title": "Payment for Invoice #123",
"description": "Web development services",
"redirect": "https://example.com/thank-you"
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/links',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"amount": 15000,
"currency": "NGN",
"title": "Payment for Invoice #123",
"description": "Web development services",
"redirect": "https://example.com/thank-you"
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/links", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_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
{
"status": true,
"description": "Payment link created",
"message": {
"details": {
"id": "link_abc123",
"title": "Payment for Invoice #123",
"slug": "a1b2c3d4",
"full_url": "https://links.payscribe.co/inv/a1b2c3d4",
"amount": 15000,
"currency": "NGN",
"status": "active"
}
},
"status_code": 200
}
The create/update responses do not echo description, redirect, or success_message back — fetch the link with Get Payment Link to confirm what was saved.
List Payment Links
GET /links
Headers
| Header | Value |
|---|---|
Authorization | Bearer $PAYSCRIBE_API_KEY |
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
page | integer | No | Page number (default: 1) |
per_page | integer | No | Results per page (default: 20) |
status | string | No | Filter by active or inactive |
q | string | No | Search title, slug, or link ID |
- cURL
- Node.js
- Python
- Go
curl -X GET "https://sandbox.payscribe.ng/api/v1/links?page=1&per_page=20" \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/links?page=1&per_page=20', {
method: 'GET',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
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
response = requests.get(
'https://sandbox.payscribe.ng/api/v1/links?page=1&per_page=20',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodGet, "https://sandbox.payscribe.ng/api/v1/links?page=1&per_page=20", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
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
{
"status": true,
"description": "Payment links fetched.",
"message": {
"details": {
"links": [
{
"id": "link_abc123",
"title": "Payment for Invoice #123",
"slug": "a1b2c3d4",
"url": "https://links.payscribe.co/inv/a1b2c3d4",
"amount": 15000,
"currency": "NGN",
"status": "active",
"created_at": "2026-07-20 12:00:00"
},
{
"id": "link_def456",
"title": "Donation",
"slug": "e5f6g7h8",
"url": "https://links.payscribe.co/inv/e5f6g7h8",
"amount": null,
"currency": "NGN",
"status": "active",
"created_at": "2026-07-19 10:30:00"
}
],
"total": 2,
"page": 1,
"per_page": 20
}
},
"status_code": 200
}
The list response does not include description or a paid flag per link — fetch the individual link with Get Payment Link for those. Note the list item's URL field is url (not full_url, which is what create/update return).
Get Payment Link
GET /links/{id}
Headers
| Header | Value |
|---|---|
Authorization | Bearer $PAYSCRIBE_API_KEY |
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Payment link ID |
- cURL
- Node.js
- Python
- Go
curl -X GET https://sandbox.payscribe.ng/api/v1/links/link_abc123 \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/links/link_abc123', {
method: 'GET',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
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
response = requests.get(
'https://sandbox.payscribe.ng/api/v1/links/link_abc123',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodGet, "https://sandbox.payscribe.ng/api/v1/links/link_abc123", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
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
{
"status": true,
"description": "Links details fetched.",
"message": {
"details": {
"id": "link_abc123",
"title": "Payment for Invoice #123",
"description": "Web development services",
"amount": 15000,
"slug": "a1b2c3d4",
"currency": "NGN",
"success_message": "Thank you for your payment.",
"redirect_url": "https://example.com/thank-you",
"created_at": "2026-07-20 12:00:00",
"updated_at": "2026-07-20 12:00:00",
"status": "active",
"business": {
"id": "b_abc123",
"name": "Acme Ltd",
"business_logo": "https://res.cloudinary.com/.../logo.png"
},
"payments": {
"wallets": {
"is_enabled": true,
"mode": {
"payscribe": {"title": "Pay with your Payscribe wallet", "meta": []},
"palmpay": {"title": "Connect and pay with your Palmpay wallet", "meta": []}
}
},
"ussd": {"is_enabled": true, "mode": []},
"bank_transfer": {"is_enabled": true, "mode": []}
}
}
},
"status_code": 200
}
This response does not include a url/full_url field or a paid/paid_at flag — build the hosted URL from slug (https://links.payscribe.co/inv/{slug}), and use List Payment Links or Webhooks to know whether a link has been paid.
Update Payment Link
PATCH /links/{id}
Headers
| Header | Value |
|---|---|
Authorization | Bearer $PAYSCRIBE_API_KEY |
Content-Type | application/json |
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Payment link ID |
amount | number | No | Fixed amount; omit for a customer-entered amount |
title | string | Conditional | Required with description and currency for a content update |
description | string | Conditional | Required with title and currency for a content update |
currency | string | Conditional | Required with title and description for a content update |
redirect | string | No | URL to redirect after payment |
status | string | No | Use active or inactive. It may be updated on its own. |
A content update is not a partial patch: send title, description, and currency together. A status-only update may send just
status.
- cURL
- Node.js
- Python
- Go
curl -X PATCH https://sandbox.payscribe.ng/api/v1/links/link_abc123 \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"amount": 20000, "title": "Updated payment", "description": "Updated August invoice payment", "currency": "NGN"}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/links/link_abc123', {
method: 'PATCH',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"amount": 20000,
"title": "Updated payment",
"description": "Updated August invoice payment",
"currency": "NGN"
}),
});
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": 20000,
"title": "Updated payment",
"description": "Updated August invoice payment",
"currency": "NGN"
}''')
response = requests.patch(
'https://sandbox.payscribe.ng/api/v1/links/link_abc123',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"amount": 20000,
"title": "Updated payment",
"description": "Updated August invoice payment",
"currency": "NGN"
}`)
request, err := http.NewRequest(http.MethodPatch, "https://sandbox.payscribe.ng/api/v1/links/link_abc123", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_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
{
"status": true,
"description": "Payment link updated successfully.",
"message": {
"details": {
"id": "link_abc123",
"title": "Updated payment",
"slug": "a1b2c3d4",
"full_url": "https://links.payscribe.co/inv/a1b2c3d4",
"amount": 20000,
"currency": "NGN",
"status": "active"
}
},
"status_code": 200
}
A status-only update ({"status": "inactive"}) returns "description": "Payment link status updated successfully." with "message": {"details": []} — it does not echo the link object back.
Delete Payment Link
DELETE /links/{id}
Headers
| Header | Value |
|---|---|
Authorization | Bearer $PAYSCRIBE_API_KEY |
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Payment link ID |
- cURL
- Node.js
- Python
- Go
curl -X DELETE https://sandbox.payscribe.ng/api/v1/links/link_abc123 \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/links/link_abc123', {
method: 'DELETE',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
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
response = requests.delete(
'https://sandbox.payscribe.ng/api/v1/links/link_abc123',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodDelete, "https://sandbox.payscribe.ng/api/v1/links/link_abc123", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
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,
"description": "Payment link deleted.",
"message": {
"details": []
},
"status_code": 200
}
Webhooks
| Event | Description |
|---|---|
payment_link.paid | Payment link has been paid |
Was this page helpful?