The "Transaction not found" error on /transaction/verify/:reference means Paystack cannot find a transaction matching the reference you sent, in the environment your API key belongs to. The most common cause is a test/live key mismatch: you initialized the transaction with a test key but are verifying with a live key (or vice versa). Other causes include a typo in the reference, URL-encoded special characters, using the transaction ID instead of the reference string, or the transaction being too old.
When you call the verify endpoint with a reference Paystack cannot find, you get:
GET /transaction/verify/my_ref_123
Authorization: Bearer sk_live_xxxxx
HTTP/1.1 404 Not Found
{
"status": false,
"message": "Transaction reference not found"
}
Some developers also see this as "Transaction not found" or "Invalid transaction reference" depending on the exact scenario. The HTTP status is 404 in most cases.
This error is frustrating because you know the transaction happened. The customer was charged. But your verification call returns not found. The transaction exists, you just cannot find it with the parameters you are using.
This is the most common cause. Your initialization call and your verification call use keys from different environments.
The scenario:
// Initialization (happens during checkout)
// Uses test key
const initResponse = await fetch('https://api.paystack.co/transaction/initialize', {
method: 'POST',
headers: {
Authorization: 'Bearer sk_test_abc123', // TEST key
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: 'customer@example.com',
amount: 50000,
}),
});
// ... customer pays ...
// Verification (happens after payment)
// WRONG: Uses live key
const verifyResponse = await fetch(
'https://api.paystack.co/transaction/verify/ref_from_test',
{
headers: {
Authorization: 'Bearer sk_live_xyz789', // LIVE key - WRONG!
},
}
);
// Result: "Transaction not found"
The fix:
// Use the SAME key for initialization and verification
const PAYSTACK_SECRET_KEY = process.env.PAYSTACK_SECRET_KEY;
// Both calls use the same key
// Initialize
const initResponse = await fetch('https://api.paystack.co/transaction/initialize', {
method: 'POST',
headers: {
Authorization: `Bearer ${PAYSTACK_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, amount }),
});
// Verify
const verifyResponse = await fetch(
`https://api.paystack.co/transaction/verify/${reference}`,
{
headers: {
Authorization: `Bearer ${PAYSTACK_SECRET_KEY}`,
},
}
);
Use a single environment variable for the key. In development, set it to your test key. In production, set it to your live key. Never hard-code keys.
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
The reference you pass to verify does not match the reference used when the transaction was created.
This happens when:
The fix: Store the reference in your database at initialization time, and read it back at verification time.
const crypto = require('crypto');
// Generate a reference
function generateReference() {
return 'txn_' + crypto.randomBytes(16).toString('hex');
}
// At initialization time
async function initializePayment(email, amount) {
const reference = generateReference();
// Store in database BEFORE calling Paystack
await db.query(
'INSERT INTO payments (reference, email, amount, status) VALUES ($1, $2, $3, $4)',
[reference, email, amount, 'pending']
);
const response = await fetch('https://api.paystack.co/transaction/initialize', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, amount, reference }),
});
return response.json();
}
// At verification time
async function verifyPayment(reference) {
// Check that this reference exists in YOUR database first
const payment = await db.query(
'SELECT * FROM payments WHERE reference = $1',
[reference]
);
if (!payment.rows.length) {
throw new Error('Unknown reference: ' + reference);
}
const response = await fetch(
`https://api.paystack.co/transaction/verify/${encodeURIComponent(reference)}`,
{
headers: {
Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
},
}
);
return response.json();
}
If your reference contains special characters, they must be URL-encoded in the verify URL path. Without encoding, the URL breaks and Paystack cannot parse the reference.
Characters that cause problems:
| Character | URL encoded | Example reference | Correct URL |
|---|---|---|---|
| # | %23 | order#123 | /transaction/verify/order%23123 |
| / | %2F | 2026/07/001 | /transaction/verify/2026%2F07%2F001 |
| + | %2B | ref+extra | /transaction/verify/ref%2Bextra |
| space | %20 | ref 001 | /transaction/verify/ref%20001 |
| & | %26 | a&b | /transaction/verify/a%26b |
The fix: Always use encodeURIComponent() on the reference when building the URL:
// WRONG: special characters break the URL
const url = `https://api.paystack.co/transaction/verify/${reference}`;
// RIGHT: encode the reference
const url = `https://api.paystack.co/transaction/verify/${encodeURIComponent(reference)}`;
Better yet, avoid special characters in references entirely. Stick to alphanumeric characters, hyphens, and underscores:
// Safe reference format
function generateSafeReference() {
return 'txn_' + crypto.randomBytes(16).toString('hex');
// Example: txn_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6
}
The verify endpoint takes the transaction reference (a string you provide or Paystack generates), not the transaction ID (a numeric value Paystack assigns).
// When you initialize a transaction, Paystack returns:
{
"status": true,
"data": {
"authorization_url": "https://checkout.paystack.com/abc123",
"access_code": "abc123",
"reference": "my_custom_ref_001" // <-- THIS is what you use for verify
}
}
// A webhook event includes:
{
"event": "charge.success",
"data": {
"id": 987654321, // <-- This is the ID. Do NOT use this for verify.
"reference": "my_custom_ref_001", // <-- Use THIS for verify.
"amount": 50000
}
}
// WRONG: using the numeric ID
const url = 'https://api.paystack.co/transaction/verify/987654321';
// Returns: "Transaction not found"
// RIGHT: using the reference string
const url = 'https://api.paystack.co/transaction/verify/my_custom_ref_001';
// Returns: the transaction details
If you only have the transaction ID and need to look up the transaction, use the /transaction/:id endpoint instead (GET request with your secret key). But for the standard verify flow, always use the reference.
When you initialize a transaction, Paystack creates a record. If the customer never completes payment and the transaction expires, the behavior depends on the transaction state:
"abandoned".If you get "Transaction not found" for a recent transaction, the cause is almost certainly one of the other issues on this page (key mismatch, wrong reference, encoding). Expiration only matters for very old transactions.
Prevention: Verify transactions as soon as possible after payment. Do not wait days or weeks to verify. Your webhook handler should verify and process immediately when charge.success arrives.
When you get "Transaction not found", work through this checklist:
sk_test_ or sk_live_? Does it match the key you used during initialization?async function verifyWithDebug(reference) {
const key = process.env.PAYSTACK_SECRET_KEY;
console.log('Verifying reference:', JSON.stringify(reference));
console.log('Key prefix:', key.substring(0, 12));
console.log('Reference type:', typeof reference);
console.log('Reference length:', reference.length);
const url = `https://api.paystack.co/transaction/verify/${encodeURIComponent(reference)}`;
console.log('Full URL:', url);
const response = await fetch(url, {
headers: { Authorization: `Bearer ${key}` },
});
const data = await response.json();
console.log('Response status:', response.status);
console.log('Response:', JSON.stringify(data, null, 2));
return data;
}
This debug function prints everything you need to diagnose the issue. Remove the verbose logging after you fix the problem.
After fixing the error, confirm it works:
To prevent this error in future integrations:
For the complete error reference, see Paystack Errors and Troubleshooting: The Complete Reference. For more on transaction verification patterns, see Verify Transaction: The Paystack Call You Must Never Skip.
KES 2,999
The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.
Start Paystack Course