Paystack Dispute Deadline Missed: What Happens
If you miss the deadline to respond to a Paystack dispute (chargeback), Paystack automatically resolves the dispute in the cardholder favour. The transaction amount is deducted from your balance and returned to the cardholder. This is irreversible. You cannot appeal, reopen, or contest the dispute after the deadline. Prevention is the only solution: set up the dispute.create webhook, pipe it to Slack or email immediately, and build an internal escalation process with deadlines before the Paystack deadline.
What Happens When You Miss the Deadline
Here is the timeline of a missed dispute:
- Day 0: A cardholder contacts their bank and disputes a charge on their statement.
- Day 0 (hours later): The bank notifies Paystack. Paystack creates a dispute record and sends you a
dispute.createwebhook event and a dashboard notification. - Day 0 to 3: You are supposed to respond with evidence (delivery proof, customer communication, terms of service, etc.). This is your window.
- Deadline passes: You did not respond. Paystack resolves the dispute in favour of the cardholder.
- Post-deadline: The transaction amount is debited from your Paystack balance. If your balance is insufficient, it goes negative and future settlements are held until the balance is recovered.
The resolution is final. There is no "I missed it by one hour, can you reopen it?" option. The card network (Visa, Mastercard) has strict timelines, and Paystack has to comply.
// What the dispute.create webhook looks like
{
"event": "dispute.create",
"data": {
"id": 12345,
"refund_amount": 5000000,
"currency": "NGN",
"status": "awaiting-merchant-feedback",
"resolution": null,
"domain": "live",
"transaction": {
"id": 67890,
"reference": "txn_abc123",
"amount": 5000000,
"domain": "live",
"status": "success"
},
"message": "Customer claims they did not authorize this transaction",
"due_at": "2026-07-23T23:59:59.000Z",
"created_at": "2026-07-20T10:30:00.000Z"
}
}
The due_at field is your deadline. After that timestamp, the dispute is auto-resolved against you.
Why You Cannot Reverse a Missed Dispute
Chargebacks are governed by card network rules (Visa, Mastercard, Verve), not by Paystack. When you miss the response window:
- Paystack reports to the card network that the merchant did not respond
- The card network rules in favour of the cardholder by default
- The bank credits the cardholder's account
- Paystack debits your merchant account
Paystack cannot override the card network's decision. Even if you have clear proof that the transaction was legitimate, presenting evidence after the deadline is like submitting a court filing after the judgment. The process has concluded.
Some merchants ask about arbitration (a second level of dispute at the card network level). Arbitration is only available for disputes where you responded within the deadline and the resolution went against you despite your evidence. It is not available for missed deadlines.
The Financial Impact
A missed dispute costs you:
- The full transaction amount. The refund amount specified in the dispute is deducted from your Paystack balance. This is usually the full transaction amount but can be partial.
- A chargeback fee. Paystack charges a fee for each dispute, regardless of the outcome. This fee varies but is typically between 1,500 and 5,000 NGN.
- The product or service you already delivered. You shipped the goods, provided the service, or granted access, and now the payment has been reversed. You are out both the money and the value delivered.
- Reputation damage. A high dispute rate (above 0.5%) triggers monitoring from the card networks. If it stays high, your account can be flagged, your processing rates increased, or your account suspended entirely.
For a business processing 1 million NGN per month, even a single missed dispute on a 50,000 NGN transaction is a 5% hit to monthly revenue. Two or three missed disputes and your monthly profit can evaporate.
Code: Webhook + Slack Alert for New Disputes
The minimum viable dispute defense is a Slack alert that fires the moment a dispute is created. Here is the complete implementation:
// Dispute alert webhook handler
import crypto from 'crypto';
interface DisputeEvent {
event: 'dispute.create' | 'dispute.remind';
data: {
id: number;
refund_amount: number;
currency: string;
status: string;
due_at: string;
created_at: string;
message: string;
transaction: {
reference: string;
amount: number;
};
};
}
// Verify the webhook signature
function verifyPaystackSignature(body: string, signature: string): boolean {
const hash = crypto
.createHmac('sha512', process.env.PAYSTACK_SECRET_KEY!)
.update(body)
.digest('hex');
return hash === signature;
}
app.post('/api/webhooks/paystack', async (req, res) => {
const signature = req.headers['x-paystack-signature'] as string;
const rawBody = JSON.stringify(req.body);
if (!verifyPaystackSignature(rawBody, signature)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Return 200 immediately
res.status(200).json({ received: true });
const event: DisputeEvent = req.body;
if (event.event === 'dispute.create') {
await handleNewDispute(event);
}
if (event.event === 'dispute.remind') {
await handleDisputeReminder(event);
}
});
async function handleNewDispute(event: DisputeEvent) {
const { data } = event;
const deadline = new Date(data.due_at);
const hoursRemaining = Math.round(
(deadline.getTime() - Date.now()) / (1000 * 60 * 60)
);
const amountFormatted = (data.refund_amount / 100).toLocaleString('en-NG', {
style: 'currency',
currency: data.currency,
});
// Send Slack alert
await fetch(process.env.SLACK_WEBHOOK_URL!, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: ':rotating_light: NEW PAYSTACK DISPUTE',
blocks: [
{
type: 'header',
text: {
type: 'plain_text',
text: 'New Paystack Dispute - Action Required',
},
},
{
type: 'section',
fields: [
{ type: 'mrkdwn', text: `*Amount:*
${amountFormatted}` },
{ type: 'mrkdwn', text: `*Deadline:*
${hoursRemaining} hours` },
{ type: 'mrkdwn', text: `*Transaction:*
${data.transaction.reference}` },
{ type: 'mrkdwn', text: `*Dispute ID:*
${data.id}` },
],
},
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*Customer message:*
${data.message || 'No message provided'}`,
},
},
{
type: 'actions',
elements: [
{
type: 'button',
text: { type: 'plain_text', text: 'View in Paystack' },
url: `https://dashboard.paystack.com/#/disputes/${data.id}`,
style: 'danger',
},
],
},
],
}),
});
// Also send email to the finance team
await sendEmail({
to: process.env.FINANCE_EMAIL!,
subject: `URGENT: Paystack Dispute - ${amountFormatted} - ${hoursRemaining}h deadline`,
body: `A new dispute has been filed for transaction ${data.transaction.reference}.
Amount: ${amountFormatted}
Deadline: ${deadline.toLocaleString()} (${hoursRemaining} hours from now)
Customer message: ${data.message || 'None'}
Respond in the Paystack dashboard:
https://dashboard.paystack.com/#/disputes/${data.id}
If you do not respond by the deadline, the dispute is automatically resolved
in the customer's favour and the amount is deducted from your balance.`,
});
// Record the dispute in your database
await db.query(
`INSERT INTO disputes (paystack_id, transaction_reference, amount, status, due_at, created_at)
VALUES ($1, $2, $3, 'awaiting_response', $4, NOW())`,
[data.id, data.transaction.reference, data.refund_amount, data.due_at]
);
}
async function handleDisputeReminder(event: DisputeEvent) {
const { data } = event;
const deadline = new Date(data.due_at);
const hoursRemaining = Math.round(
(deadline.getTime() - Date.now()) / (1000 * 60 * 60)
);
// Send escalation alert
await fetch(process.env.SLACK_WEBHOOK_URL!, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: `:warning: DISPUTE REMINDER: ${hoursRemaining}h remaining for dispute ${data.id}. Respond NOW or lose ${(data.refund_amount / 100).toLocaleString()} ${data.currency}.`,
}),
});
}
Building an Internal Escalation Process
A Slack alert is step one. You also need a process that ensures someone actually responds to the dispute. Here is a three-tier escalation:
Tier 1: Immediate (0 to 2 hours after dispute creation)
- Slack alert to the #payments channel
- Email to the person responsible for payment operations
- Log the dispute in your internal system
Tier 2: Warning (12 hours before deadline)
- If the dispute has not been responded to, send a direct message to the head of finance
- Send an SMS if the dispute amount exceeds a threshold (for example, 100,000 NGN)
Tier 3: Critical (4 hours before deadline)
- If still no response, alert the CEO or founder directly
- The alert should say "You will lose [amount] in [hours] if no one responds to this dispute"
// Escalation checker: run every hour via cron
async function checkDisputeEscalation() {
const openDisputes = await db.query(
`SELECT * FROM disputes
WHERE status = 'awaiting_response'
AND due_at > NOW()`
);
for (const dispute of openDisputes.rows) {
const hoursRemaining = Math.round(
(new Date(dispute.due_at).getTime() - Date.now()) / (1000 * 60 * 60)
);
if (hoursRemaining <= 4 && !dispute.tier3_alerted) {
// Tier 3: Critical escalation
await sendSMS(process.env.CEO_PHONE!,
`CRITICAL: Paystack dispute for ${dispute.amount / 100} ${dispute.currency} expires in ${hoursRemaining}h. Respond NOW.`
);
await db.query(
'UPDATE disputes SET tier3_alerted = true WHERE id = $1',
[dispute.id]
);
} else if (hoursRemaining <= 12 && !dispute.tier2_alerted) {
// Tier 2: Warning escalation
await sendDirectSlackMessage(
process.env.FINANCE_HEAD_SLACK_ID!,
`Dispute ${dispute.paystack_id} for ${dispute.amount / 100} ${dispute.currency} has ${hoursRemaining}h remaining. No response yet.`
);
await db.query(
'UPDATE disputes SET tier2_alerted = true WHERE id = $1',
[dispute.id]
);
}
}
}
Preventing Disputes in the First Place
The best dispute is one that never happens. Most disputes are caused by:
- Unclear billing descriptor. The customer sees a charge on their statement from a name they do not recognise and files a dispute. Make sure your Paystack business name matches what customers expect to see.
- No receipt or confirmation. Customers who do not receive a receipt after payment are more likely to dispute the charge because they are not sure the payment went through or what it was for.
- Poor customer support. A customer who cannot reach you will go to their bank instead. Respond to refund requests quickly and you prevent them from becoming chargebacks.
- Unclear refund policy. Make your refund policy visible before purchase. Customers who know the policy are less likely to dispute.
Prevention checklist:
- Send a receipt email immediately after every successful payment
- Include a recognizable business name on the receipt
- Provide a clear refund policy on your website
- Respond to customer support requests within 24 hours
- Process legitimate refund requests within 48 hours (faster than the chargeback process)
- Set up proactive monitoring for unusual transaction patterns
How to Respond to a Dispute (When You Catch It in Time)
If your alert system works and you catch the dispute before the deadline, here is how to respond effectively:
- Gather evidence. Pull up the transaction details, delivery confirmation, customer emails, IP address logs, and any other proof that the transaction was legitimate.
- Submit via the Paystack dashboard. Go to the dispute in your Paystack dashboard and upload your evidence. Include a clear, concise description of the transaction and why the dispute is not valid.
- Contact the customer directly. Sometimes the customer filed the dispute because they did not recognize the charge, not because they were defrauded. A phone call or email saying "We see you disputed a charge for [service]. This was your purchase on [date]. Would you like us to process a refund instead?" can resolve the dispute amicably.
Evidence that helps win disputes:
- Delivery tracking numbers and confirmation
- Customer signup records (name, email, IP address)
- Screenshots of the customer using the service after the charge
- Email correspondence where the customer acknowledged the purchase
- Your terms of service and refund policy
- Proof that the goods or services were delivered as described
Verification: Test Your Dispute Alert System
Test your dispute handling before a real dispute arrives:
- Test the webhook handler. Send a mock
dispute.createevent to your webhook endpoint. Confirm that the Slack alert fires, the email is sent, and the dispute is recorded in your database. - Test the escalation process. Create a test dispute record in your database with a
due_atset to 3 hours from now. Run the escalation checker. Confirm the Tier 3 critical alert fires. - Test the Slack integration. Send a test message to your Slack webhook URL. Confirm the message appears in the correct channel with the correct formatting.
- Test the email integration. Trigger the dispute email. Confirm it arrives in the finance team inbox with the correct subject line and deadline.
- Simulate a dispute.remind event. Send a mock
dispute.remindwebhook. Confirm the reminder alert fires with the correct hours remaining.
Do this test quarterly. People change roles, Slack channels get renamed, email addresses change. A dispute alert system that worked 6 months ago might not work today. For a complete reference on all Paystack errors, see the Paystack Errors and Troubleshooting: The Complete Reference.
Key Takeaways
- ✓Missing a Paystack dispute deadline means automatic loss. The cardholder gets the money back, the amount is deducted from your Paystack balance, and there is no appeal process.
- ✓Paystack typically gives you 24 to 72 hours to respond to a dispute, depending on the card network and bank. This window is not negotiable.
- ✓The dispute.create webhook event is your first alert. If you are not listening for it, you are relying on checking the Paystack dashboard manually, which does not scale.
- ✓Disputes are rare for most businesses (less than 0.1% of transactions), but each one can cost the full transaction amount plus a chargeback fee. One missed dispute on a large transaction can wipe out a week of profit.
- ✓Build a Slack or email alert that fires the moment a dispute is created. Include the transaction amount, customer email, and deadline in the alert so the team can act immediately.
- ✓The best dispute defense is prevention: clear billing descriptors, good customer communication, and responsive customer support reduce dispute rates dramatically.
- ✓Track your dispute rate. If it exceeds 0.5% of transactions, Paystack and the card networks may take action against your account, including suspension.
Frequently Asked Questions
- How long do I have to respond to a Paystack dispute?
- Typically 24 to 72 hours from when the dispute is created, depending on the card network (Visa, Mastercard, Verve) and the issuing bank. The exact deadline is in the due_at field of the dispute.create webhook. Always treat this as a hard deadline with no extensions.
- Can I get the money back after missing a dispute deadline?
- No. Once the deadline passes and the dispute is resolved in the cardholder favour, the decision is final. Paystack cannot override card network rules. The only way to recover the money is to contact the customer directly and ask them to pay again voluntarily, which rarely works.
- Does Paystack charge a fee for disputes even if I win?
- Yes. Paystack charges a chargeback processing fee regardless of the dispute outcome. The fee covers the administrative cost of handling the dispute. Even if you respond with evidence and win the dispute, the fee applies.
- What is a dangerous dispute rate and what happens if I exceed it?
- Visa and Mastercard monitor merchants with dispute rates above 0.5% of transactions. If your rate stays above this threshold, you may face increased processing fees, mandatory chargeback monitoring programs, or account suspension. Paystack will notify you if your dispute rate is approaching dangerous levels.
- Should I refund a customer who files a dispute instead of contesting it?
- Sometimes, yes. If the dispute amount is small and the cost of fighting it (your time, the chargeback fee, potential reputation impact) exceeds the refund amount, it is more economical to refund the customer directly and ask them to withdraw the dispute. But only do this if you contact the customer first and they agree to withdraw the dispute with their bank.
Ready to build real-world apps?
Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.
Apply to the McTaba Marathon