API Documentation
Complete reference for the WalletWallet API. Generate signed Apple Wallet passes with a single HTTP request.
https://api.walletwallet.dev What each field does
Edit any field and see the pass update live
Text next to the logo (top-left)
No header fields
No primary fields
No secondary fields
No back fields
Overrides color preset
Wide banner image. Use secondary fields for readable text when using this option.
Overview
The WalletWallet API lets you generate signed Apple Wallet (.pkpass) files programmatically. Send a POST request with your pass data and receive a ready-to-distribute pass file.
All requests use JSON bodies and return JSON responses, except the pass creation endpoint which returns a binary .pkpass file on success.
Authentication
All API requests require a valid API key. Include it in the Authorization header using the Bearer scheme:
Authorization: Bearer ww_live_<your_key>
API keys follow the format ww_live_ followed by 32 hexadecimal characters. You can get one instantly from the signup page.
/api/auth/usage
Returns your current monthly usage statistics. Requires authentication.
Headers
| Header | Value |
|---|---|
| Authorization | Bearer ww_live_<your_key> |
Response
{
"count": 150,
"limit": 1000,
"remaining": 850,
"resetDate": "2026-03-01",
"plan": "free"
} curl https://api.walletwallet.dev/api/auth/usage \
-H "Authorization: Bearer ww_live_<your_key>" /api/pkpass
Generates a signed Apple Wallet pass (.pkpass file). This is the primary endpoint. Requires authentication.
Headers
| Header | Value |
|---|---|
| Content-Type | application/json |
| Authorization | Bearer ww_live_<your_key> |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| barcodeValue | string | Yes | Data encoded in the barcode. Max 512 characters. |
| barcodeFormat | string | Yes | QR PDF417 Aztec Code128 |
| logoText | string | No | Text next to the logo (top-left of pass). |
| description | string | No | Accessibility text (not visible). Defaults to logoText. |
| primaryFields | array | No | Main content fields. Array of {label, value} objects. |
| secondaryFields | array | No | Fields below primary. Array of {label, value} objects. |
| headerFields | array | No | Top-right header area. Array of {label, value} objects. |
| backFields | array | No | Back of pass. Array of {label, value} objects. |
| colorPreset | string | No |
Color theme:
dark blue green red purple orange.
Defaults to dark.
|
| expirationDays | number | No |
Pass expires after this many days. Common presets:
30,
90,
365.
Any integer between
1 and
3650
is accepted.
|
| color Pro | string | No | Custom hex background color, e.g. #1e40af. Overrides colorPreset. |
| logoURL Pro | string | No | Custom logo image. Must use HTTPS — HTTP URLs are rejected. Also accepts PNG data URIs (data:image/png;base64,...). Private/internal addresses are not allowed. |
| title Legacy | string | No | Legacy shortcut. Sets primaryFields[0].value and logoText if those aren't set. |
| cardLabel Legacy | string | No | Legacy shortcut. Sets primaryFields[0].label. Defaults to CARD. |
| label Legacy | string | No | Legacy shortcut. Sets secondaryFields[0].label. |
| value Legacy | string | No | Legacy shortcut. Sets secondaryFields[0].value. |
| thumbnailURL Pro | string | No | Image shown top-right of the pass. HTTPS URL or PNG data URI. |
| stripURL Pro | string | No | Wide banner image behind the primary field. Switches pass to store card layout. HTTPS URL or PNG data URI. |
At least one of logoText, primaryFields, or title must be provided.
Response
application/vnd.apple.pkpass file. Save as .pkpass.
curl -X POST https://api.walletwallet.dev/api/pkpass \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ww_live_<your_key>" \
-d '{
"barcodeValue": "MEMBER-12345",
"barcodeFormat": "QR",
"logoText": "Membership Card"
}' \
-o membership.pkpass curl -X POST https://api.walletwallet.dev/api/pkpass \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ww_live_<your_key>" \
-d '{
"barcodeValue": "LOYALTY-98765",
"barcodeFormat": "QR",
"logoText": "Bayroast Coffee",
"description": "Loyalty card for Bayroast Coffee",
"primaryFields": [{"label": "CARD", "value": "Coffee Rewards"}],
"secondaryFields": [{"label": "TIER", "value": "Gold Status"}],
"headerFields": [{"label": "BALANCE", "value": "$25.00"}],
"colorPreset": "green",
"expirationDays": 365
}' \
-o rewards.pkpass curl -X POST https://api.walletwallet.dev/api/pkpass \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ww_live_<your_key>" \
-d '{
"barcodeValue": "VIP-001",
"barcodeFormat": "QR",
"logoText": "VIP Access",
"primaryFields": [{"label": "PASS", "value": "VIP Access"}],
"color": "#8B4513",
"logoURL": "https://example.com/logo.png"
}' \
-o vip.pkpass Barcode Formats
| Format | Type | Best For |
|---|---|---|
| QR | 2D square | General purpose, high data capacity, most common |
| PDF417 | 2D stacked | Boarding passes, ID cards, government documents |
| Aztec | 2D square | Transit tickets, compact spaces, no quiet zone needed |
| Code128 | 1D linear | Retail, inventory, shipping labels |
Color Presets
Available on all plans. Use the colorPreset field.
Pro plan: Use the color field with any hex value (e.g. #1e40af) to set a fully custom background color.
Rate Limits
| Plan | Passes / Month | Custom Color | Custom Logo | Price |
|---|---|---|---|---|
| Free | 1,000 | No | No | $0 |
| Pro | 100,000 | Yes | Yes | $19/mo |
Usage resets on the 1st of each month (UTC). You can check your current usage at any time via the /api/auth/usage endpoint.
When you exceed your limit, the API returns a 429 response with the reset date:
{
"error": "Rate limit exceeded",
"resetDate": "2026-03-01",
"message": "Monthly limit reached. Resets on 2026-03-01"
} Errors
All error responses return JSON with an error field:
{
"error": "Error message describing the issue"
} HTTP Status Codes
| Code | Description |
|---|---|
| 200 | Success |
| 400 | Bad request — invalid input, malformed JSON, or validation failure |
| 401 | Unauthorized — missing or invalid API key |
| 404 | Not found — endpoint does not exist |
| 405 | Method not allowed — wrong HTTP method |
| 429 | Rate limit exceeded — monthly quota used up |
| 500 | Internal server error |
Common Validation Errors
| Cause | Error Message |
|---|---|
| Missing required field | barcodeValue is required |
| Invalid barcode format | barcodeFormat must be one of: QR, PDF417, Aztec, Code128 |
| Title too long | title must be 64 characters or less |
| Label without value | label and value must both be provided together |
| Custom color on free plan | color is only available on the Pro plan |
| Invalid expiration | expirationDays must be between 1 and 3650 |
Code Examples
const response = await fetch('https://api.walletwallet.dev/api/pkpass', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ww_live_<your_key>'
},
body: JSON.stringify({
barcodeValue: 'TICKET-789',
barcodeFormat: 'QR',
logoText: 'Event Ticket',
primaryFields: [{ label: 'EVENT', value: 'Concert' }],
secondaryFields: [{ label: 'Seat', value: 'A-23' }]
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error);
}
// Browser: trigger download
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'ticket.pkpass';
a.click();
// Node.js: save to file
// const fs = require('fs');
// const buffer = Buffer.from(await response.arrayBuffer());
// fs.writeFileSync('ticket.pkpass', buffer); import requests
response = requests.post(
'https://api.walletwallet.dev/api/pkpass',
headers={
'Content-Type': 'application/json',
'Authorization': 'Bearer ww_live_<your_key>'
},
json={
'barcodeValue': 'ORDER-456',
'barcodeFormat': 'Code128',
'logoText': 'Order Pickup',
'primaryFields': [{'label': 'ORDER', 'value': 'Pickup'}],
'secondaryFields': [{'label': 'Order #', 'value': '456'}]
}
)
response.raise_for_status()
with open('order.pkpass', 'wb') as f:
f.write(response.content) require 'net/http'
require 'json'
require 'uri'
uri = URI('https://api.walletwallet.dev/api/pkpass')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/json'
request['Authorization'] = 'Bearer ww_live_<your_key>'
request.body = {
barcodeValue: 'MEMBER-001',
barcodeFormat: 'QR',
logoText: 'Gym Membership',
primaryFields: [{ label: 'MEMBER', value: 'Premium' }]
}.to_json
response = http.request(request)
File.binwrite('membership.pkpass', response.body) $ch = curl_init('https://api.walletwallet.dev/api/pkpass');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ww_live_<your_key>'
],
CURLOPT_POSTFIELDS => json_encode([
'barcodeValue' => 'COUPON-50OFF',
'barcodeFormat' => 'QR',
'logoText' => 'Discount Coupon',
'primaryFields' => [['label' => 'COUPON', 'value' => '50% Off']]
])
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
file_put_contents('coupon.pkpass', $response);
} package main
import (
"bytes"
"encoding/json"
"io"
"net/http"
"os"
)
func main() {
body, _ := json.Marshal(map[string]interface{}{
"barcodeValue": "PASS-999",
"barcodeFormat": "QR",
"logoText": "Access Pass",
})
req, _ := http.NewRequest("POST", "https://api.walletwallet.dev/api/pkpass", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer ww_live_<your_key>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := os.Create("access.pkpass")
defer out.Close()
io.Copy(out, resp.Body)
} Testing Your Pass
- Save the API response to a
.pkpassfile - macOS: Double-click the file to preview in Finder
- iOS: AirDrop or email the file to your device
- The pass will prompt to add to Apple Wallet
Tip: Passes are signed with Apple certificates, so they work immediately on any iOS device — no additional configuration needed.
Free plan includes 1,000 passes/month