Coupons can be used to offer customers a discount or promotion, or as a general proximity marketing asset.
Attention: please check our Android Pay documentation for details about the rendering on the Android Pay app.
![]() |
![]() |
---|---|
Generic Coupon Schematic | Paw Planet Coupon |
We are going to assume that Paw Planet is going to use sequencial barcodes, in this case each barcode numbering will be unique and is going to start from 1.
POST /v2/coupons/
Payload
{
"coupon": {
"name": "Paw Planet",
"icon_id": "ffe0d88f-916d-4a12-8244-9ecd0178ca45",
"barcode": {
"format": "pdf417"
},
"locations": [
{
"longitude": -122.3748889,
"latitude": 37.6189722
}
],
"logo_text": "Paw Planet",
"description": "20% off premium dog food",
"foreground_color": "#ffffff",
"background_color": "#ce8c46",
"primary_fields": [
{
"key": "offer",
"label": "Any premium dog food",
"value": "20% off"
}
],
"auxiliary_fields": [
{
"key": "expires",
"label": "EXPIRES",
"value": "2 weeks"
}
]
}
}
Response:
From the "create coupon" campaign request we get the following response.
The code_type
indicates the type of numbering that is going to be applied to the barcode, in this case we will be using "sequential"
{
"id": "a2d82801-e0f7-490e-b06d-548ec26e7c97",
"name": "Paw Planet",
"template_id": "24a97b3b-bd00-49c8-9663-e4c0504228fc",
"organization_name": "Paw Planet, Inc",
"gwallet_usage": null,
"gwallet_status": null,
"gwallet_message": null,
"barcode": {
"format": "pdf417",
"message": "",
"alt_text": "",
"alt_text_type": "mirror"
},
"barcodes": [],
"allow_multiple_redeems": false,
"header_fields": [],
"primary_fields": [
{
"key": "offer",
"value": "20% off",
"label": "Any premium dog food",
"behaviour": "fixed"
}
],
"secondary_fields": [],
"auxiliary_fields": [
{
"key": "expires",
"value": "2 weeks",
"label": "EXPIRES",
"behaviour": "fixed"
}
],
"back_fields": [],
"locations": [
{
"latitude": 37.6189722,
"longitude": -122.3748889,
"altitude": null,
"relevant_text": null
}
],
"beacons": [],
"expiration_date": null,
"code_type": "sequencial",
"code_fixed_value": null,
"created_at": "2016-07-15T09:49:12Z",
"updated_at": "2016-07-15T09:49:12Z",
"user_info": {},
"distributions": [
{
"id": "91d464ab-1eb2-475e-b70c-e45719b5de01",
"distribution_type": "downloadPage",
"state": "published",
"page_url": "https://passworks.io/p/5QodxK8VcA",
"pkpass_url": "https://passworks.io/p/5QodxK8VcA.pkpass",
"page_short_url": "http://pass.yt/d/7L",
"pkpass_short_url": "http://pass.yt/d/7L.pkpass",
"preview_url": "https://passworks.io/p/5QodxK8VcA.png",
"created_at": "2016-07-15T09:49:12.241Z",
"updated_at": "2016-07-15T09:49:12.365Z",
"published_at": "2016-07-15T09:49:12.374Z"
}
]
}
Field name | Type | Description |
---|---|---|
icon_id | uuid | Required. Icon id (the id of a icon type asset) |
logo_id | uuid | Optional. Logo image id (the id of a logo type asset) |
strip_id | uuid | Optional. Strip image id (the id of a logo type asset) |
thumbnail_id | uuid | Optional. Strip image id (the id of a logo type asset) |
logo_text | string | Optional. Top card text |
background_color | rgb string | Optional. Color defining the pass background color ranging from #00000 to #ffffff |
text_color | rgb string | Optional. The text color for all the value fields except primary_fields, ranging from #00000 to #ffffff |
label_color | rgb string | Optional. The text color for all label fields except primary_fields, ranging from #00000 to #ffffff |
Field name | Type | Description |
---|---|---|
name | string | Required. Must be unique, it's used to identify the Event Ticket "Campaign" |
description | string | Optional. Brief description of the pass, used by the iOS accessibility technologies. If the description is not provided the name field value is used instead. |
template_id | uuid | Optional. If not supplied, you must supply the presentation fields presented in the table above! |
barcode | hash | Optional. A single hash of barcode hash object. |
header_fields | array | Optional. Collection of field hash objects |
secondary_fields | array | Optional. Collection of field hash objects |
auxiliary_fields | array | Optional. Collection of field hash objects |
back_fields | array | Optional. Collection of field hash objects used in the rear part of the pass |
locations | array | Optional. Collection of up to 10 location hash objects |
beacons | array | Optional. Collection of up to 10 beacon hash objects |
certificate_id | uuid | Optional. You should provide your own certificate but in none is provided the passworks.io default certificate is used. |
organization_name | string | Optional. Organization name showned in the unlock screen, if none is supplied the registration organization name is used |
associated_store_identifiers | array | Optional. A list of iTunes Store item identifiers for the associated apps. Only one item in the list is used - the first item identifier for an app compatible with the current device. If the app is not installed, the link opens the App Store and shows the app. If the app is already installed, the link launches the app, as specified in passbook's documentation |
gwallet_usage | boolean | Optional. Default: false . Activate Android Pay for the campaign. Check the Android Pay for detailed information. |
code_type | string | Optional. By default is sequential. Check the available code_types and their meaning. |
code_fixed_value | string | Optional, default: null |
og | Boolean | Use Open Graph tags on the download page. Optional, default: true |
og_description | String | Open Graph description. Optional, default: "" (empty string) |
javascript | String | Javascript that will be rendered inside the download page and form, Allows the user to run their own javascript code eg: Google Analytics or Facebook Pixel |
stylesheet | String | CSS that will be rendered inside the download page and form, allows users to override the page css |
user_info | hash | Optional. This field can be used to store user related data. On Apple Wallet this field will be available as a JSON encoded string. |
max_distance | Integer | Optional. Maximum distance in meters from a relevant latitude and longitude that the pass is relevant. This number is compared to the pass’s default distance and the smaller value is used. Maximum 1000 meters for Event Tickets and 100 for everthing else. |
remote_form_url | url | Optional. Please see advanced features. |
sync_locations_on_merge | boolean | Optional. Default true . Allows you to choose if you with that campaign locations are synced or not with the passes when the campaign is updated. |
sync_beacons_on_merge | boolean | Optional. Default true . Allows you to choose if you with that campaign beacons are synced or not with the passes when the campaign is updated. |
code_type | Description |
---|---|
sequencial | This means that each issued pass will be assign unique and sequencial (sequential) number starting from number 1. eg: 1, 2, 3 ... |
fixed | For all issued passes use the value inside the code_fixed_value attribute as the barcode message |
list | Not used in the API for now. |
per_pass | This means that per pass creation request you need to supply the barcode message that will be used |
{
"key": "name",
"value": "2 weeks",
"label": "EXPIRES",
"behaviour": "fixed"
}
Field name | Type | Description |
---|---|---|
key | string | Required. A reference key, it must be unique for a campaign. |
value | string | Required. The field's value |
label | string | Required. The field's label |
behaviour | string | Optional, Options: fixed or dynamic. When not set, use fixed as default |
The presentation fields (primary_fields
, secondary_fields
, auxiliary_fields
and back_fields
) have a behaviour
attribute. This attribute can have one of two values: fixed
or dynamic
(by default the behaviour
is set to fixed
).
-
fixed
fixed
means that the value isstatic
: every pass will have the samelabel
andvalue
for this field. So when you call themerge
method in the Ruby client this field will be added or overriden (if the field with the samekey
exists) in every pass even if you had previously customized the value per pass. Don't use the type of field for custom fields in your passes eg: name, client id, ticket number ,etc. This type of behaviour is a good fit for fields that are the same across all passes eg:event date
,event location
,flight number
, etc. -
dynamic
The
dynamic
behaviour defines the field as a custom updated field that shouldn't be updated on a bulk update when themerge
method is called. This field will only be updated when the user updates that field specifically for that pass. This type of behaviour is a good fit for custom fields liketicket number
,user name
,boarding number
,seat number
, etc..
{
"altitude": 0.0,
"latitude": 0.0,
"longitude": 0.0,
"relevant_text": "notification to display"
}
Field name | Type | Description |
---|---|---|
altitude | double | Optional. Altitude, in meters, of the location. |
latitude | double | Required. Latitude, in degrees, of the location. |
longitude | double | Required. Longitude, in degrees, of the location. |
relevant_text | string | Optional. Text displayed on the lock screen when the pass is currently relevant. For example, a description of the nearby location such as “Store nearby on 1st and Main.” |
{
"major": 0.0,
"minor": 0.0,
"proximity_uuid": "30b5d792-48c9-4f12-80ff-082cae62e80f",
"relevant_text": "notification to display"
}
Field name | Type | Description |
---|---|---|
major | 16-bit unsigned integer | Optional. Major identifier of a Bluetooth Low Energy location beacon |
minor | 16-bit unsigned integer | Optional. Minor identifier of a Bluetooth Low Energy location beacon |
proximity_uuid | string | Required. Unique identifier of a Bluetooth Low Energy location beacon |
relevant_text | string | Optional. Text displayed on the lock screen when the pass is currently relevant. For example, a description of the nearby |
{
"alt_text": "Text shown below the barcode.",
"format": "pdf417",
"message": "Message encoded in the barcode."
}
Field name | Type | Description | Default |
---|---|---|---|
alt_text | string | Optional. Text shown below the barcode. | Pass's redeem code. |
format | string | Optional. Must be one of the following if supplied: qrcode, pdf417, aztec, ean128 or none. | qrcode |
message | string | Optional. Message encoded in the barcode. | Pass's redeem code. |
Updating a campaign is a two phase operation, first you need to update the campaign with the desired changes, and then you need to push the changes to all passes.
Here is an simple example on how to update the offer
and expire
fields of the previous campaign.
PATCH /v2/coupons/{campaign_id}
{
"coupon": {
"primary_fields": [
{
"key": "offer",
"label": "Any cat or dog treats",
"value": "30% off"
}
],
"auxiliary_fields": [
{
"key": "expires",
"label": "EXPIRES",
"value": "3 days"
}
]
}
}
Response:
{
"id": "5fd663d1-4ed4-4800-8d5a-caea4131358a",
"name": "Paw Planet",
"template_id": "9bd46ecb-20c8-41cb-a8d8-f848a07c220c",
"organization_name": "your company",
"barcode": {
"format": "qrcode",
"message": "",
"alt_text": ""
},
"allow_multiple_redeems": false,
"header_fields": [],
"primary_fields": [
{
"key": "offer",
"value": "30% off",
"label": "Any cat or dog treats"
}
],
"secondary_fields": [],
"auxiliary_fields": [
{
"key": "expires",
"value": "3 days",
"label": "EXPIRES"
}
],
"back_fields": [],
"locations": [
{
"latitude": 37.6189722,
"longitude": -122.3748889,
"altitude": null,
"relevant_text": null
}
],
"beacons": [],
"page_url": "http://get.passworks.io/I5dPyf_j2Q",
"pkpass_url": "http://get.passworks.io/I5dPyf_j2Q.pkpass",
"page_short_url": "https://pass.yt/p/z62",
"pkpass_short_url": "https://pass.yt/p/z62.pkpass",
"created_at": "2015-03-31T18:01:44Z",
"updated_at": "2015-03-31T18:22:50Z"
}
Now we need to push the changes to the existing passes of the campaign and if we wish we can also send a "push message" with the update at the same time.
POST /v2/coupons/{campaign_id}/merge
{
"push_message": "30% off for the next 3 days!"
}
Field name | Type | Description | Default |
---|---|---|---|
push_message | string | Optional. Text shown on the lock screen. | No message sent. |
Sending the push_message
attribute in the body of the request is optional, if you leave the body request (POST) empty the passes will be updated and pushed to the users device without displaying any push message.
This request will push all existing passes once again, guaranteeing that all that have been downloaded will contain the new changes. Otherwise, there's no guarantee that the users will receive the updated pass.
NOTE:
Special considerations, dynamic fields values of the passes will be left unchanged by the merge, this means that the merge will update the passes with the new layout, field labels, geo-location fields, etc but will let the custom fields values untouched.
Now that you've updated your campaign, all the future passes generated from this updated campaign will contain the new changes.
You can send a "push message" to all customers calling the "push" endpoint, this will force the customers wallets to fetch the latest version of the pass and send send an attached push message if defined.
POST /v2/coupons/{campaign_id}/push
Along with this push, you may also, optionally, send in a payload with a push message that will be presented to the users when the update is done, shown on the lock screen.
{
"coupon": {
"push_message": "New promotion!"
}
}
Field name | Type | Description | Default |
---|---|---|---|
push_message | string | Optional. Text shown on the lock screen. | No message sent. |
This request will push all existing passes once again, guaranteeing that all that have been downloaded will contain the new changes. Otherwise, there's no guarantee that the users will receive the updated pass.
You can create a boilerplate pass simply by POSTing to the passes route:
POST /v2/coupons/{campaign_id}/passes
If you want to personalize the pass you can supply an hash with the parameters you want to override:
POST /v2/coupons/{campaign_id}/passes/
{
"pass": {}
}
Response:
{
"id": "2df090c4-c272-41ae-904f-727cfbfbf955",
"campaign_id": "5fd663d1-4ed4-4800-8d5a-caea4131358a",
"voided": false,
"serial_number": "22142563-e452-4cfe-825e-650c05a78a5c",
"redeem_code": "0000001",
"redeemed_count": 0,
"barcode": {
"format": "qrcode",
"message": "0000001",
"alt_text": "0000001"
},
"expiration_date": null,
"max_distance": null,
"relevant_date": null,
"header_fields": [],
"primary_fields": [
{
"key": "offer",
"value": "30% off",
"label": "Any cat or dog treats"
}
],
"secondary_fields": [],
"auxiliary_fields": [
{
"key": "expires",
"value": "3 days",
"label": "EXPIRES"
}
],
"back_fields": [],
"locations": [
{
"name": null,
"altitude": null,
"latitude": 37.6189722,
"longitude": -122.3748889,
"relevant_text": null
}
],
"beacons": [],
"page_url": "http://get.passworks.io/I5dPyf_j2Q/-zGkMq8orhAE1x9l08yLCQ",
"pkpass_url": "http://get.passworks.io/I5dPyf_j2Q/-zGkMq8orhAE1x9l08yLCQ.pkpass",
"page_short_url": "https://pass.yt/p/z62",
"pkpass_short_url": "https://pass.yt/p/z62.pkpass",
"created_at": "2015-03-31T18:25:20Z",
"updated_at": "2015-03-31T18:25:20Z"
}
Imagine that you want to offer a special campaign to your top customer, you can offer a promotion to a specific client, by updating his pass.
PATCH /v2/coupons/{campaign_id}/passes/{pass_id}
{
"pass": {
"primary_fields": [
{
"key": "offer",
"label": "Any article in the store",
"value": "30% off"
}
],
"auxiliary_fields": [
{
"key": "expires",
"label": "EXPIRES",
"value": "3 days"
}
]
}
}
Response:
{
"id": "2df090c4-c272-41ae-904f-727cfbfbf955",
"campaign_id": "5fd663d1-4ed4-4800-8d5a-caea4131358a",
"voided": false,
"serial_number": "22142563-e452-4cfe-825e-650c05a78a5c",
"redeem_code": "0000001",
"redeemed_count": 0,
"barcode": {
"format": "qrcode",
"message": "0000001",
"alt_text": "0000001"
},
"expiration_date": null,
"max_distance": null,
"relevant_date": null,
"header_fields": [],
"primary_fields": [
{
"key": "offer",
"value": "30% off",
"label": "Any article in the store"
}
],
"secondary_fields": [],
"auxiliary_fields": [
{
"key": "expires",
"value": "3 days",
"label": "EXPIRES"
}
],
"back_fields": [],
"locations": [
{
"name": null,
"altitude": null,
"latitude": 37.6189722,
"longitude": -122.3748889,
"relevant_text": null
}
],
"beacons": [],
"page_url": "http://get.passworks.io/I5dPyf_j2Q/-zGkMq8orhAE1x9l08yLCQ",
"pkpass_url": "http://get.passworks.io/I5dPyf_j2Q/-zGkMq8orhAE1x9l08yLCQ.pkpass",
"created_at": "2015-03-31T18:25:20Z",
"updated_at": "2015-03-31T18:29:04Z"
}
You can force the update of a pass via push notification by simply calling the following URL:
POST /v2/coupon/{campaign_id}/passes/{pass_id}/push
You can also send a custom message that will be displayed in the lock screen via push notification sending the following content in the above request
{
"pass": {
"push_message": "Special offer just for you!"
}
}
There are two ways you can redeem a pass:
-
Redeeming a pass by referencing a redeem code You can redeem a pass by referencing a redeem code (instead of the pass_id) by POSTing to the campaign
redeem
route/v2/coupons/{campaign_id}/redeem
Issuing a payload with a root element
pass
and with the redeem code in the payload:{ "pass": { "redeem_code": "0000001", "comment": "Redeemed in store" } }
The method accepts an optional
comment
key where you can specify some information you want to be stored for reference. -
Redeem a pass directly You can redeem a pass directly, by POSTing to the pass
redeem
route:POST /v2/coupons/{campaign_id}/passes/{pass_id}/redeem
You can supply an optional
comment
key where you can specify some information you want to be stored for reference:{ "pass": { "comment": "redeemed in store" } }
If your pass was flagged as "void" you can reset the flag calling the "reset_redeem" endpoint
POST /v2/coupons/{campaign_id}/passes/{pass_id}/reset_redeem
GET /v2/coupons/
GET /v2/coupons/{campaign_id}/passes
DELETE /v2/coupons/{campaign_id}
In case of success a HTTP 200 status code is returned with empty error list.
{
"errors": []
}
In case of a error a HTTP 412 (Precondition Failed) status code is returned with a array of errors.
{
"errors": {
"campaign": "Campaign not found."
}
}
Deleting a coupon from a given campagin is strait forward:
DELETE /v2/coupons/{campaign_id}/passes/{pass_id}
You can pause, resume and archive campaigns using the following endpoints:
Pausing a campaign disables the ability to issue more passes and changes the campaign state to "paused".
POST /v2/coupons/{campaign_id}/pause
Resuming a campaign re-enables the issuing of new passes, changes the campaign state to "published" again.
POST /v2/coupons/{campaign_id}/resume
Archiving stops the issuing of new passes and and hides the campaign from the interface without deleting the campaign. It changes the campaign state to "archived".
POST /v2/coupons/{campaign_id}/archive
If the campaign state is changed successfully the endpoints return a 200 HTTP status code.
In case of a error a HTTP 412 (Precondition Failed) is returned with a error message, eg:
{
"errors": [
"Can't resume from archived to publish"
]
}
GET /v2/coupons/{campaign_id}/redeem/{redeem_code}
GET /v2/coupons/{campaign_id}/reports
GET /v2/coupons/{campaign_id}/reports/totals
For more information about the campaign reports, please check our Reports Documentation.