Issuing PassFlow Mobile Passes for Apple/Google Wallet

Complete workflow for provisioning PassFlow credentials to Apple Wallet and Google Wallet via NFC

8 mins
Intermediate

Related API Endpoints

This guide focuses on the complete workflow for issuing PassFlow mobile passes that can be added to Apple Wallet and Google Wallet for NFC-based access.

What is PassFlow?

PassFlow is DoorFlow's native service for managing digital credentials that work with Apple Wallet and Google Wallet. PassFlow supports:

  • NFC technology - Tap phone to reader (Apple VAS and Google SmartTap)
  • Barcode/QR codes - Visual scanning
  • Multiple pass types - Access cards, membership cards, event tickets, etc.

Pass Types Supported

PassFlow can issue various types of digital passes:

Access & Membership

Corporate access passes
Membership cards
School ID cards
Hotel room keys

Events & Travel

Event tickets
Boarding passes
Airline tickets

Retail

Loyalty cards
Coupons

Key Principles

Unlike some credential systems, PassFlow passes cannot be added silently:

  • Person must always approve "Add to Wallet"
  • User explicitly accepts the pass
  • Consistent with Apple and Google wallet security policies

Apple Wallet Limitations

Important: Apple passes cannot be remotely removed from Apple Wallet:

  • User must manually remove pass from their wallet
  • Deleting the credential in DoorFlow prevents future access but doesn't remove the pass from the device
  • This is an Apple Wallet platform limitation, not a DoorFlow limitation

What happens when you delete a PassFlow credential in DoorFlow

Access is immediately revoked at doors
Pass appears in wallet but won't grant access
User should manually remove pass from wallet

Google Wallet Behavior

Google SmartTap passes can be remotely revoked:

  • Deleting credential in DoorFlow removes pass from Google Wallet
  • More seamless cleanup than Apple

Pass Distribution Methods

Method 1: Email Invitation

DoorFlow automatically emails the pass invitation to the person.

When to use

Simple, hands-off implementation
You don't need to customize invitation messaging
Person already exists in DoorFlow with email address

How it works

Create PassFlow credential via API
DoorFlow sends email with "Add to Wallet" link
Person clicks link and approves adding pass
Pass provisions to Apple Wallet or Google Wallet
Access granted at physical readers

Method 2: Your App with Invitation Code

You retrieve the invitation details and present them in your own app.

When to use

Custom invitation experience
Integration with existing mobile app
In-app pass management
White-label experience

How it works

Create PassFlow credential via API
Retrieve pass invitation URL from credential
Display "Add to Wallet" button in your app
Person taps button, approves adding pass
Pass provisions to their wallet
Access granted at physical readers

Sequence Diagram

This diagram shows pass issuance triggered from either the DoorFlow UI or via API:

sequenceDiagram participant Admin as DoorFlow Admin User participant ClientApp as Client App participant DoorFlow as DoorFlow participant Wallet as Apple/Google Wallet participant Person as Person Note over Admin,Person: [1. Request PassFlow Pass for Person] alt Request via DoorFlow UI Admin->>DoorFlow: 1a. Request via website else Request via API ClientApp->>DoorFlow: 1b. Request via API end DoorFlow->>Admin: 1b. Return credential_id with status 'pending' DoorFlow->>DoorFlow: 2. Generate wallet pass Note over ClientApp,Person: [3. Pass invitation sent via email or API] alt Configured at DoorFlow account level DoorFlow->>Person: 3a. Send "Add to Wallet" link via Email else Developer handles delivery ClientApp->>DoorFlow: 3b. Request details for credential_id DoorFlow-->>ClientApp: 3b. Return wallet pass URL ClientApp->>Person: 3a. Display "Add to Wallet" button end Note over Person: [4. Person accepts pass] Person->>Wallet: 4. Tap "Add to Apple/Google Wallet" Wallet->>Person: Request user approval Person->>Wallet: Approve Wallet->>DoorFlow: 5. Confirm pass added DoorFlow->>ClientApp: Webhook: credential status changed to 'active' DoorFlow->>DoorFlow: 6. Sync credential with access readers Note over Admin,Person: [7. Remove PassFlow credential] alt Remove via DoorFlow UI Admin->>DoorFlow: 7a. Remove PassFlow credential else Remove via API ClientApp->>DoorFlow: 7b. Request via API end DoorFlow->>Wallet: Revoke credential (Google only) alt Google Wallet Wallet->>Person: Pass removed automatically else Apple Wallet Note over Person: Pass remains in wallet
but won't grant access end DoorFlow->>ClientApp: Webhook: credential status 'revoked'

