Application Programming Interface (API)

PassSource has been helping people create Apple Wallet passes since they were announced by Apple. We've had lots of experience with different industries and users and have identified several features that are unique to PassSource that allow adding significant power and capabilities to our templates without having to write a single line of code. Our API was always designed to be easy to use and test and we've extended this philosophy to our advanced features that are available to our professional accounts.

Table of Contents


PassSource uses templates to create prototypical passes from which passes can be created. Changes to the template will propagate to passes when they are updated (if no update is sent or requested, they won't see the change). If a pass has overridden the template value and the original template value changes, this will not change the override (i.e. it will still be overridden with the pass value).

Advanced Features

PassSource templates have some advanced features unavailable anywhere else that allow powerful workflows without having to write any code. You can use these features with or without using the API and many of the features eliminate the need to use the API.

API Paths

Most of the attributes of a pass are exposed via our API Paths which are used to identify values and fields. These are used for the PassSource advanced features as well as in API calls.

You can find the API path for a field in the template editor. Click on the (i) button to the right of a field to get advanced options and see the API path.

The path for additional properties are simply the key.

In addition, we've made it easier to change the pass type and the transit type by exposing the following paths:

"Magic" Strings

We've implemented something we call "magic" strings which is a fancy way of saying, anytime we have a string in a pass, we automatically look for certain special strings and automatically replace the values when creating the pass.

You can use any API path as a magic string by surrounding the path in curly brackets. For example, if you wanted to include the points balance in some text on a backfield, you could include {structure_headerFields_points_value} in the text, and when the pass is created, it will be replaced with whatever value is in the structure_headerFields_points_value field. Or if you wanted to have the user's name encoded in the barcode message, you might set the barcode_message value to {structure_secondaryFields_name_value}

Note that to prevent users from using magic strings in registration fields, curly brackets are escaped. However, if you're using the API to set these values, they will not be escaped if you use your clientHash to authenticate the edit.

In addition to paths, we also have some special strings that are available to use:

rootPathThis is replaced with the PassSource URL (currently This is useful if you are switching between the beta environment or if you're using our domain masking service or just so you don't have to worry about typos with the URL.
clientHashThis is a token used for authentication. Protect this as you would your password as it can be used to grant full account permissions. Typically, you would only use this within the pass for special additional PassSource features like the psInformationURL in the scanning feature, on other internal pages, or when using triggers to call the API and this should never be included in any pass fields (we do allow this for testing and edge cases so be careful).
clientIdIf you want the user to go to branded white-label pages, you may want to provide your client ID. This is much safer to include than the client hash as there is nothing that can be exposed other than public branding information.
templateHashThe template hash for the pass. Typically used if you are creating a link to a new registration page for a share link or if you're executing triggers that call API functions that require the template hash.
hashedSerialNumberThe hashed serial number to this specific pass. This is often used to link to the create or registration page and used whenever referencing a specific pass instance in the API.
timestampSet to the current timestamp in ISO 8601 time format (GMT time). Can be used for example to set a membership start date in an onRegister trigger.
psPassRegisteredResolves to boolean true if the pass has been registered and false otherwise. Can be used in conditions or exports.
psPassInstalledResolves to boolean true if the pass has been installed and false otherwise. Can be used in conditions or exports.
psPassScannedResolves to boolean true if the pass has been scanned using our scanning feature or the API and false otherwise. Can be used in conditions or exports.
psPassDeletedResolves to boolean true if the pass has been deleted and false otherwise. Can be used in conditions or exports.
psPassURLA convenience path that maps to {rootPath}/pass/create.php?hashedSerialNumber={hashedSerialNumber}&.
psRegisterURLA convenience path that maps to {rootPath}/pass/register.php?hashedSerialNumber={hashedSerialNumber}&.
psEditorURLA convenience path that maps to {rootPath}/pass/editor.php?hashedSerialNumber={hashedSerialNumber}&clientHash={clientHash}&. Be mindful when using this as this will contain your clientHash which can be used to update passes and templates and gives full access to your account. Use this only with API calls or as part of triggers as this should not be included on the pass itself. If psEditCode is in the template, instead of the clientHash, it will include the templateId and the psEditCode which is better if you want to restrict access to a specific template. This is a convenience path for setting psInformationURL for the scanning feature but should not be used if using the psAllowOpenScan option and not using the psEditCode as this could expose your clientHash or psEditCode. If this is used with :PassScannerConfig or in the psPortalHeader, it will not include the hashedSerialNumber.
psScanURLA convenience path that maps to "{rootPath}/pass/scan.php?clientHash={clientHash}&". This is a convenience path for setting up the Pass Scanner app with a custom URL. Be mindful when using this as this will contain your clientHash which can be used to update passes and templates and gives full access to your account. Use this only with API calls or as part of triggers as this should not be included on the pass itself. If psEditCode is in the template, instead of the clientHash, it will include the templateId and the psEditCode which is better if you want to restrict access to a specific template.
psPublicScanURLA convenience path that maps to "{rootPath}/pass/scan.php?hashedSerialNumber={hashedSerialNumber}&" (without any authentication). Set the barcode message to this to use our public scanning feature.
psScannerConfigURLA convenience path that expands to {psScanURL:PassScannerConfig} for configuring the Pass Scanner app. Be mindful when using this as this will contain your clientHash which can be used to update passes and templates and gives full access to your account. Use this only with API calls or as part of triggers as this should not be included on the pass itself. If psEditCode is in the template, instead of the clientHash, it will include the templateId and the psEditCode which is better if you want to restrict access to a specific template.
PSColorGreenA convenience constant for our default approved scan color. Currently resolves to rgb(77,186,11) but may be changed in the future.
PSColorYellowA convenience constant for our default warning scan color. Currently resolves to rgb(187,187,58) but may be changed in the future.
PSColorRedA convenience constant for our default error scan color. Currently resolves to rgb(187,49,58) but may be changed in the future.
Note that our special PassSource additional properties are also available as magic strings.

A note on hashes: If a hash value is used at the end of a URL, add an ampersand (&) at the end if you include this in a back field or in an email our hashes may end with a comma (,) which may not be included in link detection and may cause problems.

Magic strings also support some special transformations. For example, to get the URLEncoded value for the scan URL, you could use {psScanURL:URLEncoded}.

Similar to a ternary operator in many programming languages, allows displaying one value if the root value is true and another value if the root value is false. Can use variables in place of inline strings (but cannot include additional levels of magic strings). Example usage: {voided:Ternary,"Pass Voided","Pass Good"} or {booleanTrue:Ternary,"YES","NO"}
:URLEncodedPerforms a URL encoding on the string before replacing. Useful when specifying a value as a parameter in a URL.
:TrimmedTrims whitespace from either side of the string. For example, you could set up a field fullName with the value "{firstName} {lastName}" and use {fullName:Trimmed} to ensure there aren't extra spaces should the user only have a first or last name.
:StripWhitespaceRemoves all whitespace from the string. Great if you want users to enter a barcode number with spaces for readability but the actual barcode should not include spaces.
:EscapedQuotesEscapes double quotes in the string by adding a backslash before the double-quote.
:DateFormat,"format string"
Converts a date into the desired format. Format string should use the PHP date format options. You can use "c" to specify the ISO 8601 format that needs to be used for things like expiration dates or event dates. Note format string cannot contain line breaks or curly brackets.
Pads the base string with the padString to the numberOfCharacters length. padType is an optional integer and will be left if < 0 (the default), right if > 0, and on both sides (centering the text) if padType = 0.
:PassScannerConfigGenerates a URL for configuring the Pass Scanner app (sets the customURL parameter to the value stored in the key). This works with our convenience URLs like psScanURL or psEditorURL and maps to "passscanner://?customURL={KEYED_VALUE:URLEncoded}" as well as including configuration to match the first beacon if specified.
Input a string to see the example output for the demo coupon pass:

Registration pages

Our famous registration pages have gotten even better! You can create dropdowns and checkboxes for users to fill out and additional fields can also be set up for user registration. Validation is performed on registration fields to ensure that required fields are filled out and in the correct format. When the user registers, we automatically flag the pass as registered and execute any onRegister triggers you have set up. Further, you can customize the registration page and the registered pages using PassSource additional fields.

To make fields user editable, in the template editor, click the (i) button next to the field and check the User Editable box.

To enable registration, you will have to provide users with the templateHash or make the template public. Having the templateHash without the clientHash only allows users to edit user editable fields. If no user editable fields exist, the registration page will automatically re-direct to the creation page.

You may automatically pre-fill user editable fields by setting the values as GET parameters in the URL to the creation page. This page also supports sending the values via POST variables or encapsulated as a JSON string passed to a JSON variable.

To take people to a registration page to update their pass, simply direct them to {psRegisterURL} on the back of the pass. Use the psRegistrationMessage and psRegisteredMessage additional properties to customize the registration and registered pages.

For example, link to the registration page by creating a backField with the attributed value set to: <a href="{psRegisterURL}">Register your pass!</a> and the normal value to: Register your pass by tapping this link: {psRegisterURL}

Note that if you assign the value to a container path using the API (for example, setting the barcode or structure_headerFields), and any of the sub-fields are user editable, accessing the registration page of a pass will automatically assign the user editable values and remove the override value of the pass so that the user can edit the information again.


You can add validation to user editable fields. Validation will use one of our pre-coded validators to verify that the user-submitted information is valid. The Validation field should be a comma separated list of validators without any spaces. With the exception of VRequired, the validators are not applied to an empty string.

The available validators are:

VRequiredMakes sure the value is set to something.
VEmailChecks to make sure the value is a properly formatted email address
VDateMust be a properly formatted date, but can be anything PHP stringtotime function will interpret.
VPhoneA properly formatted phone number. This must be only a numeric number but +,x() characters are okay.
VNumericMust be able to be converted to a number.
VAlphaOnly a-z and A-Z are supported. No special characters or numbers.
VAlphaNumericOnly a-zA-Z0-9 are supported.
VDecimalMust be able to be parsed as a decimal digit. Uses PHP is_float function.
VIntegerMust be an integer number.
VStateCodeMust be a valid US State code.
VZipMust be numeric (but can have -) and be 5-10 digits (not including the -)
VTrueMust evaluate to boolean true (typically used with Checkboxes).
VFalseMust evaluate to boolean false (typically used with Checkboxes).

Pass Editor

We've always had some simple tools for editing passes from our Pass Scanner app, but now it's even easier to customize this page for your individual needs. Simply enter the API paths to the fields you want to edit as an additional field named psEditorPaths.

You can get to the pass editor via the following link:{hashedSerialNumber}&clientHash={clientHash} (You can use the templateId and editCode instead for more limited authentication). We suggest including this path as the value of the psInformationURL additional field when using the scanning feature (the clientHash is safe to include here if you're not using the public scan feature. You can also use {psEditorURL} magic string to automatically expand this URL and it will include the client hash or the templateId and psEditCode if set).

Template Portal

Professional accounts now have an easy way to view template stats and drill down to lists of individual passes. You can even expose fields here for quick template updates to push to all the passes. For example, if you have a marketing message you edit to push notifications to users, you could expose that field here for quick editing without going through the template editor. Similar to the editor, simply enter the API paths to the fields you want to edit as an additional field named psPortalUpdatePaths. Note that unlike the editor, these updates will be applied to the master template and then a push update will be sent to all installed passes.

Batch Create Tool

We have a tool for quickly creating multiple passes by uploading a CSV file. The file should contain a templateId column indicating the ID of the template you'd like to use. The other column headings should be API paths that you want to set in the template.

Please make sure the CSV file is UTF-8 encoded, does not contain any blank rows or hidden characters, and make sure the column names do not contain whitespace and match the capitalization of your API paths exactly to prevent any issues.

By default, the tool will generate the creation links you can send via email so that passes are only created when the link is visited. The fields in the spreadsheet will be validated to make sure they actually exist as user editable fields since that is required for dynamic links to work. You can also choose to use our link shortener to make it harder for end users to tweak the links.

You can also create the passes ahead of time which will append a hashedSerialNumber and psPassURL column to each row for use in an email tool or database or for use with our API calls. You can also simply specify a number of passes to create from a template and we will generate a list of hashed serial numbers.

If you want an even shorter link, you can replace "" with "".

We've provided an example file that modifies the logoText, barcode_altText, and background color for a generic pass (you will need to copy the generic pass and use your template ID to test).

One final note: If you're using non-ASCII characters (for example accented characters or chinese characters), be sure that the file is encoded in UTF-8 format or the tool may stop midway through. If you notice a different number of passes in the export file than you expect, check the row after the last row you see and check for special characters.

Batch Export Tool

Professional accounts now have an easy way to export the pass data for a template. It's flexible so that you can get exactly the data you need and you can build up custom fields and information using our additional and path features. There will be a Export Data button on the template portal if a psExportPaths additional field is present. This will download a CSV file with the requested data with one row per pass. Note that for performance reasons, the tool will only download in batches of 1000 and will order based on creation. To fetch more than 1000, add &start=START_INDEX to the URL where START_INDEX is the row you want to start with (for example, to fetch page 2, set START_INDEX to 1000).

Whitelabel services

If you have a reseller account, you can specify a logo and color that will be used to brand the entire site. Pages will not include any PassSource branding and ads will be hidden. To see an example, add &clientId=6 to any page to see what it looks like with our demo account branding. If you're interested in setting up a reseller account, please contact us.

The Additional JSON option on the Advanced Settings page allows you to specify additional variables available to all your templates. Settings are specified as a valid mapping of keys and values in JSON object notation. Note that these values will always override anything specified at the template or pass level. You may also use this to specify additional customizations and options for your account:

psDashboardShould be a JSON object map of labels to URLs. This will add additional menu options on the client's dashboard (useful for adding quick links in your own account). If the URL does not start with http, it will automatically be prefixed with {rootPath}.
psHelpTitleCustomize the help page title.
psHelpInfoCustomize the help page content.
psLoginHeaderCustomize the login screen.
psScanErrorTextMask any error while scanning with a single string (for example to replace our default English error messages with a different string).
pageTitleSets the title metadata value which is shown in link previews and browser headers.
siteDescriptionSets the header metadata description tag for the site.

PassSource Conditions

Apple Wallet fields and the barcode can be set to display conditionally based on certain criteria. The field/barcode will only be included on the pass if the conditions evaluate to true. This can be useful if you only want something to be on the pass some of the time, but not all the time. For example, you might want to show a deal only after the pass has been scanned.

To specify conditions, click the (i) button for the field to disclose the advanced options and you will see a PassSource Conditions field.

Conditions can also be used to selectively execute triggers.

You may specify conditions using the following grammar:

<boolean> → AND
<boolean> → OR
<comparator> → !<comparator>allows testing X <= Y by doing !(X > Y)
<comparator> → =
<comparator> → >
<comparator> → <
<test> → isEmptyis 0 or false or "" or null
<test> → isSetis not null
<test> → !<test>
<magic> → {magic}a magic string that will be evaluated and the evaluated value will be used for tests
<valueString> → <magic>
<valueString> → a number
<valueString> → a boolean value
<valueString> → a string valuea string should be denoted by surrounding in double quotes
<conditionString> → <valueString>
<conditionString> → <valueString> <test>
<conditionString> → registered | scanned | deleted | installedregistered, scanned, deleted, and installed are special keywords that are evaluated only if present (this has been deprecated since there are now magic strings for these 4 states)
<conditionString> → !<conditionString>for negating a condition
<conditionString> → (<conditionString>)for grouping conditions and nesting evaluation
<conditionString> → <conditionString> <boolean> <conditionString>for joining multiple conditions
<conditionString> → <valueString> <comparator> <valueString>if either valueString is a string, the values are compared as strings, otherwise they are compared numericly

See below for examples.

PassSource Triggers

Triggers are a powerful new feature exclusive to PassSource that allows you to build complex workflows without writing a single line of code. Triggers allow you to hook into events like pass registration, scanning, and updates. PassSource logs these events for the lifecycle of a pass, and with triggers, you can get pass updates, make changes to a pass, or more deeply integrate with a custom service exactly when you want to. Currently, the trigger action is defined as a JSON dictionary, but we will be adding a more friendly visual editor in the future.

The following is a list of access types and their corresponding trigger keys:

Trigger and ValueWhen Logged
onCheck 0Triggered by a call to the trackPassAccess API (which is called by the Pass Scanner app).
onCreate 1When the serial number is generated for a pass (guaranteed to execute exactly once per pass).
onInstall 2Logged by the web service whenever a pass is installed on a device. Repeated installs or installs on other devices will log additional entries. Triggers onInstall triggers regardless if the pass has already been installed.
onDeviceUpdate 3Logged when user invokes a manual update by doing the pull-to-refresh on the back of the pass. To prevent issues, additional requests within 30 seconds are ignored.
onRegister 4This is called if the registration form is submitted. Add the condition !registered to only execute the first time the registration form is submitted.
n/a 5Logged by the updatePassesForTemplate and pushPassUpdate API calls. The former is used by the "Update All Passes" button on the template editor and the template portal when submitting exposed fields. There are no associated triggers as this is not a user-driven event. Also, will not be logged if the update includes more than 100 passes.
onDelete 6Logged when the user deletes a pass from a device. Note that there is no guarantee that this will be called when a pass is deleted and should not be depended on for determinining if a user has a pass installed or not.
onScan 7Triggered by a call to the trackPassAccess API (which is called by our scanning feature).
onEdit 8Triggered when submitting the editor form. Use to automatically process pass before sending updates to the user.

When a trigger is encountered, it looks through the template for a set of actions and executes them in order. Each trigger action should be a dictionary with an action key and some additional keys depending on the action. The following lists the valid actions and thier properties:

ActionAdditional Keys and Description
urlmagic strings will be replaced and URL encoded
resultsPath(optional) the results of the URL will be assigned to this path if specified
Makes a request to the specified URL. You can use this to call out to web services or even make PassSource API calls. The results of the request are ignored unless resultsPath is specified. If there is a problem, use the API to set the psTriggerError additional value of the pass to a string. If this is set, no further triggers will be executed and the event will not be logged. If this is done in an onRegister trigger, the error will be displayed to the user.
targetPathan API path
valuethe new value to set (magic will be applied)
Sets the value of the field at targetPath to value.
targetPathan API path
amountthe amount to increment the value (specify a negative number to decrement)
Increments the value of the field by amount. The field will be forced to a number if it is not already. You can also use this to decrement a field by passing a negative value.
targetPathan API path to a field with a JSON array of triggers
This can be used to run a set of triggers which you may want to call from multiple triggers (like onCreate, onRegister, and onEdit). Think of this like running a sub-routine of triggers. The value of this field should be a valid JSON array of triggers. Example
In addition to the listed keys, all triggers also support a PSConditions key to specify conditions that will be evaluated to determine if trigger is executed or skipped.

To see an example of how a trigger action JSON dictionary is written, check out our examples. A simple one changes the color of a pass when scanned.

PassSource Additional Properties

Our templates allow the setting of additional properties that are not included in the pass itself so that you can have hidden/private values.

Additional fields have a key and a value which can be set to be user editable. The key can be used wherever we use API paths but the values are generally hidden from users. You can use these fields to build databases and applications on top of the PassSource platform without having to host your own database and when combined with PassSource Triggers, many common workflows can be created without any custom code or services.

You can add any keys except keys that are already used by the pass (such as barcode, backgroundColor, logoText, coupon, etc.). They must be unique which is why we suggest using prefixes to identify your keys.

We have pre-defined several special keys that add additional functionality if set as additional parameters:

psPassDescriptionIf set, this will be used in pass lists and on the editor page. If this is not set, we'll set it for you, so you can use this value even if it isn't set up as an additional field. This is also used as the descriptive label in the Pass Scanner app.
psPathMigrationsA JSON object mapping old paths to new paths so if you decide to move structure_auxilliaryFields_myField_value to structure_secondaryFields_myNewField_value, you can specify this map so user's data will properly migrate when updated or fetched: {"structure_auxilliaryFields_myField_value": "structure_secondaryFields_myNewField_value"}
psIndexPathsA comma separated list of paths that also serve as a key to ensure that passes created are unique. Attempting to create a pass with the same value as an existing pass with the same index value will result in the original pass being returned instead.
psRegistrationMessageTo further customize the user's registration experience, this value will appear at the top of the registration form and may include HTML.
psRegisteredMessageThis appears on the page that appears after the user registers or updates their pass and may include HTML. Set this value in an onRegister trigger to provide custom messages based on the user's registration data.
psEditorPathsA comma-separated list of paths to use with our editor tool. We suggest setting the psInformationURL to {psEditorURL} to use this editor feature with our scanning feature.
psEditCodeAllows specifying a code that can be used to edit passes for a specific template without having the clientHash. If this is specified in the template, convenience URLs (like psEditorURL) will include this and the template ID instead of your clientHash to restrict authentication based on a template. Having the psEditCode and the template ID, someone could edit the values of a pass but cannot make changes to the template or edit the values of passes based on other templates. If this is present, there will be an additional "Configure Pass Scanner App" button in the PassSource Settings for the template.
psEditorHeaderHTML text to customize the appearance of the editor page. Defaults to <h2>{psButtons}{psPassDescription}</h2> (note psButtons is not a normal magic string but is replaced with the appropriate buttons for this page.
psPortalHeaderHTML text to customize the appearance of the portal page. Defaults to <h2>Template Information</h2>
psPortalUpdatePathsA comma-separated list of paths that you want to expose on the portal page for quick updates to the template. It's like your own personal registration form for the template!
psExportPathsA comma-separated list of paths to use with our Batch Export tool.
psLoginHeaderHTML text to customize the login screen for the public scanning interface.
psTriggerErrorSet this value to a string to stop execution of any triggers, not mark the pass as registered, and display the error to the user. The user will see this and can go back to the registration page to correct the error, so provide a useful message. This field will automatically be cleared and reset at the beginning of any trigger execution and can be set without being set up as an additional field in your template (in fact, you should not set this in your template unless you are testing and want triggers to always fail).


We have developed a robust scanning system that allows quick setup in many different types of workflows. While you can build your own scanner or use our API to integrate with virtually any system, we also offer some solutions for situations where a system does not exist.

We offer various ways to scan our passes. By default, the PassScan QR code is simply a link to create a copy of the pass which allows it to be printed on physical cards and paper so that it can be scanned by a configured scanner or added to a wallet app.

If you do not have scanning hardware, we offer our Pass Scanner app for iOS which has multiple features including broadcasting an iBeacon signal.

We also offer a way to use 3rd party QR code scanners (including the one built into iOS 11 and later) to trigger scans using our psPublicScanURL feature. This allows you to set the barcode_message to {psPublicScanURL} and any QR scanner that can open URLs can be used as a scanner! Any onScan triggers will not fire and no scan text will be displayed unless 1) the scanner is authenticated using the account login, 2) the scanner is authenticated using the psEditCode feature, or 3) you have set psAllowOpenScan to true.

As long as the scanner app supports cookies, it will remember this authentication between scans. If you do need to remove a scanner's access, simply change the template's edit code or your account password if not using an edit code.

The user interface for both our Pass Scanner app and the psPublicScanURL feature can be customized for quick integration and onboarding. The background color (psScanColor) and displayed text (psScanText) can be set via triggers or in the template or using conditional magic string values.

An additional button can be presented for more information (label: psInformationLabel, url: psInformationURL).

Our UI can be bypassed entirely and instead show an alternate URL (psDisplayURL) while still triggering any onScan triggers and using our authentication services.

Add these additional parameters to your template to configure the scanner interface (or you can use our API to create your own custom user interface):

psScanColorA color value for the background (we recommend a dark color as the text will always be white).
psScanTextThe text to display when scanned. This can be set using our onScan triggers or you can use a Magic String function like the :Ternary operator to conditionally display messages.
psInformationURLIf present, a "more information" button will be displayed.
psInformationLabelThis will override the default text of the "more information" button (which defaults to "More Information". Early versions of our Pass Scanner app do not support this feature.
psDisplayURLA url to a page that will be displayed after any onScan triggers are processed. Can be used to provide a custom scan interface while still using other features like triggers and public scan authentication.
psAllowOpenScanIf this is set to true, any public scan authentication will be bypassed to allow anyone to trigger any onScan triggers (this should not be used if you require any sort of validation or verification as the results could be faked).
psEditCodePromptThe text to be displayed if psEditCode is present and using the psPublicScanURL feature. Defaults to simply displaying the same error that would normally be displayed.

Pass Scanner app

Our Pass Scanner app (free on the App Store) can be used as an end-to-end solution to scan and manage your Apple Wallet passes.

You can go into the app, tap the ?! Button, and select the various options.

The app can be set to display the encoded data (for copying or simply viewing), or it can pass the barcode data as a get parameter (message)to any URL (like our scan URL), or you can enter your PassSource clientHash to authenticate.

When using the custom URL mode, a GET parameter message will be added to the custom URL with the contents of the barcode data as a string. The app then opens a web-view with that URL where you can perform custom actions or create your own interface.

You can automatically configure and authenticate the app using the button on your advanced settings page.

To automatically configure custom URL mode with any URL, you can visit the following configuration URL on the device with the Pass Scanner app installed: passscanner://?customURL={your custom url URL encoded}

The flash in the app turns on automatically if there isn't enough light. Make sure the phone is not resting on the table when you start the app and there is enough light in the room and the flash should not engage.

The app is designed not to go to sleep while the scanner is visible, so we recommend keeping the device plugged in if you are going to set this up near a register.

In PassScan mode, it will automatically return to scanning screen after 5 seconds unless you tap anywhere on the screen.

To automatically set the iBeacon parameters for the app, use a URL in the format: passscanner://?proximityUUID={proximityUUID}&major={major}&minor={minor}

You can use this link to set up the app to be a a beacon for our demo passes.

Technical Details

Getting Started

Getting started with the API is simple!

  1. Sign up for a professional account.
  2. Get your client hash from your advanced settings page.
  3. Start creating and updating passes using API links!

See our examples and tutorials for some ideas.

Creating Passes via Links

This is the easiest way to make changes to templates without having to worry about the "API". In fact, this was the primary method for making changes and is still supported with the old login API, the new login API, or the clientHash.

You can send links to users with their information pre-filled so that you don't have to pre-generate passes.

This method is particularly useful if you already have an email program that you can insert fields from your database into. It does not require any web server or backend to manage your passes so it is the easiest solution for restaurants distributing loyalty cards or coupons. Only user-editable fields can be overwritten, so it's easy and convenient way to pre-fill data for users. If you don't want users to be able to edit their data, you will probably want to use the API to pre-generate fixed passes instead.

Before you create your pass link, you will want to get the templateHash. This can be found on the template editor under PassSource options.

Once you have your template hash, to create the pass link, just construct a link as follows:{templateHash}&{apiPaths} The apiPaths are any number of key value pairs where the key is an API path and the value is the URL encoded value to set.

You may also overwrite and specify images by using the key images_ plus the image name and the value to a URL encoded URL to the image. For example, you could add the following to change out the logo: & The possible image keys are as follows. Note that not all pass types support all image types. For more information, check out Apple's documentation (you may need an Apple developer account to access). images_icon images_logo images_thumbnail images_strip images_background images_footer You may add "@2x" to the end of any of those to specifically target the high resolution version and we recommend setting both the normal and the @2x versions. We will automatically scale the images to be the optimal size, so you can use the same path for both the @2x and normal versions.

You can also pre-generate passes and then link to the pass using the hashedSerialNumber. This is a good method for creating store-cards and other items where you would rather the user not be able to modify the values themselves. The first step is to add the user's custom fields in the same manner as the clear-text pass links and include your clientHash or simply use the API. If you're using the create page, you can add &generate=true to the URL to have it show the hashedSerialNumber instead of a page with the pass or downloading the pass. However, we recommend using the createPassFromTemplateFields API call as you will be guaranteed a JSON response even if there is a problem.

The hashedSerialNumber is a string representing the serial number for this pass. You can store this in your database as you will use this for any updates and tracking of this pass. When you want to give this pass to the user, simply direct them to the following URL:{hashedSerialNumber} This link will display a page with the pass and a link to download unless they are on an iPhone or iPod touch in which case it will simply prompt the user to add the pass to their Apple Wallet.

Why hash the serial number? The serial number is not secured for the user since they can obviously open up the pass file to get that. The reason we hash the serial number is it adds an additional level of complexity. Since anyone can download any pass by just having the serial number, hashing it makes brute-force guessing of serial numbers that much more difficult.

Getting the pass file

Typically you'll be linking to passes using the hashedSerialNumber as passing around large data files isn't very efficient, but if you have an iOS native app or you have to have the pass data, you can download the Apple Wallet pass file.

To download the pass file, add &download=true to the create URL. This acts the same as the generate parameter with the difference that instead of returning the serial number, it will return the pass itself. If you need the hashedSerialNumber, do the generate command and then use the user's create link with the download parameter to get the pass data.

Client Hash

We now have offer a stateless API that uses a token for authentication instead of requiring logins and cookies. We call this authentication token your clientHash. This is a unique key that is specific to your account and is used in all your API calls to validate that you have permission to perform the action. It is based on your account ID, email, password, and subscription status, so if any of these values change, so will your clientHash (so if you ever need to invalidate a client hash, simply change your password). It is important that you do not share your clientHash and make sure it is not publicly viewable by not including the magic string "{clientHash}" in public/visible fields in the template.

You can use the login API function to get your clientHash, or you can simply copy it from your advanced settings page.


Pretty much everything is based on templates. You can use our website to create templates to customize passes to your liking. This will be your first step in designing and testing passes you will customize for your users.

Go ahead and create some templates and try out the passes on your test device to see how they look. Once they look right, you can move on to creating passes for your users.

Public passes are branded with a link back to and are shareable.

General Philosophy

We have designed our API to be simple to use and simple to test. We wanted to allow companies to send an email blast that could contain links with embedded personal information to easily create personalized passes for their users. We also wanted developers to be able to easily test the API with just a web browser without having to write complicated code or use any specialized frameworks.

API functions can be called by simply constructing a URL with the following format:{apiFunctionName}.php?clientHash={clientHash}&{parameters} Most of the API calls expect data to be passed as simple HTML GET parameters, however, for functions that accept large amounts of data, you can also send a JSON encoded object with the parameter keys as a string via HTML POST field with the key "JSON" (this is also useful if you have any array data that is not easily flattened). Functions that support POST upload are indicated in the table. The clientHash and templateHash parameters should always be sent via GET for authentication.

All API calls return a JSON object with at least a success key that may be true or false. If it is false, there will be a key message that will contain the error message. If you are testing and want formatted instead of compact JSON, add a GET parameter prettyOutput to format the JSON (this only happen for short JSON responses). If there is an error, please handle it appropriately. If you ignore the error and continue to generate errors using the API, we will lockout your API usage for a period of time to minimize server resources. As long as you work to prevent errors, this should not affect most people's normal API usage and has been built to catch potential runaway scripts or coding errors. If you encounter this and need assistance, feel free to contact us.

A note about Dates

Certain fields need to be in a very specific date+time format or the pass will not install. If you use our interfaces, we'll do our best to validate and force your data to the proper values, however, if you use the API, you may be able to construct a pass with an invalid date format causing the pass to not install. If you have XCode installed, you can connect an iOS device and view the logs to see what the exact error is, but if it's a date error, it will look something like this: Unable to parse relevantDate 2017-10-17T00:00:00 as a date. We expect dates in "W3C date time stamp format", either "Complete date plus hours and minutes" or "Complete date plus hours, minutes and seconds". For example, 1980-05-07T10:30-05:00.

API Functions

functionparameters, description, and returns
emailaccount email
passwordaccount password
Logs in and gets your clientHash (also sets session cookies for editor page or other interface pages).
clientHashYour clientHash token.
logout Logs you out and clears the session cookies. Client hash not required.
clientHashclientHash for authentication
Validates that this is a valid client hash and returns client account information.
clientNameThe name set when you registered.
clientIdYour client ID.
emailThe email address on file.
clientTypeThe type of client. Free = 0, Personal = 1, Professional = 2. Since this requires the API, this value should always be 2 (professional account).
clientHashSame value passed in for reference.
expiresThe time when your subscription is set to expire.
additionalThe value set in the additional JSON field of your advanced settings.
whitelabeltrue or false depending on if whitelabel services are enabled.
referenceIdIf this is a managed account, this will be the ID of the managing account. Otherwise, 0.
backgroundColorIf whitelabelled, this is the custom background color set for the account used for interface customization.
logoURLIf whitelabelled, this is the URL that the logo is linked to.
passTypeIDThe pass type identifier (for creating passes manually).
teamIdentifierThe team identifier (for creating passes manually).
certificatePasswordThe certificate password (for creating passes manually).
clientHashclientHash for authentication
Returns all templates for this client. In addition to the templateInfo fields, we also provide a url key that will point to the template portal page.
templatesAn array of template objects (see templateInfo for the structure).
clientHashclientHash for authentication
templateHashThe template hash (can be found in the PassSource options of the template editor).
Returns information about a template, including what you would need to submit modifications to insertUpdateTemplate. In addition to the fields below, it also includes the same information from clientInfo for the template owner (which may not match the clientHash passed for authentication if you have a manager account).
templateIdThe template ID.
templateNameThe template name.
templatePublictrue if the template is marked as public or false.
passConstructorThis is a structured array that contains all the information to build a template. If you plan on using this, please contact us.
templateUpdatedThe timestamp when the template was last updated.
templateHashThe hash for this template (for public edits and creation URLs).
clientHashclientHash for authentication
templateHashThe template hash (can be found in the PassSource options of the template editor).
Returns statistics for the template. If you have more than 500 passes generated for a template, the stats will require manual updates as they will take some time to generate.
createdThe number of passes created.
installedThe number of passes installed.
deletedThe number of passes deleted.
theoreticalThe number of passes theoretically installed (installed - deleted).
scannedThe number of passes scanned using our scanning feature or API.
registeredThe number of passes registered.
ageThe age in seconds of the oldest stat. Will always be 0 for small data sets.
clientHashclientHash for authentication
templateHashThe template hash (can be found in the PassSource options of the template editor).
accessTypeThe accessType you wish to get passes for.
(optional) countThe number of passes to return. -1 is the default and will fetch all passes.
(optional) startThe index of the pass to start counting from (for pagination).
Returns a list of passes that match the accessType. Each object in the list contains the hashedSerialNumber and the userFields.
passesArray of pass objects.
clientHashclientHash for authentication
templateHashThe template hash (can be found in the PassSource options of the template editor).
Sends a push notification to all installed passes with the current information. Use after changing the template.
countThe number of passes updated.
insertUpdateTemplate POST data supported (clientHash and templateHash parameters should be sent via GET)
clientHashclientHash for authentication
templateNameThe name to assign to the template.
templatePublictrue if you want to make this template publicly available or false.
passConstructorA structured array with the template information. You should be able to use an existing pass json as a base.
templateHashThe template hash of the existing pass or the templateHash of a template to copy.
(optional) templateIdThe existing template ID. Must match the templateHash if provided.
Creates a new template or updates an existing template.
templateHashThe template hash for the template.
templateIdThe template ID for the template.
updateClientAdditional POST data supported (clientHash parameter should be sent via GET)
clientHashclientHash for authentication.
additionalJSON data to set the client additional field for the client (should fetch this value from the client info before setting to preserve existing values).
Returns success as long as additional is valid JSON and replacedAdditional with the previous additional value that was replaced.
updateTemplateFields POST data supported (clientHash and templateHash parameters should be sent via GET)
clientHashclientHash for authentication.
templateHashThe templateHash for the template.
updatePassesIf this is set to true, all passes from this template will be pushed an update notification (like invoking updatePassesForTemplate).
API pathsAPI paths can be added to set these values on the template. This will only update the specific paths and any values that have been overridden by a specific pass instance will remain overridden.
Updates the specified fields for a template pushing the updates to all passes if desired. Returns success if everything works correctly and if updatePasses was set to true, will return the count value like updatePassesForTemplate.
uploadTemplateImages POST data supported (clientHash and templateHash parameters should be sent via GET)
clientHashclientHash for authentication
templateHashThe template hash (can be found in the PassSource options of the template editor).
icon, logo, etc.Any image names are supported and should contain either the raw binary image data or a URL to an image. Can also be set to the string "DELETE" to remove an existing image.
Uploads or deletes the specified images for the template replacing any existing images.
clientHashclientHash for authentication
templateHashThe template hash (can be found in the PassSource options of the template editor).
Deletes a template and all associated passes and tracking from the database.
deletedTemplateIdThe template ID of the deleted template.
createPassFromTemplateFields POST data supported (clientHash and templateHash parameters should be sent via GET)
clientHashclientHash for authentication. This is optional, but if this clientHash does not have access to the template or is missing, only user editable fields will be updated and other values will be ignored.
templateHashThe template hash (can be found in the PassSource options of the template editor).
API pathsAPI paths can be added to set these values on the pass.
Creates a pass from the specified template and initializes the userFields with any provided API paths. You can pass these same values to to see the pass rendered instead of getting a JSON response. If the template has psIndexPaths set and a matching pass for an index path exists, that pass will be updated instead of creating a new pass.
hashedSerialNumberThe hashedSerialNumber for the created pass.
clientHashclientHash for authentication
hashedSerialNumberThe hashedSerialNumber for the pass.
Fetches the data for the pass. Includes the template information in addition to the below fields.
hashedSerialNumberSame as provided value.
userFieldsA JSON object with the override keys and values.
clientHashclientHash for authentication
hashedSerialNumberThe hashedSerialNumber for the pass.
Fetches a list of all the tracking events that have happened for this pass. Each event is an object with an accessType key and a time key.
trackingArray of events.
clientHashclientHash for authentication
hashedSerialNumberThe hashedSerialNumber for the pass.
accessTypeThe accessType event to log.
Logs a specified accessType for a pass. This can be used to build your own scanner or to integrate all our features with apps or workflows. If accessType is a check or scan, the return json may include the following keys:
updatedTrue if the triggers caused the pass to be modified and an update pushed, false if not.
scanTextText to display on the scanner. This is evaluated from psScanText so set that value to customize.
scanColorThe color for the scanner background (in RGB, color name, or hex). This is evaluated from psScanColor which can be set to customize the interface.
descriptionA description of the pass. This is evaluated from psPassDescription so set this value to customize.
informationURLA URL to make edits to the pass. This is evaluated from psInformationURL so set this value to customize.
informationLabelThe text to use for the button in the scanner interface when informationURL is present. This is evaluated from psInformationLabel so set this value to customize.
displayURLA URL to display instead of our default scanner interface. This is evaluated from psDisplayURL so set this value to customize.
clientHashclientHash for authentication
hashedSerialNumberThe hashedSerialNumber for the pass.
accessTypeThe accessType event test.
For determining whether or not the specified event has been logged for a pass.
checktrue if the event has been logged, false if not.
lastThe timestamp for the last occurrence of the event. Only present if check is true.
updatePassFields POST data supported (clientHash and hashedSerialNumber parameters should be sent via GET)
clientHashclientHash for authentication. This is optional, but if this clientHash does not have access to the template or is missing, only user editable fields will be updated and other values will be ignored.
hashedSerialNumberThe hashedSerialNumber for the pass.
psReplaceAllIf this is set to true, all existing fields will be replaced (and any fields missing will be deleted). Otherwise, this will just update any existing fields.
API pathsAPI paths can be added to set these values on the pass. If you provide the client hash, provide all fields that you want to store for the pass as this will replace the entire set of existing fields if psReplaceAll is set.
Updates the specified fields for a pass. You can pass these same values to to see the pass rendered instead of getting a JSON response. If clientHash is provided and psReplaceAll is set, this will replace entire set of existing fields. If clientHash is not provided, will replace all user editable fields (deleting fields that are user editable that are not present if psReplaceAll set).
updatedtrue if the pass was updated (including updating images), false if not (if no values actually changed).
clientHashclientHash for authentication
hashedSerialNumberThe hashedSerialNumber for the pass.
Sends a push notification for this pass with the current information. Use after making changes like after updatePassFields (the changes above are not automatically pushed in case you are using from a trigger or you need to make multiple saves).
clientHashclientHash for authentication
hashedSerialNumberThe hashedSerialNumber for the pass.
Deletes the pass from the database. Future attempts to update or fetch this pass will fail. Only use to remove test data in case user still has link to the pass or has pass installed.
imageForNameTemplateHash Either provide the templateHash or the hashedSerialNumber
imageThe name of the desired image.
(optional) templateHashThe template hash (can be found in the PassSource options of the template editor).
(optional) hashedSerialNumberThe hashedSerialNumber for the pass.
Gets the URL for the specified image if it exists (will not succeed if image does not exist).
urlThe URL to the named image.

Changes and Notes

Please note that we've made a lot of upgrades since our initial release of PassSource in 2012. We've done our best to maintain backwards compatibility with previous versions of our API, but if you find that we missed something, please let us know! Over time, we will eventually remove the older API hooks, but we've checked how people are using things and most of the things we've deprecated weren't being used and we've replaced them with more generic and powerful features.

We've re-written the entire back-end to be much more modular, efficient, and flexible with increased whitelabel support across the board. We have even created some PHP classes you can drop-in to your code to make things even easier (which was quite a feat as our API was already probably the easiest out there). One of the main reasons for the new APIs was to make custom solutions easier for us and our clients and most of the new site has been built on top of the API, so it should be fairly comprehensive.

We've also completely re-designed our front-end to better match newer design sensibilities and to allow complete re-branding of most of the site should you need to whitelabel.

We have simplified things across the board and made things consistent and made the most requested features even easier to implement via new dashboards and portal pages.

Latest Highlights

We will demo new features on our new beta site before pushing changes live, so if you ever need to test in a playground environment, you can use that server to make destructive edits or test the API or features. Periodically, we will replace the account and template data on the beta server with the live server values, however, passes and tracking may not be synced. Feel free to contact us about the use of the beta server. The beta site can be found at:

Automatic Purging

Tracking data and passes that haven't been updated in over 2 years may automatically be purged from our system. Additionally, passes that have no device or access tracking data (passes that have not been accessed or installed besides their initial creation) will be purged after 6 months.

If you think this may be an issue or if you have a pass that may have been purged that you need, please contact us.

Constructing Templates Manually

You can use our PassSource.php classes to update templates or you can update the code manually via the API.

This part of the API starts getting pretty complex, so if you'd like our help in doing something, please drop us a line.

All templates are built on a template structure which is simply a JSON object that defines information about a pass and indicates user editable fields. It's purposefully designed to be very similar to Apple's pass JSON structure and in fact you could take the manifest.json from a pass to create a new template.

A field can consist of the value or, more commonly, will be a PSAttribute JSON object which will have the following keys:

PSValueThe actual value.
PSEditableWILL BE DEPRECATED! If you are using this, let us know and we'll make sure your code migrates. We will be moving to a psRegistrationPaths field similar to psEditorPaths to allow for ordering of fields which won't be compatible with PSEditable. This will also add some additional consistency. Currently, this will indicate that this field should appear on the registration form and can be set via URL parameters. Editable fields may have the following additional properties:
PSLabelThis will be used when the field is displayed on the registration, editor, or portal forms. By default, this will be the path to the field.
PSAttributeTypeBy default, most fields are just text, but you can have special editors. Currently we support PSAttributeTypeText, PSAttributeTypeDropdown, PSAttributeTypeMultiselect, PSAttributeTypeCheckbox, PSAttributeTypeColor, PSAttributeTypeStepper. We will be adding a date picker and time picker and location picker soon.
PSOptionsYou can specify a set of options that will appear as a dropdown on the registration form. They "keys" of this JSON dictionary should be the labels and the "values" should be the value that will be set.
PSStepAmountIf the PSAttributeType is PSAttributeTypeStepper, this value indicates the amount to increment or decrement by. This is mainly for conversion from the legacy PassScan increment option.

Examples and Tutorials

Prompt a user to register.

Create a backField with the following value: Register your pass for additional benefits! {psRegisterURL} Set the attributedValue to: <a href="{psRegisterURL}">Register your pass</a> for additional value! Add the condition: !registered

Include a link to update registration.

Create a backField with the following value: Update your information: {psRegisterURL} Set the attributedValue to: <a href="{psRegisterURL}">Update your information</a> Add the condition: registered

Push notifications to installed users.

Create a backField with label "Latest News" and value set to your message.

Open the advanced options and note the API path. (example: structure_backFields_news_value)

Set the changeMessage in the advanced settings to "%@" (without the quotes).

Create an additional field with key psPortalUpdatePaths and value "structure_backFields_news_value" (without the quotes).

Go to the template portal page and enter in the new message to send to passes.

Setting the geolocations for a pass.

The locations value can be set using the JSON format specified by Apple. For example, to set the locations to the Eiffel Tower, the Empire State building, and the Statue of Liberty, the JSON would be: [ { "longitude":48.8583701, "latitude":2.2944813, "relevantText":"Welcome to the Eiffel Tower!" }, { "longitude":40.7484444, "latitude":-73.9878441, "altitude":381, "relevantText":"Welcome to the 102nd floor of the Empire State Building!" }, { "longitude":40.6892534, "latitude":-74.0466891, "relevantText":"Welcome to the Statue of Liberty!" } ]

The JSON needs to be URL encoded to pass it as a parameter using the URL API. You can do this online using our encoding tool.

The encoded result is: %5B%0A%09%7B%0A%09%09%22longetude%22%3A48.8583701%2C%0A%09%09%22latitude%22%3A2.2944813%2C%0A%09%09%22relevantText%22%3A%22Welcome%20to%20the%20Eiffel%20Tower!%22%0A%09%7D%2C%0A%09%7B%0A%09%09%22longitude%22%3A40.7484444%2C%0A%09%09%22latitude%22%3A-73.9878441%2C%0A%09%09%22altitude%22%3A381%2C%0A%09%09%22relevantText%22%3A%22Welcome%20to%20the%20102nd%20floor%20of%20the%20Empire%20State%20Building!%22%0A%09%7D%2C%0A%09%7B%0A%09%09%22longitude%22%3A40.6892534%2C%0A%09%09%22latitude%22%3A-74.0466891%2C%0A%09%09%22relevantText%22%3A%22Welcome%20to%20the%20Statue%20of%20Liberty!%22%0A%09%7D%0A%5D%0A

This link will create a demo pass with the locations automatically set:!%22%0A%09%7D%2C%0A%09%7B%0A%09%09%22longitude%22%3A40.7484444%2C%0A%09%09%22latitude%22%3A-73.9878441%2C%0A%09%09%22altitude%22%3A381%2C%0A%09%09%22relevantText%22%3A%22Welcome%20to%20the%20102nd%20floor%20of%20the%20Empire%20State%20Building!%22%0A%09%7D%2C%0A%09%7B%0A%09%09%22longitude%22%3A40.6892534%2C%0A%09%09%22latitude%22%3A-74.0466891%2C%0A%09%09%22relevantText%22%3A%22Welcome%20to%20the%20Statue%20of%20Liberty!%22%0A%09%7D%0A%5D%0A

Change the color of a pass when scanned.

Add an onScan trigger: {"action":"PSActionSetValue","targetPath":"backgroundColor","value":"blue"}

Add 25 points to a header field after registering the pass.

Go into your template and note the API path to the field (for our example, we'll use structure_headerFields_points_value).

Add an onRegister trigger (note we added a condition to make sure the points only get granted once): {"action":"PSActionIncrementValue","targetPath":"structure_headerFields_points_value","amount":25,"PSConditions":"!registered"}

Set scan output based on previous scan.

This sets this scan display values based on whether the pass has been scanned or not for use with our scanning feature.

Add an additional field named subtriggers with the following value: [ { "action" : "PSActionSetValue", "targetPath" : "psScanColor", "value" : "{PSColorGreen}" }, { "action" : "PSActionSetValue", "targetPath" : "psScanText", "value" : "First Scan" }, { "action" : "PSActionSetValue", "targetPath" : "psScanColor", "value" : "{PSColorYellow}", "PSConditions" : "scanned" }, { "action" : "PSActionSetValue", "targetPath" : "psScanText", "value" : "Already Scanned", "PSConditions" : "scanned" } ]

Set both the onCheck and onScan triggers to: { "action": "PSActionExecuteTriggers", "targetPath": "subtriggers" }

Only show field if the value is not empty.

For a backfield with a value path of structure_backFields_myKey_value, set the conditions of the entire field to: {structure_backFields_myKey_value} !isEmpty

Only show field if the points value is greater than 20.

Assuming the points field path is structure_headerFields_points_value, set the conditions of the field to: {structure_headerFields_points_value} > 20

Only show field if the secondary name field is equal to "PassSource".

Assuming the field path structure_secondaryFields_name_value, set the conditions of the field to: {structure_secondaryFields_name_value} = "PassSource"

Only show field if the pass has been scanned.

Set the conditions of the field to: scanned

Show field until the pass is registered.

Set the conditions of the field to: !registered

Customize how passes are displayed in lists.

To have the pass show the name and points in lists and for the scanner and editor descriptions, create an additional field with key psPassDescription and set it to: {structure_secondaryFields_name_value} has {structure_headerFields_points_value} points

Create a barcode that can be used to install the pass as well as for the Pass Scanner app.

The newest version of our Pass Scanner app will automatically identify hashes within a URL, so you can simply set the barcode message to {psPassURL} and the code can be scanned to get the pass as well as be used to update the pass.

QR Code Generator is a great site for generating QR code images that can be printed on physical cards. Use the templateHash to create a link to generate a new pass with each scan, or use the hashedSerialNumber to generate a code for a specific pass.

Add 1 point to a header field whenever the pass is scanned.

Go into your template and note the API path to the field (for our example, we'll use structure_headerFields_points_value).

Add an onScan trigger: {"action":"PSActionIncrementValue","targetPath":"structure_headerFields_points_value","amount":1}

Set the barcode message, altText, and member name for a new pass via an emailed link.

First you need the templateHash for the template and make sure the barcode message, altText, and member name are all user editable (click the (i) button next to the field and check the User Editable box.)

For our example, we're setting the barcode message and altText to "9991212", and the member name (which is a primary field) to "George P. Burdell".

Don't forget to URL encode the values. You could use this method to distribute customized passes to a mailing list with each person's email containing a link to a pass with their name and member ID pre-filled so all they have to do is tap the link and tap add to get it in their Apple Wallet. You could add additional user-editable fields and direct to the registration page instead of the create page if you wanted to capture additional information.

Construct the link as follows:,&barcode_message=9991212&barcode_altText=9991212&structure_primaryFields_member_value=George%20P.%20Burdell

Get your clientHash from the API.

Go to the following link:{email}&password={password} then extract the clientHash parameter from the returned JSON.

Create a loyalty card with automatic redemption.

Create a coupon card (for our example, we're using our PassSource loyalty demo), then set up the following triggers to set up the automatic actions during a scan:

You can further enhance the experience by having different fields appear depending on if the pass is a coupon or a storeCard, so that you can indicate what will happen when it is scanned or change out the offer.

Use a web widget to allow users to create passes for their cards.

This widget will create a generic pass with the user-entered member number as the barcode message: <form method="get" action=""> <input type="hidden" name="templateHash" value="eNortjIysVIqLA00jIiyyC0pMXKOcPYqyrIozC4ItLVVsgZcMKPxCfk," /> <label>Enter your member number:</label> <input type="text" name="barcode_message" /> <input type="submit" value="Get Pass" /> </form>

Here are some functions and examples we use in PHP.

This is the wrapper for CURL we use to make it easier to fetch the contents of a web page (you may already have something similar already built-in to your frameworks): function fetchURL($url, $post_data = null, $cookie_data = null) { $curl_connection = curl_init($url); // First we set the connection timeout to 30 seconds, so we don't have our script waiting indefinitely if the remote server fails to respond. curl_setopt($curl_connection, CURLOPT_CONNECTTIMEOUT, 20); // CURLOPT_RETURNTRANSFER set to true forces cURL not to display the output of the request, but return it as a string. curl_setopt($curl_connection, CURLOPT_RETURNTRANSFER, true); // Finally, we set CURLOPT_FOLLOWLOCATION to 1 to instruct cURL to follow "Location: " redirects found in the headers sent by the remote site. curl_setopt($curl_connection, CURLOPT_FOLLOWLOCATION, 1); // save cookies $cookie_path = tempnam("/tmp", "temp_cookie_" . time()); // echo "COOKIE PATH: $cookie_path"; if (!is_null($cookie_data)) { file_put_contents($cookie_path, $cookie_data); } curl_setopt($curl_connection, CURLOPT_COOKIEFILE, $cookie_path); curl_setopt($curl_connection, CURLOPT_COOKIEJAR, $cookie_path); if (!empty($post_data) && is_array($post_data)) { // Now we must prepare the data that we want to post. We can first store this in an array, with the key of an element being the same as the input name of a regular form, and the value being the value that we want to post for that field. // In order to format the data like this, we are going to create strings for each key-value pair (for example key1=value1) then combine them in one string. // PHP 5+ has a nice function for this: http_build_query $post_string = http_build_query($post_data); // Next, we need to tell cURL which string we want to post. For this, we use the CURLOPT_POSTFIELDS option. curl_setopt($curl_connection, CURLOPT_POSTFIELDS, $post_string); } //perform our request $result = curl_exec($curl_connection); $headers = curl_getinfo($curl_connection); //close the connection curl_close($curl_connection); // store cookie data into database $cookie_data = file_get_contents($cookie_path); // cleanup and remove the temp file unlink($cookie_path); return array($result, $cookie_data); }

And here is an example of a wrapper we have for one of our API functions: // pass updates and images can be fetched with just the template hash without the need for the client hash (if only modifying user editable fields) function createPassFromTemplateFields($clientHash, $templateHash, $userFields) { list($results, $_SESSION['psCookieData']) = fetchURL("$templateHash&clientHash=$clientHash", array("JSON" => json_encode($userFields)), $_SESSION['psCookieData']); return json_decode($results, true); }

Create a pass in iOS and prompt user to add to Apple Wallet.

This creates a pass setting the barcode message and altText and member name. #import <PassKit/PassKit.h> #define template_hash @"eNortjIysVIqLA00jIiyyC0pMXKOcPYqyrIozC4ItLVVsgZcMKPxCfk," // you'll want to include your client hash if you're not just modifying user editable fields. // do the following in a UIViewController // run asynchronously to not block main thread dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // create pass with template NSString *passURL = [NSString stringWithFormat:@"", template_hash]; NSLog(@"Loading pass from URL: %@", passURL); NSData *passData = [NSData dataWithContentsOfURL:[NSURL URLWithString:passURL]]; PKPass *pass = [[PKPass alloc] initWithData:passData error:&error]; if (pass == nil) { NSLog(@"Error creating pass: %@", error); } else { dispatch_async( dispatch_get_main_queue(), ^{ PKAddPassesViewController *apvc = [[PKAddPassesViewController alloc] initWithPass:pass]; [self presentViewController:apvc animated:YES completion:nil]; }); } } else { NSLog(@"There was a login error: %@", error); } });