Skip to main content

Livepasses Python SDK

Official Python SDK for the Livepasses API — the leading API-first digital wallet pass platform.

Generate and manage Apple Wallet and Google Wallet passes for events, loyalty programs, coupons, and more.

Installation

pip install livepasses

Requirements: Python 3.10+

Quick Start

from livepasses import Livepasses, GeneratePassesParams, PassRecipient, CustomerInfo, BusinessData

client = Livepasses("your-api-key")

result = client.passes.generate(GeneratePassesParams(
    template_id="tmpl-001",
    passes=[PassRecipient(
        customer=CustomerInfo(first_name="Alice", last_name="Smith", email="alice@example.com"),
        business_data=BusinessData(section_info="VIP", row_info="A", seat_number="1"),
    )],
))

for p in result.passes:
    print(f"Pass: {p.id} — Apple: {p.platforms.apple.add_to_wallet_url}")

Authentication

Get your API key from the Livepasses Dashboard.

from livepasses import Livepasses

client = Livepasses("lp_live_your_api_key")

Pass Generation

Single Pass

from livepasses import (
    GeneratePassesParams, PassRecipient, CustomerInfo, BusinessData, BusinessContext, EventContext
)

result = client.passes.generate(GeneratePassesParams(
    template_id="tmpl-001",
    business_context=BusinessContext(
        event=EventContext(event_name="Concert", event_date="2026-06-15T20:00:00Z"),
    ),
    passes=[PassRecipient(
        customer=CustomerInfo(first_name="John", last_name="Doe", email="john@example.com"),
        business_data=BusinessData(section_info="Floor", row_info="B", seat_number="15"),
    )],
))

Batch Generation with Polling

For large batches, the API processes passes asynchronously. generate_and_wait handles polling automatically:

from livepasses import GenerateAndWaitOptions

result = client.passes.generate_and_wait(
    GeneratePassesParams(
        template_id="tmpl-001",
        passes=[...],  # hundreds of recipients
    ),
    options=GenerateAndWaitOptions(
        poll_interval=2.0,
        max_attempts=150,
        on_progress=lambda status: print(f"Progress: {status.progress_percentage}%"),
    ),
)

print(f"Generated {len(result.passes)} passes")

Batch Status (Manual Polling)

If you need more control over polling, use generate + get_batch_status:

initial = client.passes.generate(GeneratePassesParams(
    template_id="tmpl-001",
    passes=[...],
))

if initial.batch_operation:
    import time
    batch_id = initial.batch_operation.batch_id
    status = client.passes.get_batch_status(batch_id)
    while not status.is_completed:
        time.sleep(2)
        status = client.passes.get_batch_status(batch_id)
        print(f"Progress: {status.progress_percentage}%")
    print(f"Completed: {status.statistics.successful} successful, {status.statistics.failed} failed")

Pass Lifecycle

Lookup

from livepasses import LookupPassParams

pass_info = client.passes.lookup(LookupPassParams(pass_id="pass-001"))
print(f"Valid: {pass_info.is_valid}, Status: {pass_info.status}")

Validate

validation = client.passes.validate("pass-001")
print(f"Can redeem: {validation.can_be_redeemed}")

Redeem

from livepasses import RedeemPassParams, RedemptionLocation

# Generic redemption
result = client.passes.redeem("pass-001")

# Redemption with location; free-text notes go in metadata
result = client.passes.redeem("pass-001", RedeemPassParams(
    location=RedemptionLocation(name="Store #1", latitude=4.6097, longitude=-74.0817),
    metadata={"note": "Walk-in customer"},
))

Check-in (Events)

Coordinates go inside location:

from livepasses import CheckInParams, RedemptionLocation

result = client.passes.check_in("pass-001", CheckInParams(
    gate="Main Gate",
    location=RedemptionLocation(name="Main Gate", latitude=4.6097, longitude=-74.0817),
))

Redeem Coupon

from livepasses import RedeemCouponParams, RedemptionLocation

result = client.passes.redeem_coupon("pass-001", RedeemCouponParams(
    location=RedemptionLocation(name="Store #42"),
    transaction_amount=45000,
    transaction_currency="COP",
    metadata={"note": "Applied to order #12345"},
))