Implementation

Method 1: Email Invitation

Step 1: Create person with email

bash
POST /api/3/people
{
  "person": {
    "first_name": "Alex",
    "last_name": "Chen",
    "email": "alex@example.com",
    "enabled": true
  }
}

Step 2: Create PassFlow credential

bash
POST /api/3/people/{person_id}/credentials
{
  "person_credential": {
    "credential_type_id": 9,
    "enabled": true
  }
}

Response:

json
{
  "id": "cred_pf123",
  "credential_type_id": 9,
  "status": "pending",
  "enabled": true,
  "person_id": 12345,
  "created_at": "2024-01-15T14:25:00Z"
}

Step 3: DoorFlow automatically emails invitation

Person receives email with "Add to Apple Wallet" or "Add to Google Wallet" button. No additional work required.

Method 2: Your App Handles Delivery

Step 1: Create person

bash
POST /api/3/people
{
  "person": {
    "first_name": "Alex",
    "last_name": "Chen",
    "enabled": true
  }
}

Step 2: Create PassFlow credential

bash
POST /api/3/people/{person_id}/credentials
{
  "person_credential": {
    "credential_type_id": 9,
    "enabled": true
  }
}

Step 3: Retrieve pass details

bash
GET /api/3/people/{person_id}/credentials/{credential_id}

Response:

json
{
  "id": "cred_pf123",
  "credential_type_id": 9,
  "status": "pending",
  "wallet_pass_url": "https://wallet.doorflow.com/passes/abc123",
  "enabled": true,
  "person_id": 12345
}

Step 4: Present "Add to Wallet" in your app

iOS (Apple Wallet):

swift
// Display Apple Wallet button
let passURL = URL(string: "https://wallet.doorflow.com/passes/abc123")!
// Use PKAddPassButton or custom UI

Android (Google Wallet):

kotlin
// Display Google Wallet button
val passUrl = "https://wallet.doorflow.com/passes/abc123"
// Use Google Wallet button or custom UI

Web:

html
<a href="https://wallet.doorflow.com/passes/abc123">
  <img src="add-to-apple-wallet.svg" alt="Add to Apple Wallet">
</a>

Credential Status Lifecycle

PassFlow credentials go through these statuses:

  1. pending - Pass created, waiting for person to add to wallet
  2. active - Person added pass to wallet, credential working
  3. revoked - Credential removed from DoorFlow, access denied

Monitor via webhooks:

json
{
  "event": "credential.status_changed",
  "credential_id": "cred_pf123",
  "person_id": 12345,
  "old_status": "pending",
  "new_status": "active",
  "timestamp": "2024-01-15T14:30:00Z"
}

Apple VAS Limitations

Apple Value Added Services (VAS) passes have restrictions

Limited use cases: Primarily for sports clubs, gyms, membership organizations
Not for general corporate access: Apple restricts VAS usage
Approval required: Your use case must be approved by Apple

If your use case doesn't fit Apple VAS restrictions

Consider HID Mobile Access instead (works with Apple)
Use PassFlow for Google Wallet (Android users)
Provide backup credentials (card or PIN)

Best Practices

Provide Backup Credentials

Not all users can use mobile passes:

bash
# Issue PassFlow credential (primary)
POST /api/3/people/12345/credentials
{
  "credential_type_id": 9,
  "enabled": true
}

# Issue PIN (backup)
POST /api/3/people/12345/credentials
{
  "credential_type_id": 6,
  "value": "******",
  "enabled": true
}

