Manage Tokens
List, update, and delete stored payment methods for your customers
Manage Tokens
After creating tokens, you'll need to manage them throughout their lifecycle. This guide covers how to list, retrieve, update, and delete tokens for your shoppers.
List Tokens for a Shopper
Retrieve all stored payment methods for a specific customer. This is useful for displaying saved cards in your checkout or account settings.
curl -X GET "https://api.plexypay.com/v2/tokens?shopper_id=shopper_123456" \
-H "x-api-key: Bearer YOUR_API_KEY"import { Plexy } from '@plexy/plexy-web';
const plexy = new Plexy({
apiKey: process.env.PLEXY_SECRET_KEY,
});
const tokens = await plexy.tokens.list({
shopper_id: 'shopper_123456',
});
console.log(`Found ${tokens.data.length} saved payment methods`);
tokens.data.forEach(token => {
console.log(`${token.card.brand} ending in ${token.card.last4}`);
});import os
from plexy import Plexy
plexy = Plexy(api_key=os.environ['PLEXY_SECRET_KEY'])
tokens = plexy.tokens.list(shopper_id='shopper_123456')
print(f'Found {len(tokens.data)} saved payment methods')
for token in tokens.data:
print(f'{token.card.brand} ending in {token.card.last4}')Response
{
"data": [
{
"id": "tok_card_visa_4242",
"type": "card",
"shopper_id": "shopper_123456",
"recurring_processing_model": "CardOnFile",
"card": {
"brand": "visa",
"last4": "4242",
"exp_month": 12,
"exp_year": 2028,
"funding": "credit",
"issuer_country": "US"
},
"billing_address": {
"line1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "US"
},
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
},
{
"id": "tok_card_mastercard_5678",
"type": "card",
"shopper_id": "shopper_123456",
"recurring_processing_model": "Subscription",
"card": {
"brand": "mastercard",
"last4": "5678",
"exp_month": 6,
"exp_year": 2027,
"funding": "debit",
"issuer_country": "KZ"
},
"created_at": "2026-02-20T14:45:00Z",
"updated_at": "2026-02-20T14:45:00Z"
}
],
"has_more": false
}Filtering Options
You can filter tokens by various criteria:
# Filter by recurring model
curl -X GET "https://api.plexypay.com/v2/tokens?shopper_id=shopper_123456&recurring_processing_model=Subscription" \
-H "x-api-key: Bearer YOUR_API_KEY"
# Filter by card brand
curl -X GET "https://api.plexypay.com/v2/tokens?shopper_id=shopper_123456&card_brand=visa" \
-H "x-api-key: Bearer YOUR_API_KEY"
# Pagination
curl -X GET "https://api.plexypay.com/v2/tokens?shopper_id=shopper_123456&limit=10&starting_after=tok_card_visa_4242" \
-H "x-api-key: Bearer YOUR_API_KEY"// Filter by recurring model
const subscriptionTokens = await plexy.tokens.list({
shopper_id: 'shopper_123456',
recurring_processing_model: 'Subscription',
});
// Filter by card brand
const visaTokens = await plexy.tokens.list({
shopper_id: 'shopper_123456',
card_brand: 'visa',
});
// Pagination
const paginatedTokens = await plexy.tokens.list({
shopper_id: 'shopper_123456',
limit: 10,
starting_after: 'tok_card_visa_4242',
});# Filter by recurring model
subscription_tokens = plexy.tokens.list(
shopper_id='shopper_123456',
recurring_processing_model='Subscription',
)
# Filter by card brand
visa_tokens = plexy.tokens.list(
shopper_id='shopper_123456',
card_brand='visa',
)
# Pagination
paginated_tokens = plexy.tokens.list(
shopper_id='shopper_123456',
limit=10,
starting_after='tok_card_visa_4242',
)Retrieve a Single Token
Get detailed information about a specific token.
curl -X GET https://api.plexypay.com/v2/tokens/tok_card_visa_4242 \
-H "x-api-key: Bearer YOUR_API_KEY"const token = await plexy.tokens.retrieve('tok_card_visa_4242');
console.log('Token ID:', token.id);
console.log('Card:', `${token.card.brand} **** ${token.card.last4}`);
console.log('Expires:', `${token.card.exp_month}/${token.card.exp_year}`);token = plexy.tokens.retrieve('tok_card_visa_4242')
print(f'Token ID: {token.id}')
print(f'Card: {token.card.brand} **** {token.card.last4}')
print(f'Expires: {token.card.exp_month}/{token.card.exp_year}')Response
{
"id": "tok_card_visa_4242",
"type": "card",
"shopper_id": "shopper_123456",
"recurring_processing_model": "CardOnFile",
"card": {
"brand": "visa",
"last4": "4242",
"exp_month": 12,
"exp_year": 2028,
"funding": "credit",
"issuer_country": "US",
"issuer_name": "Chase"
},
"billing_address": {
"line1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "US"
},
"metadata": {
"nickname": "Personal Visa"
},
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}Update Token Details
You can update certain token attributes like billing address and metadata. Card details themselves cannot be modified - you would need to create a new token.
curl -X PATCH https://api.plexypay.com/v2/tokens/tok_card_visa_4242 \
-H "x-api-key: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"billing_address": {
"line1": "456 New Street",
"line2": "Apt 7B",
"city": "New York",
"state": "NY",
"postal_code": "10001",
"country": "US"
},
"metadata": {
"nickname": "Work Card",
"is_default": true
}
}'const updatedToken = await plexy.tokens.update('tok_card_visa_4242', {
billing_address: {
line1: '456 New Street',
line2: 'Apt 7B',
city: 'New York',
state: 'NY',
postal_code: '10001',
country: 'US',
},
metadata: {
nickname: 'Work Card',
is_default: true,
},
});
console.log('Token updated:', updatedToken.id);
console.log('New address:', updatedToken.billing_address.city);updated_token = plexy.tokens.update(
'tok_card_visa_4242',
billing_address={
'line1': '456 New Street',
'line2': 'Apt 7B',
'city': 'New York',
'state': 'NY',
'postal_code': '10001',
'country': 'US',
},
metadata={
'nickname': 'Work Card',
'is_default': True,
},
)
print(f'Token updated: {updated_token.id}')
print(f'New address: {updated_token.billing_address.city}')Response
{
"id": "tok_card_visa_4242",
"type": "card",
"shopper_id": "shopper_123456",
"recurring_processing_model": "CardOnFile",
"card": {
"brand": "visa",
"last4": "4242",
"exp_month": 12,
"exp_year": 2028,
"funding": "credit"
},
"billing_address": {
"line1": "456 New Street",
"line2": "Apt 7B",
"city": "New York",
"state": "NY",
"postal_code": "10001",
"country": "US"
},
"metadata": {
"nickname": "Work Card",
"is_default": true
},
"updated_at": "2026-03-25T16:00:00Z"
}Updatable Fields
| Field | Description |
|---|---|
billing_address | Customer's billing address |
metadata | Custom key-value pairs (up to 50 keys) |
To update card details (number, expiry, CVC), create a new token and delete the old one. Card networks may automatically update expiry dates through Account Updater if enabled.
Delete a Token
Remove a stored payment method. This is typically done when a customer removes a card from their account.
curl -X DELETE https://api.plexypay.com/v2/tokens/tok_card_visa_4242 \
-H "x-api-key: Bearer YOUR_API_KEY"await plexy.tokens.delete('tok_card_visa_4242');
console.log('Token deleted successfully');plexy.tokens.delete('tok_card_visa_4242')
print('Token deleted successfully')Response
{
"id": "tok_card_visa_4242",
"deleted": true
}Deleting a token is permanent and cannot be undone. Any active subscriptions or scheduled payments using this token will fail. Ensure you have confirmation flows in your application before deleting customer payment methods.
Disable a Token
Instead of permanently deleting a token, you can disable it. This prevents the token from being used for new payments while preserving the record.
curl -X POST https://api.plexypay.com/v2/tokens/tok_card_visa_4242/disable \
-H "x-api-key: Bearer YOUR_API_KEY"const disabledToken = await plexy.tokens.disable('tok_card_visa_4242');
console.log('Token disabled:', disabledToken.id);
console.log('Status:', disabledToken.status); // "disabled"disabled_token = plexy.tokens.disable('tok_card_visa_4242')
print(f'Token disabled: {disabled_token.id}')
print(f'Status: {disabled_token.status}') # "disabled"Re-enable a Token
You can re-enable a disabled token if needed.
curl -X POST https://api.plexypay.com/v2/tokens/tok_card_visa_4242/enable \
-H "x-api-key: Bearer YOUR_API_KEY"const enabledToken = await plexy.tokens.enable('tok_card_visa_4242');
console.log('Token re-enabled:', enabledToken.id);
console.log('Status:', enabledToken.status); // "active"enabled_token = plexy.tokens.enable('tok_card_visa_4242')
print(f'Token re-enabled: {enabled_token.id}')
print(f'Status: {enabled_token.status}') # "active"Managing Tokens in the Plexy Dashboard
You can also manage tokens directly in the Plexy Dashboard:
Go to Customers in the sidebar and search for the shopper.
Click on the customer to see their stored payment methods.
From here you can:
- View token details and transaction history
- Update billing address
- Disable or delete tokens
- See which subscriptions are linked to each token
Check Token Validity
Before attempting a payment, you can verify that a token is still valid.
curl -X POST https://api.plexypay.com/v2/tokens/tok_card_visa_4242/verify \
-H "x-api-key: Bearer YOUR_API_KEY"const verification = await plexy.tokens.verify('tok_card_visa_4242');
if (verification.valid) {
console.log('Token is valid and can be charged');
} else {
console.log('Token is invalid:', verification.reason);
// Possible reasons: "expired", "card_declined", "account_closed"
}verification = plexy.tokens.verify('tok_card_visa_4242')
if verification.valid:
print('Token is valid and can be charged')
else:
print(f'Token is invalid: {verification.reason}')
# Possible reasons: "expired", "card_declined", "account_closed"Response
{
"token_id": "tok_card_visa_4242",
"valid": true,
"card": {
"brand": "visa",
"last4": "4242",
"exp_month": 12,
"exp_year": 2028
},
"verified_at": "2026-03-25T16:30:00Z"
}Token verification performs a zero-amount authorization. Some card issuers may show a temporary pending charge to the cardholder. Use this sparingly and consider caching results.
Monitor Expiring Tokens
Proactively identify tokens that will expire soon to request updated payment methods from customers.
# Get tokens expiring in the next 30 days
curl -X GET "https://api.plexypay.com/v2/tokens?expires_before=2026-04-25" \
-H "x-api-key: Bearer YOUR_API_KEY"// Get tokens expiring in the next 30 days
const thirtyDaysFromNow = new Date();
thirtyDaysFromNow.setDate(thirtyDaysFromNow.getDate() + 30);
const expiringTokens = await plexy.tokens.list({
expires_before: thirtyDaysFromNow.toISOString().split('T')[0],
});
for (const token of expiringTokens.data) {
console.log(`Token ${token.id} expires ${token.card.exp_month}/${token.card.exp_year}`);
// Send reminder email to customer
await sendExpirationReminder(token.shopper_id, token);
}from datetime import datetime, timedelta
# Get tokens expiring in the next 30 days
thirty_days_from_now = datetime.now() + timedelta(days=30)
expiring_tokens = plexy.tokens.list(
expires_before=thirty_days_from_now.strftime('%Y-%m-%d'),
)
for token in expiring_tokens.data:
print(f'Token {token.id} expires {token.card.exp_month}/{token.card.exp_year}')
# Send reminder email to customer
send_expiration_reminder(token.shopper_id, token)API Reference
List Tokens Parameters
| Parameter | Type | Description |
|---|---|---|
shopper_id | string | Filter by customer (required) |
recurring_processing_model | string | Filter by token type |
card_brand | string | Filter by card brand (visa, mastercard, etc.) |
status | string | Filter by status (active, disabled) |
expires_before | string | Filter tokens expiring before date (YYYY-MM-DD) |
limit | integer | Number of results (1-100, default 10) |
starting_after | string | Cursor for pagination |
Update Token Parameters
| Parameter | Type | Description |
|---|---|---|
billing_address | object | Customer billing address |
billing_address.line1 | string | Street address |
billing_address.line2 | string | Apartment, suite, etc. |
billing_address.city | string | City |
billing_address.state | string | State or province |
billing_address.postal_code | string | ZIP or postal code |
billing_address.country | string | Two-letter country code |
metadata | object | Custom key-value pairs |
Error Handling
import { PlexyError } from '@plexy/plexy-web';
try {
const token = await plexy.tokens.retrieve('tok_invalid');
} catch (error) {
if (error instanceof PlexyError) {
switch (error.code) {
case 'token_not_found':
console.error('Token does not exist');
break;
case 'token_disabled':
console.error('Token has been disabled');
break;
case 'unauthorized':
console.error('Token belongs to a different merchant');
break;
default:
console.error('Error:', error.message);
}
}
}from plexy import PlexyError
try:
token = plexy.tokens.retrieve('tok_invalid')
except PlexyError as error:
if error.code == 'token_not_found':
print('Token does not exist')
elif error.code == 'token_disabled':
print('Token has been disabled')
elif error.code == 'unauthorized':
print('Token belongs to a different merchant')
else:
print(f'Error: {error.message}')Best Practices
- Display tokens clearly - Show card brand, last 4 digits, and expiry to help customers identify their cards
- Handle disabled tokens - Check token status before displaying in checkout
- Monitor expiration - Set up automated reminders for expiring cards
- Confirm deletion - Always confirm with the user before deleting a payment method
- Use metadata wisely - Store nicknames or default flags to improve UX
- Enable Account Updater - Automatically update card details when issuers reissue cards
Next Steps
- Forward Payment Details - Share tokens with third-party providers
- Account Management - Automatic card updates and retry logic