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-Afterheader - 5xx Server Errors — exponential backoff with jitter, only for idempotent methods (
GET,HEAD,PUT,DELETE). APOSTthat hits a5xxis not retried — no SDK sends anIdempotency-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:
- generate_passes.py — End-to-end pass generation, lookup, validation, and check-in
- loyalty_workflow.py — Loyalty card lifecycle: generate, earn points, spend points, update tier
- coupon_workflow.py — Coupon pass generation and redemption
- template_management.py — CRUD operations on pass templates
- webhook_setup.py — Register, list, and manage webhooks
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)
| File | Size | Uploaded | |
|---|---|---|---|
| livepasses-0.3.0.tar.gz | 32.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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
|