Delete a Pass from a Klaviyo Flow
Updated June 3, 2026
TL;DR: Permanently revoke a customer's pass when they unsubscribe, churn, or their membership expires.
- Two methods: Webhook action (no code) or Code block (full control)
- This is a hard delete — the pass is permanently removed from the customer's wallet
- PassNinja automatically clears
passninja_serial_numberand setspassninja_has_passtofalseon the Klaviyo profile after deletion
Overview
Use a delete flow to revoke a customer's pass when they churn, unsubscribe globally, or their membership lapses. The delete call uses the pass serial number stored on the Klaviyo profile. After the delete, PassNinja fires a Pass Deleted event back to Klaviyo which clears the pass properties from the profile automatically.
This is permanent. A deleted pass cannot be restored — the customer must be issued a new pass if they rejoin.
Prerequisites
| Requirement | Details |
|---|---|
| PassNinja account | Customer must have an active pass |
| Klaviyo integration connected | PassNinja → Integrations → Klaviyo must be connected |
external_id on profile | Set automatically to the pass serial after pass creation |
You will need:
| Value | Where to find it |
|---|---|
PASSNINJA_API_KEY | PassNinja → Settings → API Keys |
PASSNINJA_ACCOUNT_ID | PassNinja → Settings → API Keys (format: aid_0x...) |
PASSNINJA_TEMPLATE_ID | PassNinja → Pass Templates → select template (format: ptk_0x...) |
Method 1: Webhook Action (No Code)
Step 1 — Configure the request
| Field | Value |
|---|---|
| Method | DELETE |
| URL | https://api.passninja.com/v1/passes/ptk_0x4d5e6f/{{ person.external_id }} |
Replace ptk_0x4d5e6f with your actual template ID.
No request body is needed for a delete.
Step 2 — Add headers
| Header | Value |
|---|---|
X-API-KEY | Your PassNinja API key |
X-ACCOUNT-ID | Your account ID (e.g. aid_0x1a2b3c) |
Step 3 — Guard against profiles without a pass
Add a Profile Property condition before the Webhook action:
external_idis set
This prevents the webhook from firing for profiles that don't have a pass.
Suggested triggers
| Trigger | Use case |
|---|---|
| Unsubscribed from All | Revoke pass on global unsubscribe |
| Custom metric: Account Closed | Customer-initiated account deletion |
| Custom metric: Membership Expired | Remove pass when membership lapses |
| Removed from Segment | Segment exit triggers revocation |
Method 2: Code Block
Step 1 — Set up environment variables
In the Code action's Environment Variables tab, add:
| Variable | Value |
|---|---|
PASSNINJA_API_KEY | Your PassNinja API key |
PASSNINJA_ACCOUNT_ID | Your account ID |
PASSNINJA_TEMPLATE_ID | Your pass template ID |
Step 2 — Write the code
export const handler = async (event, profile, context) => {
const attrs = profile.data.attributes;
const serial = attrs.external_id;
if (!serial) {
return { skipped: true, reason: 'no pass found' };
}
const response = await fetch(
`https://api.passninja.com/v1/passes/${process.env.PASSNINJA_TEMPLATE_ID}/${serial}`,
{
method: 'DELETE',
headers: {
'X-API-KEY': process.env.PASSNINJA_API_KEY,
'X-ACCOUNT-ID': process.env.PASSNINJA_ACCOUNT_ID,
},
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`PassNinja error: ${JSON.stringify(error)}`);
}
return { deleted: true };
};
Suggested triggers
| Trigger | Use case |
|---|---|
| Unsubscribed from All | Revoke pass on global unsubscribe |
| Custom metric: Account Closed | Customer-initiated account deletion |
| Custom metric: Membership Expired | Remove pass when membership lapses |
| Removed from Segment | Segment exit triggers revocation |
What happens after deletion
PassNinja fires a Pass Deleted event to Klaviyo after the delete completes. The WalletPass Application Object record for this pass is updated automatically:
| Field | Value after deletion |
|---|---|
status | deleted |
updated_at | Timestamp of the deletion |
The external_id on the profile remains set to the serial — the profile is not deleted from Klaviyo. Any subsequent flow actions guarded by external_id is set will correctly skip the profile if you remove the external_id manually, or you can use the WalletPass object's status = deleted condition to filter.
Troubleshooting
Delete fails with 404
The serial in external_id may belong to a pass that was already deleted. The Code block's if (!serial) guard handles this gracefully. For the Webhook action, add the external_id is set condition before the action.
Pass still appears in the customer's wallet after deletion
Apple Wallet and Google Wallet remove the pass automatically when they next sync. This may take a few minutes depending on the device's connection. The pass cannot be used after deletion even if it's still visually present.
Support
- Email: support@passninja.com
- API Reference: passninja.com/documentation
- All Klaviyo tutorials: passninja.com/tutorials/klaviyo
More articles focused on Klaviyo
PassNinja's native Klaviyo integration brings four capabilities to your marketing stack: NFC-enab...
Create a Pass from a Klaviyo Flow Using a Webhook ActionKlaviyo's native **Webhook** flow action sends an HTTP POST directly to the PassNinja API. You co...
Getting Started with KlaviyoWith PassNinja + Klaviyo, you can:
Update a Pass from a Klaviyo FlowOnce a customer has a pass, you can update its content whenever their Klaviyo profile changes — p...