The API refuses any body field it does not declare with a 400, so there is no notes parameter: put free text in metadata.

Update a Pass

Change fields on one pass and, optionally, show the holder a message. updated_fields keys are the pass type's updatable field names as the API spells them (camelCase) and are sent exactly as written. Send a non-empty updated_fields, a non-empty message_body, or both.

from livepasses import UpdatePassParams

client.passes.update("pass-001", UpdatePassParams(
    updated_fields={"memberTier": "Platinum", "validUntil": "2026-12-31"},
    reason="Tier upgrade",
    message_body="Congratulations on reaching Platinum!",
))

# Silent change: no banner on the holder's phone
client.passes.update("pass-001", UpdatePassParams(
    updated_fields={"memberTier": "Platinum"},
    notify=False,
))

Push a scoped update

Push field updates to all eligible passes of a template:

from livepasses import PushTemplatePassesParams

client.passes.push_template("template-id", PushTemplatePassesParams(
    updated_fields={"gate": "Gate C"},
    reason="Event-wide gate change",
))

Pass Types

Event Tickets

business_data = BusinessData(
    section_info="VIP", row_info="A", seat_number="1",
    ticket_type="VIP", price=150.00, currency="USD",
)

Loyalty Cards

from livepasses import LoyaltyTransactionParams

business_data = BusinessData(
    membership_number="MEM-12345",
    current_points=500,
    member_tier="Gold",
)

# Earn points
result = client.passes.loyalty_transact("pass-001", LoyaltyTransactionParams(
    transaction_type="earn", points=100, description="Purchase at Store #42",
))

# Spend points
result = client.passes.loyalty_transact("pass-001", LoyaltyTransactionParams(
    transaction_type="spend", points=50, description="Redeemed: Free coffee",
))

Coupons

business_data = BusinessData(
    promo_code="SUMMER2026",
    campaign_id="camp-001",
    max_usage_count=1,
)

Templates

List and get

from livepasses import ListTemplatesParams

# List
templates = client.templates.list(ListTemplatesParams(status="Active"))

# Get details
detail = client.templates.get("tmpl-001")

Create a template

from livepasses import CreateTemplateParams

template = client.templates.create(CreateTemplateParams(
    name="VIP Event Pass",
    description="Premium event ticket template",
    # The block you send decides the template type: "event" makes an event ticket.
    business_features={
        "event": {
            "eventName": "Aurora Music Fest",
            "eventDate": "2030-06-15T20:00:00Z",
            "venueName": "Aurora Arena",
            "showSeatNumbers": True,
        },
    },
))
print(f"Created: {template.id} — {template.name}")

Update a template

from livepasses import UpdateTemplateParams

updated = client.templates.update("tmpl-001", UpdateTemplateParams(
    name="VIP Event Pass v2",
    description="Updated premium event ticket template",
))

Activate / Deactivate

client.templates.activate("tmpl-001")
client.templates.deactivate("tmpl-001")

Webhooks

from livepasses import CreateWebhookParams

webhook = client.webhooks.create(CreateWebhookParams(
    url="https://your-app.com/webhooks/livepasses",
    events=["pass.generated", "pass.redeemed", "pass.sharing_suspected"],
))
print(f"Secret: {webhook.secret}")  # use this to verify webhook signatures

# List all
webhooks = client.webhooks.list()

# Delete
client.webhooks.delete(webhook.id)

Error Handling

Every refusal the API can make answers with a real HTTP status (400/403/404/409/422/429/500/502/503) and the envelope {success:false,data:null,error:{code,message,details,timestamp,traceId,fields?}}. The SDK raises a typed error from any status or body — including a status-only response it can't parse as JSON, such as a challenge 401 or a proxy error. All errors are typed for precise except handling:

from livepasses import (
    LivepassesError,
    AuthenticationError,
    ValidationError,
    ForbiddenError,
    NotFoundError,
    RateLimitError,
    QuotaExceededError,
    BusinessRuleError,
)

try:
    result = client.passes.generate(params)
except AuthenticationError:
    print("Invalid API key")
except ValidationError as e:
    print(f"Validation failed: {e.details}")
except ForbiddenError:
    print("Insufficient permissions for this operation")
except NotFoundError:
    print("Template or pass not found")
except RateLimitError as e:
    print(f"Rate limited — retry after {e.retry_after}s")