Monitor Pass Acceptance

Don't assume pass was added to wallet:

bash
# Check credential status
GET /api/3/people/{person_id}/credentials/{credential_id}

# Or use webhooks (recommended)
POST /api/3/webhooks
{
  "url": "https://your-app.com/webhooks/doorflow",
  "events": ["credential.status_changed"]
}

Test on Both Platforms

PassFlow works differently on iOS vs Android:

Test checklist

Pass adds successfully to Apple Wallet
Pass adds successfully to Google Wallet
NFC tap works on readers (both platforms)
Pass displays correctly (branding, colors)
Pass removal works (auto on Android, manual on iOS)
Backup credentials work when phone is unavailable

Educate Users

Users need guidance on:

  • How to add pass to their wallet
  • How to use pass at readers (tap phone)
  • What to do if pass doesn't work (use backup PIN)
  • How to remove pass when no longer needed (Apple users)

Troubleshooting

Pass stuck in 'pending' status

Person hasn't added pass to wallet yet. Check:

  • Did they receive the invitation?
  • Do they know how to add passes to their wallet?
  • Is wallet app installed and working?
  • Try resending invitation

Pass added to wallet but not working at readers

Check these common issues:

  1. Credential not synced to readers

    • Wait a few minutes for sync
    • Check reader is online
  2. Person lacks group membership

    bash
    GET /api/3/people/{person_id}
    # Check group_ids includes appropriate groups
    
  3. Person is disabled

    bash
    GET /api/3/people/{person_id}
    # Check "enabled": true
    
  4. Reader doesn't support NFC

    • Verify reader has NFC capability
    • Check reader is configured for PassFlow
  5. Phone NFC is disabled

    • User must enable NFC in phone settings
    • Test with another NFC-enabled app

How do I update pass information (name)?

Once you update the person's name in DoorFlow it will be passed on to the pass using push notifications automatically.

Can a person have PassFlow pass on multiple wallets?

No. A PassFlow pass can only appear in one wallet. Once the pass has been activated within a wallet the pass invitation will no longer allow the pass to be added to another wallet.

Security Considerations

Pass URLs are Sensitive

Wallet pass URLs should be treated securely:

  • Anyone with URL can add pass to their wallet
  • Use HTTPS for all pass delivery
  • Consider URL expiration if implementing custom delivery
  • Don't log pass URLs in plain text

Prevent Unauthorized Pass Sharing

Educate users not to share "Add to Wallet" links:

  • Passes should only be added to the authorized person's device
  • Sharing pass URLs could grant unauthorized access
  • Monitor for multiple passes added from same invitation

Comparison: PassFlow vs HID Mobile Access

Feature PassFlow HID Mobile Access
Wallet integration Apple Wallet, Google Wallet HID Mobile Access app
User experience Native wallet app Dedicated HID app
Setup complexity Simpler More complex
Apple support Limited (VAS restrictions) Full support
Android support Full support Full support
Remote revocation Partial (Android only) Full (both platforms)
Best for Android-focused, simple deployments Full mobile credential control

When to choose PassFlow

Android-heavy user base
Want native wallet integration
Simple deployment requirements
Don't need granular credential control

When to choose HID Mobile Access

Need Apple support without VAS restrictions
Enterprise-grade credential management
Full remote revocation capabilities
Already using HID infrastructure

Quick Reference

Create PassFlow credential:

bash
POST /api/3/people/{person_id}/credentials
Body: {
  "credential_type_id": 9,
  "enabled": true
}

Retrieve wallet pass URL:

bash
GET /api/3/people/{person_id}/credentials/{credential_id}
# Returns: { "wallet_pass_url": "https://wallet.doorflow.com/passes/abc123" }

Check credential status:

bash
GET /api/3/people/{person_id}/credentials/{credential_id}
# Returns: { "status": "pending|active|revoked" }

Revoke credential:

bash
DELETE /api/3/people/{person_id}/credentials/{credential_id}

Required OAuth scope: account.person