Batch Account Updater
Proactively update all stored card credentials on a schedule to prevent payment failures
Batch Account Updater
Batch Account Updater proactively checks all your stored cards for updates on a regular schedule. Unlike Real-Time Account Updater which checks at payment time, Batch Account Updater keeps your entire card portfolio current between transactions.
How It Works
Plexy submits your stored cards to card networks in batches and processes the returned updates:
Configuring Batch Updates
Via Plexy Dashboard
Go to Settings > Account Management > Account Updater.
Toggle Batch Account Updater to enabled.
Choose your update frequency:
- Daily - Check cards every day (recommended for high-volume merchants)
- Weekly - Check cards every week
- Monthly - Check cards once per month
Optionally filter which cards to include:
- Cards expiring within N days
- Cards not checked in N days
- Specific card brands
Choose when batch processing should run (e.g., 2:00 AM - 6:00 AM UTC).
Via API
Create and manage batch update jobs programmatically:
curl -X POST https://api.plexypay.com/v2/account_updater/batches \
-H "x-api-key: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"filter": {
"expiring_within_days": 90,
"not_checked_within_days": 30,
"card_brands": ["visa", "mastercard"]
},
"limit": 10000
}'import { Plexy } from '@plexy/plexy-web';
const plexy = new Plexy({
apiKey: process.env.PLEXY_SECRET_KEY,
});
const batch = await plexy.accountUpdater.createBatch({
filter: {
expiring_within_days: 90,
not_checked_within_days: 30,
card_brands: ['visa', 'mastercard'],
},
limit: 10000,
});
console.log('Batch ID:', batch.id);
console.log('Cards to process:', batch.total_cards);
console.log('Estimated completion:', batch.estimated_completion);import os
from plexy import Plexy
plexy = Plexy(api_key=os.environ['PLEXY_SECRET_KEY'])
batch = plexy.account_updater.create_batch(
filter={
'expiring_within_days': 90,
'not_checked_within_days': 30,
'card_brands': ['visa', 'mastercard'],
},
limit=10000,
)
print(f"Batch ID: {batch.id}")
print(f"Cards to process: {batch.total_cards}")
print(f"Estimated completion: {batch.estimated_completion}")Response
{
"id": "batch_abc123",
"object": "account_updater_batch",
"status": "pending",
"total_cards": 8547,
"filter": {
"expiring_within_days": 90,
"not_checked_within_days": 30,
"card_brands": ["visa", "mastercard"]
},
"created_at": "2026-03-25T02:00:00Z",
"estimated_completion": "2026-03-27T02:00:00Z"
}Batch File Format
When processing large volumes, you can submit cards via file upload:
Submission File Format (CSV)
payment_method_id,card_brand,card_last4,exp_month,exp_year
pm_card_visa_001,visa,4242,12,2026
pm_card_mc_002,mastercard,5555,06,2027
pm_card_visa_003,visa,1234,03,2026Upload a Batch File
curl -X POST https://api.plexypay.com/v2/account_updater/batches/upload \
-H "x-api-key: Bearer YOUR_API_KEY" \
-F "file=@cards_to_update.csv" \
-F "description=Monthly card update - March 2026"import fs from 'fs';
const file = fs.readFileSync('./cards_to_update.csv');
const batch = await plexy.accountUpdater.uploadBatch({
file: file,
filename: 'cards_to_update.csv',
description: 'Monthly card update - March 2026',
});
console.log('Batch ID:', batch.id);with open('cards_to_update.csv', 'rb') as file:
batch = plexy.account_updater.upload_batch(
file=file,
description='Monthly card update - March 2026',
)
print(f"Batch ID: {batch.id}")Processing Results
Check Batch Status
curl https://api.plexypay.com/v2/account_updater/batches/batch_abc123 \
-H "x-api-key: Bearer YOUR_API_KEY"const batch = await plexy.accountUpdater.getBatch('batch_abc123');
console.log('Status:', batch.status);
console.log('Progress:', `${batch.processed_cards}/${batch.total_cards}`);batch = plexy.account_updater.get_batch('batch_abc123')
print(f"Status: {batch.status}")
print(f"Progress: {batch.processed_cards}/{batch.total_cards}")Completed Batch Response
{
"id": "batch_abc123",
"object": "account_updater_batch",
"status": "completed",
"total_cards": 8547,
"processed_cards": 8547,
"results": {
"updated": 423,
"no_update": 7891,
"account_closed": 156,
"contact_cardholder": 77
},
"breakdown": {
"new_pan": 189,
"new_expiry": 234
},
"created_at": "2026-03-25T02:00:00Z",
"completed_at": "2026-03-27T01:45:00Z"
}Download Results File
curl https://api.plexypay.com/v2/account_updater/batches/batch_abc123/results \
-H "x-api-key: Bearer YOUR_API_KEY" \
-o batch_results.csvconst results = await plexy.accountUpdater.downloadResults('batch_abc123');
fs.writeFileSync('./batch_results.csv', results);results = plexy.account_updater.download_results('batch_abc123')
with open('batch_results.csv', 'wb') as file:
file.write(results)Results File Format (CSV)
payment_method_id,update_type,old_last4,new_last4,old_exp_month,old_exp_year,new_exp_month,new_exp_year,action_required
pm_card_visa_001,new_expiry,4242,4242,12,2026,12,2029,
pm_card_mc_002,new_pan,5555,8910,06,2027,06,2030,
pm_card_visa_003,account_closed,1234,,,,,contact_cardholder
pm_card_visa_004,no_update,7890,,,,,
pm_card_mc_005,contact_cardholder,1111,,,,,contact_cardholderWebhook Events
Plexy sends webhooks during and after batch processing:
account_updater.batch_started
{
"id": "evt_abc123",
"type": "account_updater.batch_started",
"created_at": "2026-03-25T02:00:00Z",
"data": {
"batch_id": "batch_abc123",
"total_cards": 8547,
"estimated_completion": "2026-03-27T02:00:00Z"
}
}account_updater.batch_completed
{
"id": "evt_def456",
"type": "account_updater.batch_completed",
"created_at": "2026-03-27T01:45:00Z",
"data": {
"batch_id": "batch_abc123",
"total_cards": 8547,
"results": {
"updated": 423,
"no_update": 7891,
"account_closed": 156,
"contact_cardholder": 77
},
"download_url": "https://api.plexypay.com/v2/account_updater/batches/batch_abc123/results"
}
}payment_method.updated
Sent for each card that receives an update:
{
"id": "evt_ghi789",
"type": "payment_method.updated",
"created_at": "2026-03-26T14:30:00Z",
"data": {
"payment_method": "pm_card_visa_001",
"customer": "cus_abc123",
"update_source": "batch_account_updater",
"batch_id": "batch_abc123",
"update_type": "new_expiry",
"changes": {
"exp_year": {
"old": 2026,
"new": 2029
}
}
}
}payment_method.action_required
Sent for cards requiring manual follow-up:
{
"id": "evt_jkl012",
"type": "payment_method.action_required",
"created_at": "2026-03-26T14:35:00Z",
"data": {
"payment_method": "pm_card_visa_003",
"customer": "cus_def456",
"batch_id": "batch_abc123",
"action": "contact_cardholder",
"reason": "account_closed"
}
}Scheduling Best Practices
Recommended Schedules by Volume
| Monthly Card Volume | Recommended Schedule | Rationale |
|---|---|---|
| < 10,000 | Monthly | Sufficient for low volume |
| 10,000 - 100,000 | Weekly | Balance cost and freshness |
| > 100,000 | Daily | Maximum authorization rates |
Strategic Scheduling
Schedule batch updates to complete before your major billing cycles. If you bill on the 1st of each month, run batch updates around the 25th to catch any card changes before billing.
// Example: Schedule batch update 5 days before billing cycle
const billingDate = new Date('2026-04-01');
const updateDate = new Date(billingDate);
updateDate.setDate(updateDate.getDate() - 5);
const batch = await plexy.accountUpdater.createBatch({
scheduled_for: updateDate.toISOString(),
filter: {
billing_date: billingDate.toISOString().split('T')[0],
},
});Filter Options
Customize which cards are included in each batch:
| Filter | Description | Example |
|---|---|---|
expiring_within_days | Cards expiring within N days | 90 |
not_checked_within_days | Cards not checked recently | 30 |
card_brands | Specific card networks | ["visa", "mastercard"] |
customer_ids | Specific customers | ["cus_123", "cus_456"] |
created_before | Cards created before date | "2026-01-01" |
has_failed_payment | Cards with recent failures | true |
Example: Target High-Risk Cards
const batch = await plexy.accountUpdater.createBatch({
filter: {
// Cards expiring soon
expiring_within_days: 60,
// OR cards with recent failures
has_failed_payment: true,
// AND haven't been checked in 7 days
not_checked_within_days: 7,
},
});Processing Batch Results
Automated Processing
Set up automated handling of batch results:
app.post('/webhooks/plexy', async (req, res) => {
const event = verifyWebhook(req);
switch (event.type) {
case 'account_updater.batch_completed':
await processBatchResults(event.data);
break;
case 'payment_method.action_required':
await handleActionRequired(event.data);
break;
}
res.status(200).send('OK');
});
async function processBatchResults(data) {
const { batch_id, results } = data;
// Log summary
console.log(`Batch ${batch_id} completed:`);
console.log(` Updated: ${results.updated}`);
console.log(` No change: ${results.no_update}`);
console.log(` Closed: ${results.account_closed}`);
console.log(` Need contact: ${results.contact_cardholder}`);
// Generate report for operations team
if (results.account_closed > 0 || results.contact_cardholder > 0) {
await notifyOperationsTeam({
batch_id,
accounts_needing_attention:
results.account_closed + results.contact_cardholder,
});
}
}
async function handleActionRequired(data) {
const { payment_method, customer, action, reason } = data;
// Queue customer outreach
await customerOutreachQueue.add({
customer,
payment_method,
reason,
priority: reason === 'account_closed' ? 'high' : 'medium',
template:
reason === 'account_closed'
? 'payment_method_expired'
: 'update_payment_method',
});
}Costs and Limits
| Item | Details |
|---|---|
| Network fees | $0.01-$0.03 per card checked (passed through at cost) |
| Batch size limit | 100,000 cards per batch |
| Daily limit | Configurable per account |
| Processing time | 24-72 hours depending on volume |
Card network fees apply to all cards submitted, regardless of whether an update is found. Optimize your filters to target cards most likely to have updates.
Next Steps
- Real-Time Account Updater - Updates at payment time
- Account Updater Overview - Compare both modes
- Auto Rescue - Recover failed payments