# Coupons

Add promotional codes to your messages. {{< badge "axp" >}}

## About Coupons

![A landing page with a promotional code — Barcode requires JavaScript](https://www.airship.com/docs/images/coupons-landing-page_hu_9f85950f7b0c85f1.webp)

*A landing page with a promotional code — Barcode requires JavaScript*

Like other forms of personalization in Airship, with Coupons you create a merge field representing a value you want to personalize, surrounded by double curly braces `{{ }}`, also known as [Handlebars](https://www.airship.com/docs/reference/glossary/#handlebars). Airship replaces the merge field (and braces) with the data specified by the merge field at send time — just like mail merge in a word processor or mail client.

The source of this data (including your promotional codes) is a CSV file you upload to Airship and the campaign you define for the codes. We refer to this source as a *feed* for the campaign, and you'll see the term in the dashboard and in the required formatting for your message content.

Example uses:

   * Provide exclusive discounts or rewards to retain or transition your customers and build loyalty
   * Clear out excess or slow-moving inventory
   * Re-engage existing customers with your business

Coupons are supported for messages in both the dashboard and API.

---

Promotional codes are rendered in plain text in your messages. You can use the JsBarcode library to [render the codes as barcodes instead](#rendering-as-a-barcode).

Codes are limited to 100 requests per second and are best used with automation. Consider this rate if you intend to distribute codes via broadcasts to large audiences.

### Supported message types

You can add codes to any message content you create using the Message, A/B Test, Automation, and Sequence composers:

* Push notification
* In-app message (standard format)
* Message Center (Not supported for A/B Tests)
* Landing page
* Web push notification
* Email
* SMS
* Open channel

You can also add codes to [Templates](https://www.airship.com/docs/reference/glossary/#template) for:

* Push notification
* Message Center (can also be used for landing pages)
* Web push notification
* Email
* SMS
* Open channel

The same message types and template content are supported in the API.

Coupons are not supported for In-App Automations or Scenes, as these are ephemeral and not a good place to show the promotional code. We suggest using a link in these formats to other messaging formats that are persistent.

### Remaining code notifications

When you upload your CSV file, a value equal to 10% of the number of codes is stored and checked when a code is used in a message. We'll notify you by email when 90% of codes have been used in messages and again at 100%.

You can specify up to five email addresses for notification. Add `coupons@partner-alerts.airship.com` as a contact in your email client to ensure these messages are not marked as spam.

We repeat the calculation when you [add more codes to a campaign](#managing-campaigns-and-adding-more-codes).

### Coupons workflow

The following is the general workflow for using promotional codes in messages. For illustration, we are using retail loyalty as an example.

1. [Create your CSV file.](#formatting-your-csv-file) The first row must contain headers `coupon_code` and `campaign`. Each additional row must contain a unique alphanumeric code and the same ID for the campaign. (You can add more information in your file, but we're keeping it simple for this example).
   **Example CSV**

   ```text
coupon_code,campaign
   RS8E3589,2023loyalty
   RS8E3590,2023loyalty
   RS8E3591,2023loyalty
```


1. [Create a campaign](#creating-a-campaign) in the dashboard. You will upload your CSV file and define the following:
   * *Campaign name* — This is used for identification in the list of all campaigns in your project. 
   * *Campaign ID* — This must match what you put in your CSV. You will use the ID when creating your messages so Airship can use codes from the right campaign.
   * *Validity period* — The effective and expiration dates and times for the campaign and its codes.

   In our examples the campaign ID is `2023loyalty`.

1. [Create your content](#adding-codes-to-messages) using the following format. In all content, use the feed ID `couponing`.

   ```text
{{#feed "couponing"}}
   Content referencing the feed, like a {{coupon_code}}.
   {{/feed}}
```


   Using our loyalty example, your message would look like this in the [Interactive Editor](https://www.airship.com/docs/reference/glossary/#interactive_editor):
   ![Coupons](https://www.airship.com/docs/images/interactive-coupon_hu_5ef052723c1b0ec3.webp)

1. [Specify the campaign ID and determine failure behavior](#specifying-the-campaign-id-and-feed-failure-behavior) when sending the message.

   * In the dashboard, you'll do this in the Delivery step of a composer.
   * With the API, you'll make these specifications in the External Data Feed References object.

   > **Important:** As a best practice, verify the campaign ID, its validity period, and that it has available codes prior to sending the message. The feed will fail if the expiration date-time has passed or does not have remaining codes.  
>    
>    You can view the number of available codes for each campaign:
>    
>    1\. Next to your project name, select the dropdown menu (
> ), then **Settings**.<br>
>    2\. Under **Project settings**, select **Coupons**.


---

Boom! We just sent codes to our audience. Now what happens? This is what Airship does next:

* Populates your message content at send time, using one code from your CSV file per message
* Checks the number of remaining codes so we can process your [remaining code notifications](#remaining-code-notifications)
* Generates an event every time a code is used in a message (See next section)

> **Note:** Codes do not populate messages as ordered in your CSV file. For example, if your CSV file has rows with codes in order `1001, 1002, 1003`, they may be sent to users in order `1003, 1001, 1002`.


### Reporting

Codes are associated with the [Channel ID](https://www.airship.com/docs/reference/glossary/#channel_id) of the message recipient at send time. This association is recorded as a [Custom Event](https://www.airship.com/docs/reference/glossary/#custom_event) with name `coupon_assigned`. The event properties use keys `coupon_code` and `campaign`. `coupon_code_alt` will also appear if included in your CSV file. The property values are the actual code used in the message and the campaign ID.

The event is recorded the first time a code for a given campaign is assigned to a Channel ID. Additional events are not recorded if you send messages to the same Channel ID that references the same code.

**Example Coupons custom event**

```json
{
    "name": "coupon_assigned",
    "properties":
    {
        "coupon_code": "RS8E3589",
        "campaign": "2023loyalty"
    }
}
```


## Formatting your CSV file

When uploading in the dashboard, maximum file size 1.5 GB. There is no file size limit when using SFTP [to add more codes](#managing-campaigns-and-adding-more-codes) to a campaign. To ensure the support of special characters and accents, the file must be encoded to UTF-8 without BOM.

* `campaign` — Required. The campaign ID. Used to determine which campaign to use for populating message content. Ignored when uploaded in the dashboard. Must match an existing campaign when uploaded via SFTP.
* `coupon_code` — Required. A unique alphanumeric value to render in a message. Duplicate values are ignored and not added to the campaign as available codes.
* `coupon_code_alt` Optional. An alternative version of the coupon code (such as an online code) that can be rendered in a message. Must be a unique alphanumeric value.
 * `starts_time` — Optional. The effective date-time for the campaign. Must be in ISO 8601 date-time format `YYYY-MM-DDThh:mm:ss`. Defaults to current date-time if not provided. **Ignored when file is uploaded in the dashboard.** Updates the campaign effective date-time in the dashboard when uploaded via SFTP.
* `ends_time` — Optional. The expiration date-time for the campaign. Must be in ISO 8601 date-time format `YYYY-MM-DDThh:mm:ss`. **Ignored when uploaded in the dashboard.** Updates or sets the campaign expiration date-time in the dashboard when uploaded via SFTP.

### Pre-assigning coupon codes

By default, coupon codes are randomly assigned to recipients at send time. You can pre-assign coupon codes to specific contacts by adding user IDs to your CSV file. Pre-assigned coupon codes are not added to the campaign as available codes or included in the [remaining code notifications](#remaining-code-notifications).

To pre-assign codes to [Channel IDs](https://www.airship.com/docs/reference/glossary/#channel_id), add these headers and values to your CSV:

* `identifier_type` — Set to `channel_id`.
* `identifier` — The Channel ID of the contact to pre-assign to the coupon code.

To pre-assign codes using [Named User](https://www.airship.com/docs/reference/glossary/#named_user) IDs, you must first contact your technical account manager or [Airship Support](https://support.airship.com/) to enable the feature. Once enabled, use the same headers as above, but with these values:

* `identifier_type` — Set to `named_user`.
* `identifier` — The Named User ID of the contact to pre-assign to the coupon code.

<!-- Handle in a later update with PINT-1782 We aren't covering this info in the Reporting section.

If not including now, we need to remove them from the sample CSV that follows.

* `allow_multiple_coupons` — Optional. Integer value (0 or 1) indicating if a single identifier can have more than one promotional code assigned. Default is 0, which means only one promotional code can be assigned to a single identifier.

By default, `coupon_codes` are associated with a single `identifier_type`.

If you want to create a campaign to provide multiple coupons per `identifier_type`, use the `allow_multiple_coupons` column and set the value to 1. 

-->

### Sample CSV for Coupons

Use the selector to see sample files containing all headers and only the required headers.


#### Only required headers


```text
coupon_code,campaign
myCouponCode0,myCampaign
myCouponCode1,myCampaign
```



#### All headers



```text
coupon_code,coupon_code_alt,campaign,starts_time,ends_time,identifier,identifier_type
myCouponCode0,myOnlineCode0,myCampaign,2023-11-20T21:43:44,2024-11-20T21:43:44
myCouponCode1,myOnlinceCode1,myCampaign,2023-11-20T21:43:44,2024-11-20T21:43:44,9c36e8c7-5a73-47c0-9716-99fd3d4197d5,channel_id
```




## Creating a campaign

1. Next to your project name, select the dropdown menu (
), then **Settings**.
1. Under **Project settings**, select **Coupons**.
1. Click **Create coupon campaign**.
1. Configure fields:
   * *Campaign name* — Used for identification in the list of all coupon campaigns in your project.
   * *Campaign ID* — Used to determine which campaign to use for populating message content.
      * Format supports alphanumeric characters, dashes, and underscores.
      * The ID must be unique for a project, without spaces or special characters.
      * The `campaign` value in your CSV file will be ignored.
      * **You cannot change the campaign ID later.**
   * *Effective/Expiration dates* — Optional. The beginning and end dates and times for the campaign and its codes.
      * If the effective date and time are not specified, they default to the date-time of saving the campaign.
      * **If you set a start date and time, you cannot edit them later**.
   * *Remaining codes contact* — The email address used to contact you with [remaining code notifications](#remaining-code-notifications). You can enter up to five comma-separated addresses. Addresses entered here will replace those entered for every campaign in the project.
1. Upload your CSV file.
1. Click **Save campaign**.

You will return to the [list of all campaigns in the project](#managing-campaigns-and-adding-more-codes).

## Adding codes to messages

You add codes to messages in two parts:

1. Formatting the message content
1. Specifying the campaign to use as the data feed and determining how to handle the message if the feed fails

### Formatting content

Formatting your message content is identical in the dashboard and the API. In the dashboard you add content when configuring a template or in the Content step in a composer.


#### Feed without campaign


```text
{{#feed "couponing"}}
Content referencing the feed, like a {{coupon_code}}.
{{/feed}}
```



#### Feed with campaign


```text
{{#feed "couponing" campaign="FallSavings"}}
Content referencing the feed, like a {{coupon_code}}.
{{/feed}}
```




The feed ID is always `couponing`. To render coupon codes, the campaign ID, or effective and expiration dates, use `{{coupon_code}}`, `{{campaign}}`, `{{campaign_start_date}}`, and `{{campaign_end_date}}`.

Dates are rendered in ISO 8601 date-time format: `YYYY-MM-DDThh:mm:ss`. If you want dates to appear differently in your message, use [Date and time helpers](https://www.airship.com/docs/guides/personalization/handlebars/date-time-helpers/). Examples for a campaign with expiration date 2023-10-15:

   * To render the date as `10/15/2023`, you would enter it like so in your message content: `{{dateFormat campaign_end_date locale="en-US" format="SHORT"}}`.
   * To render the date as `Oct 15, 2023`, you would use: `{{dateFormat campaign_end_date locale="en-US" format="MEDIUM"}}`.

You can use reference variables (or [Merge Fields](https://www.airship.com/docs/reference/glossary/#merge_field)) that aren't a part of a feed and variables that are part of a feed in the same `#feed` block. See [Differentiating data sources](https://www.airship.com/docs/guides/personalization/sources/external-data-feeds/#differentiating-data-sources) in *External Data Feeds*.

> **Tip:** When using the [Interactive Editor](https://www.airship.com/docs/reference/glossary/#interactive_editor) to create content for a template, email, landing page, or Message Center message, we provide a helper for inserting the feed formatting.
> 
> 1. Click *Data Feed* in the left sidebar.
> 1. Click 
>  next to *Coupon Service*. The feed ID `couponing` will be exposed.
> 1. Hover over the feed ID and click 
> .
> 1. Select a field in your message content and paste the copied data. You should see:
>    ```text
>    {{#feed "couponing"}}
>    {{/feed}}
>    ```


### Rendering as a barcode

Use the [JsBarcode library](https://github.com/lindell/JsBarcode) to render your alphanumeric codes as barcodes. Make sure that the location where you are using a barcode supports HTML and JavaScript. For example, you cannot use barcodes in push notifications since they only support plain text.

**Sample content for a barcode**

```html
<div style="text-align:center;">
   <svg id="barcode"></svg>
</div>
<script src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.5/dist/JsBarcode.all.min.js"></script>
<script>
   JsBarcode("#barcode", "{{#feed 'couponing' as |data|}}{{data.coupon_code}}{{/feed}}");
</script>
```


### Displaying an alternative coupon code

If your use case requires two versions of a coupon code such as an in-store and an online version, you can include both versions in your CSV file and display them in your message.

**Content format for alternative code**

```text
{{#feed "couponing"}}
In-store code {{coupon_code}} or an online code {{coupon_code_alt}}
{{/feed}}
```


### Specifying the campaign ID and feed failure behavior

Specifying the campaign ID and determining feed failure handling differs between the dashboard and API.

In the dashboard, you set both in the Delivery step of a composer in *External data feed options*:

* **Default value for campaign** — Enter the campaign ID (not the campaign name) for the campaign you want to use for the message. The default value is the first campaign created for the project.

* **Failure behavior** — Select *Abort sending the message* or *Send message without this data*.
   
   For most Coupons scenarios, you likely want to use *Abort...* since the message won't make sense without a code and, potentially, its validity period.. Also consider how ongoing (automated and recurring) messages will be affected if you later [delete their campaigns](#deleting-a-campaign). See also: [Success, Retry, and Error Conditions](#success-retry-and-error-conditions).

![Configuring a Coupons feed in the composer](https://www.airship.com/docs/images/coupons-delivery_hu_f6cfb36382f70974.webp)

*Configuring a Coupons feed in the composer*

---

With the API, you set the campaign ID and failure (error) behavior in the [External Data Feed References Object](https://www.airship.com/docs/developer/rest-api/ua/schemas/external-data-feeds-references/). When using a Coupons feed you must include a [`templates` object](https://www.airship.com/docs/developer/rest-api/ua/operations/personalization/) in the payload or set the `personalization` option to `true`.

   * The value for `name` is `couponing` for all Coupons campaigns.
   
   * The possible values for `on_error` are `cancel` and `continue`. These are equivalent to the dashboard options *Abort sending the message* and *Send message without this data*.
   
     `continue` will result in sending the message despite an error. Otherwise, a feed failure will prevent Airship from sending your message.
      
   * To specify the campaign ID, you must include a `defaults` object `couponing` with property `campaign` and the campaign ID as its value.

**Example push request using a Coupons feed**

```http
POST /api/push HTTP/1.1
Authorization: Basic <master authorization string>
Accept: application/vnd.urbanairship+json; version=3
Content-Type: application/json

{
   "device_types":[
      "ios"
   ],
   "audience":{
      "tag": "earlyBirds"
   },
   "notification":{
      "alert":"{{#feed \"couponing\"}} As a token of our appreciation for being a loyal Fan, please enjoy 15% off your next in-app order. Offer expires {{dateFormat campaign_end_date locale="en-US" format="SHORT"}} Use coupon code: {{coupon_code}}{{/feed}}"
   },
   "options": {
      "personalization": true
   },
   "feed_references": {
      "feeds": [
         {
            "name": "couponing",
            "params": {
            },
            "on_error": "cancel"
         }
      ],
      "defaults": {
         "couponing": {
            "campaign": "2023loyalty"
         }
      }
   }
}
```


## Managing campaigns and adding more codes

To see a list of all your campaigns and their current number of available coupons.:

1. Next to your project name, select the dropdown menu (
), then **Settings**.
1. Under **Project settings**, select **Coupons**.

Campaigns are sorted by effective date and time, latest first.

Click 
 to edit the following for a campaign:

* Name
* Expiration date and time
* Remaining codes contact — Addresses entered here will replace those entered for every campaign in the project.
* [CSV file](#formatting-your-csv-file) — You can upload a new file only. You cannot download previously uploaded data.

Generally, you only need to upload a new CSV file when you want to **add more codes** to a campaign. You can either append rows to the original CSV file used to create the campaign and reupload it or create a new CSV file containing only rows with new codes. If creating a new file, the only required headers are `coupon_code` and `campaign`, even if a previously uploaded file contained additional headers.

You also have the option to provide a new CSV file via SFTP. Understand the differences in handling before deciding on a method:

* If you upload a new CSV file using the dashboard, only `coupon_code` values will be processed. All other columns will be ignored.
* If you upload a new CSV file via SFTP, all values (other than for `campaign`) will update the current campaign settings in the dashboard. For this reason, always verify that your CSV file has the correct `campaign` value for the campaign you intend to update.

To upload a new CSV file via SFTP, see: [SFTP upload for CSV files](https://www.airship.com/docs/guides/audience/segmentation/sftp-upload/).

> **Note:** Since message content is populated at send time, if you upload a new CSV file for a campaign, scheduled messages will use the most recent campaign data. They won't necessarily use the values available when the message was created. "Scheduled" includes recurring messages.


### Deleting a campaign

> **Warning:** If you delete a campaign, any messages content referencing its data will be handled according to the messages's [feed failure behavior setting](#specifying-the-campaign-id-and-feed-failure-behavior).


1. Next to your project name, select the dropdown menu (
), then **Settings**.
1. Under **Project settings**, select **Coupons**.
1. Click 
 for a campaign.

## Success, Retry, and Error conditions

<p>Airship considers any 2xx response from a feed to be a success and will continue a message using the feed response. Airship considers any 3xx, 4xx, and 5xx codes (except 502/503) to be error conditions.</p>
<table>
  <thead>
      <tr>
          <th>HTTP status</th>
          <th>Result</th>
          <th>Behavior</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>2xx</td>
          <td>Success</td>
          <td>Airship uses the feed response, even if empty.</td>
      </tr>
      <tr>
          <td>3xx</td>
          <td>Error</td>
          <td>Error behavior determined by user.</td>
      </tr>
      <tr>
          <td>4xx</td>
          <td>Error</td>
          <td>Error behavior determined by user.</td>
      </tr>
      <tr>
          <td>5xx</td>
          <td>Error</td>
          <td>Error behavior determined by user.</td>
      </tr>
      <tr>
          <td>timeout (60 sec)</td>
          <td>Retry</td>
          <td>Up to 4 retries, 60 sec between attempts.</td>
      </tr>
      <tr>
          <td>502/503</td>
          <td>Retry</td>
          <td>Up to 4 retries, 60 sec between attempts.</td>
      </tr>
  </tbody>
</table>