except QuotaExceededError:
    print("API quota exceeded — upgrade your plan")
except BusinessRuleError as e:
    print(f"Business rule violation: {e}")
except LivepassesError as e:
    # Catch-all for any other API error
    print(f"API error [{e.code}]: {e} (HTTP {e.status})")

Error codes

Use ApiErrorCodes for programmatic error code comparisons:

from livepasses import LivepassesError, ApiErrorCodes

try:
    client.passes.redeem("pass-001")
except LivepassesError as e:
    if e.code == ApiErrorCodes.PASS_ALREADY_USED:
        print("This pass has already been redeemed")
    elif e.code == ApiErrorCodes.PASS_EXPIRED:
        print("This pass has expired")
    elif e.code == ApiErrorCodes.TEMPLATE_INACTIVE:
        print("The template for this pass is inactive")
    else:
        print(f"Unhandled error: {e.code}")

Exception hierarchy

Exception Typical status When
AuthenticationError 401 Invalid, expired, or revoked API key
ValidationError 400 Request validation failed — carries fields: dict[str, list[str]] | None, the field path -> validation messages map; keys are the API's camelCase field paths (e.g. operations[0].path), not converted to snake_case
ForbiddenError 403 Insufficient permissions
NotFoundError 404 Resource not found
RateLimitError 429 Rate limit exceeded
QuotaExceededError 422 API quota or subscription limit exceeded

The status column is the one each class usually carries; the error's .status is always the response's real HTTP status. A 401 is always the authentication error and a 403 always the forbidden error, whatever error.code says. A 409 without a mapped code, and every 5xx, raise the base LivepassesError. | BusinessRuleError | 422 | Business rule violation (pass expired, already used, etc.) |

Pagination

Manual pagination

from livepasses import ListPassesParams

page1 = client.passes.list(ListPassesParams(page=1, page_size=50))
print(f"Page 1 of {page1.pagination.total_pages} ({page1.pagination.total_items} total)")

# Get next page
if page1.pagination.current_page < page1.pagination.total_pages:
    page2 = client.passes.list(ListPassesParams(page=2, page_size=50))

Auto-pagination

Iterate over all passes without manual page management:

for p in client.passes.list_auto_paginate():
    print(f"{p.id}: {p.status}")

Configuration

client = Livepasses(
    "your-api-key",
    base_url="https://api.livepasses.com",  # default
    timeout=30.0,                            # seconds, default
    max_retries=3,                           # default
)

Automatic Retries

The SDK automatically retries:

  • 429 Too Many Requests — honors Retry-After header
  • 5xx Server Errors — exponential backoff with jitter, only for idempotent methods (GET, HEAD, PUT, DELETE). A POST that hits a 5xx is not retried — no SDK sends an Idempotency-Key, so a retry could re-run a non-idempotent operation.

Type Checking

This package ships with a py.typed marker (PEP 561) and is fully compatible with mypy and pyright.

mypy your_app.py   # full type safety

Examples

See the examples/ directory for runnable scripts:

Run any example with:

export LIVEPASSES_API_KEY="your-api-key"
python examples/generate_passes.py

License

MIT

Release files for livepasses 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for livepasses 0.3.0
File Size Uploaded
livepasses-0.3.0.tar.gz 32.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for livepasses 0.3.0
File Interpreter ABI Platform
livepasses-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 60.0 kB

Release files / livepasses-0.3.0.tar.gz

Download URL livepasses-0.3.0.tar.gz
Size 32.1 kB
Tags Source
SHA-256 checksum
How to use checksums
b161d0803a9b191d5a7285f5faf2a619d51acc826f80db76e7f60894607336b6
BLAKE2b-256 checksum
How to use checksums
96e835931860bd32b8c501f139fdd759d1b0e715d74eac06b548e6d9d383ae4f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / livepasses-0.3.0-py3-none-any.whl

Download URL livepasses-0.3.0-py3-none-any.whl
Size 27.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6fbf940f2e096f61bbfe00ca709b3d3a4884104bee53b11b3e6cb314a1ede3f1
BLAKE2b-256 checksum
How to use checksums
93001bd54b4d311ab81e19bce5e98929040851258b98f15f48ff0990f9809b8f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page