Issue Your First Card
This recipe walks through creating a customer, issuing a virtual USD card, funding it, and verifying the balance.
- 01Create customerCreate or select the customer eligible for a card.
- 02Check fundingConfirm the issuing wallet has the required balance.
- 03Issue and fundUse a stable reference for every value-changing request.
- 04Confirm final stateRefresh provider/card state before showing a balance.
Use a dashboard-issued sandbox API key on your server. Do not enter credentials into this documentation site.
The commands below use sandbox values. Replace the placeholder with a ps_pk_test_... key held only in your server environment. Switch both base URL and key together only after the go-live checklist.
Prerequisites
- A Payscribe business account with approved KYC Level 2
- Your API key (
ps_pk_test_...for sandbox,ps_pk_live_...for production)
Step 1: Create a customer
Every card is linked to a customer. Create one if you don't have a customer ID yet:
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/customers/create \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com",
"phone": "08012345678"
}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/customers/create', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com",
"phone": "08012345678"
}),
});
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'''{
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com",
"phone": "08012345678"
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/customers/create',
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(`{
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com",
"phone": "08012345678"
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/customers/create", 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": "Customer created successfully.",
"message": {
"details": {
"customer_id": "cus_abc123",
"name": "Jane Doe",
"email": "jane@example.com"
}
},
"status_code": 200
}
Save the customer_id — you'll need it in the next step.
Step 2: Fund your wallet
Cards are funded from your USD wallet. Check your balance first:
- cURL
- Node.js
- Python
- Go
curl -X GET https://sandbox.payscribe.ng/api/v1/my-account/balances \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/my-account/balances', {
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/my-account/balances',
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/my-account/balances", 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))
}
If your USD balance is low, you can:
- Convert NGN to USD via the FX endpoint
- Deposit USDT/USDC via stablecoin deposit
Step 3: Issue a virtual card
Create a virtual USD card for the customer with an initial top-up amount. The minimum top-up is $1.
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/cards/create \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cus_abc123",
"currency": "USD",
"type": "virtual",
"amount": 50
}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/cards/create', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"customer_id": "cus_abc123",
"currency": "USD",
"type": "virtual",
"amount": 50
}),
});
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'''{
"customer_id": "cus_abc123",
"currency": "USD",
"type": "virtual",
"amount": 50
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/cards/create',
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(`{
"customer_id": "cus_abc123",
"currency": "USD",
"type": "virtual",
"amount": 50
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/cards/create", 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": "Card created successfully.",
"message": {
"details": {
"event_id": "1e6a0e2b-6f1d-4f2a-9b3e-2c9a6d0f1a2b",
"event_type": "issuing.created.successful",
"trans_id": "3b2f9c7a-4d5e-4a1b-8c6d-9e0f1a2b3c4d",
"ref": "ref_def456",
"card": {
"id": "card_def456",
"card_type": "virtual",
"currency": "USD",
"brand": "VISA",
"name": "JANE DOE",
"first_six": "428852",
"last_four": "3456",
"masked": "428852 **** **** 3456",
"secure_details": {
"alg": "AES-256-GCM",
"iv": "base64-iv",
"tag": "base64-tag",
"data": "base64-ciphertext",
"aad": "bid:123|env:sandbox|card:card_def456"
},
"billing": {
"street": "220 KARAND",
"city": "Yugau",
"state": "JAWA",
"country": "ID",
"postal_code": "8299"
},
"created_at": "2026-07-29T09:15:00.000Z",
"updated_at": "2026-07-29T09:15:00.000Z"
},
"customer": {
"id": "cus_abc123",
"name": "JANE DOE"
}
}
},
"status_code": 201
}
Card number, CVV, and expiry are only available encrypted in card.secure_details (AES-256-GCM, decrypted with your business's Merchant Hash Key from Settings → API Keys) — they are never returned in plaintext.
What's happening:
- Your wallet is debited the top-up amount
- The card is issued and activated immediately
Step 4: Verify card balance
Check that the card was created and funded correctly:
- cURL
- Node.js
- Python
- Go
curl -X GET "https://sandbox.payscribe.ng/api/v1/cards/card_def456" \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/cards/card_def456', {
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/cards/card_def456',
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/cards/card_def456", 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))
}
Step 5: Top up the card
Add more funds later:
- cURL
- Node.js
- Python
- Go
curl -X PATCH https://sandbox.payscribe.ng/api/v1/cards/card_def456/topup \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"amount": 100}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/cards/card_def456/topup', {
method: 'PATCH',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"amount": 100
}),
});
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": 100
}''')
response = requests.patch(
'https://sandbox.payscribe.ng/api/v1/cards/card_def456/topup',
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": 100
}`)
request, err := http.NewRequest(http.MethodPatch, "https://sandbox.payscribe.ng/api/v1/cards/card_def456/topup", 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))
}
Step 6: Monitor card transactions
Subscribe to these webhook events in the dashboard:
| Event | When it fires |
|---|---|
cards.auth.approved | Card used at a merchant (authorization) |
cards.auth.settled | Merchant captured the authorized amount |
cards.adjusted.refund | Refund processed |
cards.auth.declined | Card declined (check reason in payload) |
Full example (Python)
import requests
BASE = "https://sandbox.payscribe.ng/api/v1"
KEY = "ps_pk_test_your_api_key"
HEADERS = {"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"}
# Step 1: Create customer
cust = requests.post(f"{BASE}/customers/create", headers=HEADERS, json={
"first_name": "Jane", "last_name": "Doe",
"email": "jane@example.com", "phone": "08012345678"
}).json()
cid = cust["message"]["details"]["customer_id"]
print(f"Customer: {cid}")
# Step 2: Issue card
card = requests.post(f"{BASE}/cards/create", headers=HEADERS, json={
"customer_id": cid, "currency": "USD", "type": "virtual", "amount": 50
}).json()
card_id = card["message"]["details"]["card"]["id"]
print(f"Card: {card_id}")
# Step 3: Top up
topup = requests.patch(f"{BASE}/cards/{card_id}/topup", headers=HEADERS, json={"amount": 100}).json()
print(f"Top-up — new balance: ${topup['message']['details']['card']['balance']}")
Next steps
Was this page helpful?