PlexySDK DOCS

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,2026

Upload 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.csv
const 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_cardholder

Webhook 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

Monthly Card VolumeRecommended ScheduleRationale
< 10,000MonthlySufficient for low volume
10,000 - 100,000WeeklyBalance cost and freshness
> 100,000DailyMaximum 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:

FilterDescriptionExample
expiring_within_daysCards expiring within N days90
not_checked_within_daysCards not checked recently30
card_brandsSpecific card networks["visa", "mastercard"]
customer_idsSpecific customers["cus_123", "cus_456"]
created_beforeCards created before date"2026-01-01"
has_failed_paymentCards with recent failurestrue

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

ItemDetails
Network fees$0.01-$0.03 per card checked (passed through at cost)
Batch size limit100,000 cards per batch
Daily limitConfigurable per account
Processing time24-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

На этой странице