sparkfly-api 1.0.0
Sparkfly Platform API
Keywords: OpenAPI,OpenAPI-Generator,Sparkfly Platform API

    # Getting Started ## Overview  The Sparkfly Platform provides a full lifecycle for promotions and rewards from creation to distribution to settlement. The platform integrates in real-time at the point-of-sale and provides item level discounting and tracking. The capabilities of the Sparkfly Platform are available through the use of the Sparkfly Platform API. ## General API Information The Sparkfly Platform API is implemented using a RESTful interface that allows access to all functionality of the Sparkfly Platform. Communication with the API is performed using HTTPS on port 443.\n  ## Request / Response The Sparkfly Platform API supports the JSON data serialization format. The HTTP Content-Type header is used to specify the request format and the HTTP Accept header or a URI extension is used to specify the response format. Content-Type is required if a transaction includes a request body. Either the Accept Header or URI Extension is required if a transaction includes a response body. The URI Extension will take precedence if a conflicting Accept Header and URI Extension are received.  | Format | Content-Type | Accept-Header | URI Extension | ------ | ------------ | ------------- | ------------- | JSON | application/json | application/json | .json  ### Sample JSON Request with Headers We use cURL for demonstration purposes, but you can use any client of your choosing. ### Request &#x60;&#x60;&#x60; $ curl -i --header &#39;X-Auth-Token: 228b236272b6ffc7be0496a5f8186f4767afa3ade292ea1565831892941bb6cd&#39; \       --header &#39;Content-Type: application/json&#39; https://api.sparkfly.com/v1.0/members/1 &#x60;&#x60;&#x60; ### Response Headers &#x60;&#x60;&#x60; HTTP/1.1 200 OK Content-Type: application/json; charset&#x3D;utf-8 X-Runtime: 0.042759 Content-Length: 153 &#x60;&#x60;&#x60; The request would work the same way if we had sent .json at the end of the request URI (/v1.0/members/1.json) instead of sending the \\&quot;application/json\\&quot; header. ## Authentication Authentication is performed against the API by providing a specified Account ID and API key within the HTTP x-headers as X-Auth-Identity and X-Auth-Key. Accounts have particular privileges providing various levels of access to resources within the platform. ### Request &#x60;&#x60;&#x60; $ curl -i --header &#39;X-Auth-Identity: &lt;SPARKFLY_PROVIDED_ID&gt;&#39; \       --header &#39;X-Auth-Key: &lt;SPARKFLY_PROVIDED_KEY&gt;&#39; \       https://api.sparkfly.com/auth &#x60;&#x60;&#x60;  ### Response Headers &#x60;&#x60;&#x60; HTTP/1.1 204 No Content X-Auth-Token: ab598e824fd304af32fe34ed4a1af1210ce226ecf05c5f043a4f388d3ab74b12 &#x60;&#x60;&#x60; Note how the response headers will contain an X-Auth-Token. All future API operations will only need to pass the received X-Auth-Token for authentication. Tokens will be valid for 24 hours. Applications must be developed in order to re-authenticate if an invalid token is used. ## API Versioning The API version is required and is indicated in the first element of the URI and will take the form: v[VERSION].  API version numbers will only be incremented when functionality is added that breaks compatibility with previous API versions.  Example: https://api.sparkfly.com/v1.0/ ## API Operations Please see the main section of the documentation for request/response examples of the actions that are possible. # Sample Implementation The purpose of this document is to give you a basic overview of some of the core API features. It assumes you already have API credentials and access to the Admin Portal.  ## Authentication The first thing you will want to do is use your provided API credentials to authenticate and get an authentication token. Please see the documentation concerning authentication for more information. In all the requests below, we will need to send our API token in our request headers.  ## Members A typical setup step will be to tell the API about your user database. This way, you can create offers for your members. First let&#39;s see if there are any users in the database.  &#x60;&#x60;&#x60; $ curl -i --header &#39;X-Auth-Token: &lt;YOUR-AUTH_TOKEN&gt;&#39; https://api-staging.sparkfly.com/v1.0/members.json &#x60;&#x60;&#x60;  ### Status  &#x60;&#x60;&#x60; HTTP/1.1 200 OK &#x60;&#x60;&#x60; ### Body &#x60;&#x60;&#x60; {   \&quot;page\&quot;: 1,   \&quot;per_page\&quot;: 50,   \&quot;total_entries\&quot;: 0,   \&quot;total_pages\&quot;: 0,   \&quot;members\&quot;: [] } &#x60;&#x60;&#x60;  We can see that there are no members yet. Let&#39;s create one.  &#x60;&#x60;&#x60; $ curl -i --header &#39;X-Auth-Token: &lt;YOUR-AUTH_TOKEN&gt;&#39; \       -XPOST -d &#39;{ \&quot;member\&quot;: { \&quot;identifier\&quot;: \&quot;unique-member-id\&quot; } }&#39; \       https://api-staging.sparkfly.com/v1.0/members.json &#x60;&#x60;&#x60;  ### Status &#x60;&#x60;&#x60; HTTP/1.1 201 Created &#x60;&#x60;&#x60;  ### Body &#x60;&#x60;&#x60; {   \&quot;member\&quot;: {     \&quot;identifier\&quot;: \&quot;unique-member-id\&quot;,     \&quot;notification_mode\&quot;: \&quot;app\&quot;,     \&quot;id\&quot;: 1009,     \&quot;created_at\&quot;: \&quot;2013-01-28T21:44:10Z\&quot;,     \&quot;updated_at\&quot;: \&quot;2013-01-28T21:44:10Z\&quot;   } } &#x60;&#x60;&#x60;  First, we send a POST to /v1.0/members.json to create our new member. The only thing you need to provide is a unique identifier to relate the member back to your system. You can repeat this any number of times until all of your users are loaded. Notice how in the response body, the newly created member has an ID of 1009. We can use that to request a specific member (or any resource) in the system at a later time by using the ID like so:  &#x60;&#x60;&#x60; $ curl -i --header &#39;X-Auth-Token: &lt;YOUR-AUTH_TOKEN&gt;&#39; \       https://api-staging.sparkfly.com/v1.0/members/1009.json &#x60;&#x60;&#x60;  ### Status &#x60;&#x60;&#x60; HTTP/1.1 200 OK &#x60;&#x60;&#x60;  ### Body &#x60;&#x60;&#x60; {   \&quot;member\&quot;: {     \&quot;identifier\&quot;: \&quot;unique-member-id\&quot;,     \&quot;notification_mode\&quot;: \&quot;app\&quot;,     \&quot;id\&quot;: 1009,     \&quot;created_at\&quot;: \&quot;2013-01-28T21:44:10Z\&quot;,     \&quot;updated_at\&quot;: \&quot;2013-01-28T21:44:10Z\&quot;   } } &#x60;&#x60;&#x60;  If a member is coming from your application, you may also wish to search for them by your internal identifier. You can do that by querying the member search API endpoint:   &#x60;&#x60;&#x60; $ curl -i --header &#39;X-Auth-Token: &lt;YOUR-AUTH_TOKEN&gt;&#39; \       https://api-staging.sparkfly.com/v1.0/members/search.json?identifier&#x3D;unique-member-id &#x60;&#x60;&#x60;  ### Status &#x60;&#x60;&#x60; HTTP/1.1 200 OK &#x60;&#x60;&#x60;  ### Body &#x60;&#x60;&#x60; {   \&quot;member\&quot;: {     \&quot;identifier\&quot;: \&quot;unique-member-id\&quot;,     \&quot;notification_mode\&quot;: \&quot;app\&quot;,     \&quot;id\&quot;: 1009,     \&quot;created_at\&quot;: \&quot;2013-01-28T21:44:10Z\&quot;,     \&quot;updated_at\&quot;: \&quot;2013-01-28T21:44:10Z\&quot;   } } &#x60;&#x60;&#x60;  ## Offers Next we&#39;ll want to set up an offer. You can create and configure a offer using the API if desired, but it it usually easier to do this through the Admin Portal. Follow the instructions in the Portal, and by the end of the process you should have at least one Merchant, and an Offer (this is the minimum setup needed to run an offer and generate codes for your members). It is also important to note that a Offer will be run by one or more Channels. A Channel is linked to a offer in a way that allows the system to track where your members are coming from, so that you can view offer statistics and control the number of members that redeem a offer.\n\nAssuming we&#39;re done setting things up, let&#39;s query for our offer and make sure everything is working &#x60;&#x60;&#x60; $ curl -i --header &#39;X-Auth-Token: &lt;YOUR-AUTH_TOKEN&gt;&#39; https://api-staging.sparkfly.com/v1.0/offers.json &#x60;&#x60;&#x60; ### Status &#x60;&#x60;&#x60; HTTP/1.1 200 OK &#x60;&#x60;&#x60;  ### Body &#x60;&#x60;&#x60; {   \&quot;page\&quot;: 1,   \&quot;per_page\&quot;: 50,   \&quot;total_entries\&quot;: 1,   \&quot;total_pages\&quot;: 1,   \&quot;offers\&quot;: [     {       \&quot;offer\&quot;: {         \&quot;id\&quot;: 1701,         \&quot;name\&quot;: \&quot;Buy a Burger, Get Free Medium Fries\&quot;,         \&quot;description\&quot;: null,         \&quot;offer_type\&quot;: 0,         \&quot;offer_code\&quot;: null,         \&quot;points_earning_value\&quot;: null,         \&quot;points_required_value\&quot;: null,         \&quot;reward_item_description\&quot;: null,         \&quot;reward_item_value\&quot;: null,         \&quot;terms_and_conditions\&quot;: null,         \&quot;quest_only\&quot;: false,         \&quot;max_amount\&quot;: null,         \&quot;min_spend_amount\&quot;: null,         \&quot;max_redemptions\&quot;: null,         \&quot;max_redemptions_per_member\&quot;: null,         \&quot;max_redemptions_per_member_per_day\&quot;: null,         \&quot;merchant_name\&quot;: null,         \&quot;activates_at\&quot;: null,         \&quot;expires_at\&quot;: null,         \&quot;stop_offering_at\&quot;: null,         \&quot;created_at\&quot;: \&quot;2013-01-28T22:25:33Z\&quot;,         \&quot;updated_at\&quot;: \&quot;2013-01-28T22:25:33Z\&quot;       }     }   ] } &#x60;&#x60;&#x60; Great! Now we can see our offer \&quot;Buy a Burger, Get Free Medium Fries\&quot;  ## Creating Credentials For Members After we release our offer, we will want to generate redeemable codes for our members. We do this by creating a record called a Credential. A Credential includes a code which allows a member to redeem a offer at a given location. A typical scenario might go something like this. A member is in your store and is using your Cool Mobile App (Channel ID 456), they click a button that says \&quot;Redeem Now\&quot;, and you app triggers a backend query to the Sparkfly Platform to generate a credential like so  &#x60;&#x60;&#x60; $ curl -i --header &#39;X-Auth-Token: &lt;YOUR-AUTH_TOKEN&gt;&#39; \       -X POST \       https://api-staging.sparkfly.com/v1.0/credentials.json?member_id&#x3D;1009&amp;offer_id&#x3D;1701&amp;channel_id&#x3D;456 &#x60;&#x60;&#x60;  ### Status &#x60;&#x60;&#x60; HTTP/1.1 201 Created &#x60;&#x60;&#x60; ### Body &#x60;&#x60;&#x60; {   \&quot;credential\&quot;: {     \&quot;id\&quot;: 578,     \&quot;identifier\&quot;: \&quot;3540\&quot;,     \&quot;status\&quot;: \&quot;open\&quot;,     \&quot;member_id\&quot;: 1009,     \&quot;offer_id\&quot;: 1701,     \&quot;store_id\&quot;: 1,     \&quot;merchant_id\&quot;: 251,     \&quot;redeemed_at\&quot;: null,     \&quot;voided_at\&quot;: null,     \&quot;offer_name\&quot;: \&quot;Buy a Burger, Get Free Medium Fries\&quot;,     \&quot;merchant_name\&quot;: \&quot;Q Burger\&quot;,     \&quot;location_address\&quot;: null,     \&quot;code_lifetime\&quot;: null   } } &#x60;&#x60;&#x60; Congratulations! You now have a Credential that you can display for your user. The key thing to look for here is the identifier field, \&quot;3540\&quot;. This code is what can be entered in at the point of sale to trigger a redemption in Sparkfly&#39;s system!  Check out the section entitled Credentials In Depth if you run into any problems, or need more information on generating credentials.  ## Setting Up a Landing Page Sparkfly provides a web-based landing page for any offer that you would like to have take advantage of this capability. The landing page provides additional tracking information in the form of Impressions, as well as the option of additional security. The following section explains how to setup an offer to use the landing page via the API.  First, make sure you are authenticated with the API and have at least one created offer.  &#x60;&#x60;&#x60; $ curl -i --header &#39;X-Auth-Token: &lt;YOUR-AUTH_TOKEN&gt;&#39; \       https://api-staging.sparkfly.com/v1.0/offers.json &#x60;&#x60;&#x60;  ### Body &#x60;&#x60;&#x60; {   \&quot;page\&quot;: 1,   \&quot;per_page\&quot;: 50,   \&quot;total_entries\&quot;: 0,   \&quot;total_pages\&quot;: 0,   \&quot;offers\&quot;: [] } &#x60;&#x60;&#x60; Since we don&#39;t have any offers yet we will create one now. All we need is a name.  &#x60;&#x60;&#x60; $ curl -i --header &#39;X-Auth-Token: &lt;YOUR-AUTH_TOKEN&gt;&#39; \       -X POST -d &#39;{ \&quot;offer\&quot;: { \&quot;name\&quot;: \&quot;Test offer\&quot; } }&#39; \       https://api-staging.sparkfly.com/v1.0/offers.json &#x60;&#x60;&#x60;  ### Body  &#x60;&#x60;&#x60; {   \&quot;offer\&quot;: {     \&quot;id\&quot;: 1290,     \&quot;merchant_id\&quot;: null,     \&quot;manufacturer_id\&quot;: null,     \&quot;name\&quot;: \&quot;Test offer\&quot;,     \&quot;description\&quot;: null,     \&quot;offer_type\&quot;: 0,     \&quot;category\&quot;: null,     \&quot;offer_code\&quot;: null,     \&quot;pos_offer_code\&quot;: null,     \&quot;criteria\&quot;: {},     \&quot;points_earning_value\&quot;: null,     \&quot;points_required_value\&quot;: null,     \&quot;reward_item_description\&quot;: null,     \&quot;reward_item_value\&quot;: null,     \&quot;terms_and_conditions\&quot;: null,     \&quot;quest_only\&quot;: false,     \&quot;merchant_name\&quot;: null,     \&quot;external_reward\&quot;: null,     \&quot;is_reward\&quot;: false,     \&quot;locked\&quot;: null,     \&quot;activates_at\&quot;: null,     \&quot;expires_at\&quot;: null,     \&quot;stop_offering_at\&quot;: null,     \&quot;max_amount\&quot;: null,     \&quot;min_spend_amount\&quot;: null,     \&quot;max_redemptions\&quot;: null,     \&quot;max_redemptions_per_member\&quot;: null,     \&quot;max_redemptions_per_member_per_day\&quot;: null,     \&quot;account_id\&quot;: 8,     \&quot;required_item_set_id\&quot;: null,     \&quot;redemption_item_set_id\&quot;: null,     \&quot;required_item_count\&quot;: null,     \&quot;redemption_item_count\&quot;: null,     \&quot;status\&quot;: \&quot;pending\&quot;,     \&quot;created_at\&quot;: \&quot;2014-02-11T17:44:54Z\&quot;,     \&quot;updated_at\&quot;: \&quot;2014-02-11T17:44:54Z\&quot;,     \&quot;offers\&quot;: [],     \&quot;formatting\&quot;: {},     \&quot;passbook_configuration\&quot;: null,     \&quot;landing_page_urls\&quot;: [],     \&quot;channels\&quot;: [],     \&quot;item_sets\&quot;: [],     \&quot;eligibility\&quot;: {       \&quot;merchant_item_set_ids\&quot;: [],       \&quot;manufacturer_item_set_ids\&quot;: [],       \&quot;merchant_ids\&quot;: [],       \&quot;store_list_ids\&quot;: [],       \&quot;member_list_ids\&quot;: [],       \&quot;channel_ids\&quot;: []     }   },   \&quot;errors\&quot;: {} } &#x60;&#x60;&#x60;  We have created an offer with ID 1290. We also need to approve the offer by sending a PUT request to the approve URL before it will appear correctly on the landing page  &#x60;&#x60;&#x60; $ curl -i --header &#39;X-Auth-Token: &lt;YOUR-AUTH_TOKEN&gt;&#39; \       -X PUT \       https://api-staging.sparkfly.com/v1.0/offers/1290/actions/approve.json &#x60;&#x60;&#x60;  Next, make sure you have at least one Channel on which to publish your offer.  &#x60;&#x60;&#x60; $ curl -i --header &#39;X-Auth-Token: &lt;YOUR-AUTH_TOKEN&gt;&#39; \       https://api-staging.sparkfly.com/v1.0/channels.json &#x60;&#x60;&#x60;  If the total_entries field is 0, we will need to create a channel as well. For demonstration purposes, we&#39;ll create a landing page for an email campaign. Again, only a name is required here.   &#x60;&#x60;&#x60; $ curl -i --header &#39;X-Auth-Token: &lt;YOUR-AUTH_TOKEN&gt;&#39; \       -X POST -d &#39;{ \\&quot;channel\\&quot;: { \&quot;name\&quot;: \&quot;Email\&quot; } }&#39; \       https://api-staging.sparkfly.com/v1.0/channels.json &#x60;&#x60;&#x60; ### Body &#x60;&#x60;&#x60; {   \&quot;channel\&quot;: {     \&quot;id\&quot;: 5,     \&quot;name\&quot;: \&quot;Email\&quot;,     \&quot;allow_sms_keyword\&quot;: false,     \&quot;allow_hmac\&quot;: false,     \&quot;conversion_callback_url\&quot;: null   },   \&quot;errors\&quot;: {} } &#x60;&#x60;&#x60; Now we also have a channel with an ID of 5, meaning we now have at least one offer and one channel. Now we will need to associate the two. To associate the channel to the offer, we will need to POST to the following URL using your offer ID and a body containing an array with your channel ID. &#x60;&#x60;&#x60; $ curl -i --header &#39;X-Auth-Token: &lt;YOUR-AUTH_TOKEN&gt;&#39; \     -X POST -d &#39;{ \&quot;offer\&quot;: { \&quot;channel_ids\&quot;: [5] } }&#39; \     https://api-staging.sparkfly.com/v1.0/offers/1290/channels.json &#x60;&#x60;&#x60; We should get a 201 Created response code and something like: &#x60;&#x60;&#x60; {   \&quot;offer\&quot;: {     \&quot;channel_ids\&quot;: [       5     ],     \&quot;id\&quot;: 1290   } } &#x60;&#x60;&#x60; Now if we perform a GET request on the offer, we should see that the channel is associated. The section we are interested in is the field called \&quot;channels\&quot; on the offer. &#x60;&#x60;&#x60; $ curl -i --header &#39;X-Auth-Token: &lt;YOUR-AUTH_TOKEN&gt;&#39; \     https://api-staging.sparkfly.com/v1.0/offers/1290.json &#x60;&#x60;&#x60;  ### Body (some content omitted) &#x60;&#x60;&#x60; {   \&quot;offer\&quot;: {     \&quot;channels\&quot;: [       {         \&quot;channel\&quot;: {           \&quot;id\&quot;: 5,           \&quot;xid\&quot;: \&quot;Z7nnWs6\&quot;,           \&quot;name\&quot;: \&quot;Email\&quot;,           \&quot;security\&quot;: null,           \&quot;security_key\&quot;: null,           \&quot;offer_id\&quot;: 1290,           \&quot;sms_keyword\&quot;: null,           \&quot;allow_return_later\&quot;: true         }       }     ]   } } &#x60;&#x60;&#x60; In particular, pay attention to the \&quot;xid\&quot; field. This is an identifier that links your offer to your channel, and it will act as the landing page URL. I should now be able to visit the landing page, with the xid as the path, and see my offer. For example, in the staging environment, our landing page would be: https://mp-staging.sparkfly.com/Z7nnWs6  ### Note Landing pages are cached for performance, and are re-cached every hour. If you go to a landing page, and get a 404 or do not see your changes, you may need to wait up to an hour for the page to be updated.  So at this point, we should be able to see a relatively plain offer page. Let&#39;s add a banner image to show up at the top of the landing page. To add an image, we will need to update the offer field called \&quot;formatting\&quot; on the offer, by sending a PUT request. Formatting is a nested object which contains a field called \&quot;bg_image\&quot;. If we update bg_image, it should show up on the landing page:  &#x60;&#x60;&#x60; $ curl -i --header &#39;X-Auth-Token: &lt;YOUR-AUTH_TOKEN&gt;&#39; \         -X PUT -d &#39;{ \&quot;offer\&quot;: { \&quot;formatting\&quot;: { \&quot;bg_image\&quot;: \&quot;https://my-domain/banner_image.png\&quot; } } }&#39; \         https://api-staging.sparkfly.com/v1.0/offers/1290.json &#x60;&#x60;&#x60;  We should get a 200 response that shows our bg_image in the response.  The recommended size for a banner image is 1200x600px. You can also set a background color for the header by setting \&quot;bg_color\&quot; in the formatting options, like so: &#x60;&#x60;&#x60; $ curl -i --header &#39;X-Auth-Token: &lt;YOUR-AUTH_TOKEN&gt;&#39; \         -X PUT -d &#39;{ \&quot;offer\&quot;: { \&quot;formatting\&quot;: { \&quot;bg_image\&quot;: \&quot;https://my-domain/banner_image.png\&quot;, \&quot;bg_color\&quot;: \&quot;#FF00AA\&quot; } } }&#39; \         https://api-staging.sparkfly.com/v1.0/offers/1290.json &#x60;&#x60;&#x60; ## Other Considerations For security purposes, the landing page is served over SSL. In order to maintain this security, any image you link to should also be served over https.  If you&#39;re testing a landing page from a desktop or laptop computer, the default behavior is to show a static, printable code. If you would like to preview what the landing page will look like on a mobile device, you can attach the parameter printable&#x3D;false to the landing page URL. For example https://mp-staging.sparkfly.com/TRYstCR?printable&#x3D;false  # Credentials  ## Introduction Credentials are the backbone of the Sparkfly API. When issued correctly, they allow a member to redeem one or more offers. Sparkfly will properly handle the creation, expiration and redemption of identifiers that can be used to link a member to a purchase at the point of sale.  ## Associating Multiple Offers Credentials can exist that allow for more than one offer to be redeemed at a time. We&#39;re assuming you have already authenticated an have a valid auth token at this time (see the authentication section for an example). Below is an example of multiple offers on a credential:  ### Associating Multiple Offers First we can create a new credential for our member with id #123. After that is successful, we can attempt to attach our offers.  #### Request &#x60;&#x60;&#x60; $ curl -i -XPOST --header &#39;X-Auth-Token: d47bfec9446fbe92ee9f7a7da3d1ab973ec0802c28b15f6d9a95ada41937b07d&#39; \       &#39;https://api.sparkfly.com/v1.0/credentials.json?member_id&#x3D;123&#39; &#x60;&#x60;&#x60; ### First JSON Response  #### Response Body &#x60;&#x60;&#x60; {   \&quot;error\&quot;: \&quot;channel_id is required\&quot; } &#x60;&#x60;&#x60; Oops! We can see here that the request failed, because we forgot to include a channel_id. If you don&#39;t yet have a channel, see the channel API under main to see how to create one. Let&#39;s assume we have a working channel, add the ID to the request, and try again.  #### Request &#x60;&#x60;&#x60; $ curl -i -XPOST --header &#39;X-Auth-Token: d47bfec9446fbe92ee9f7a7da3d1ab973ec0802c28b15f6d9a95ada41937b07d&#39; \       &#39;https://api.sparkfly.com/v1.0/credentials.json?member_id&#x3D;123&amp;channel_id&#x3D;456&#39; &#x60;&#x60;&#x60; ### Second JSON Response #### Response Body &#x60;&#x60;&#x60; {   \&quot;credential\&quot;: {     \&quot;id\&quot;: 1009,     \&quot;account_id\&quot;: 5,     \&quot;created_at\&quot;: \&quot;2013-09-16T18:41:13Z\&quot;,     \&quot;updated_at\&quot;: \&quot;2013-09-16T18:41:13Z\&quot;,     \&quot;identifier\&quot;: null,     \&quot;member_id\&quot;: 123,     \&quot;store_id\&quot;: null,     \&quot;channel_id\&quot;: 456,     \&quot;offer_ids\&quot;: [],     \&quot;annotations\&quot;: {},     \&quot;code_lifetime\&quot;: null,     \&quot;supports_barcode\&quot;: false,     \&quot;merchant_id\&quot;: null,     \&quot;offer_name\&quot;: null,     \&quot;merchant_name\&quot;: null,     \&quot;location_address\&quot;: null,     \&quot;url\&quot;: null   },   \&quot;errors\&quot;: {} } &#x60;&#x60;&#x60;  Great! Now we get a 201 (Created) response, with associated member and channel IDs. The credential comes back with an ID of 1009. You&#39;ll notice that the identifier is \&quot;null\&quot;, because there are no associated offers yet (offer_ids is an empty set).  Next we&#39;ll want to associate offers so we can get an identifier generated. We can do this by issuing a PUT request to the newly created credential. The first offer we want to associate has an ID of 1011 &#x60;&#x60;&#x60; $ curl -i -XPUT --header &#39;X-Auth-Token: d47bfec9446fbe92ee9f7a7da3d1ab973ec0802c28b15f6d9a95ada41937b07d&#39; \       &#39;https://api.sparkfly.com/v1.0/credentials/1009.json?channel_id&#x3D;456&amp;offer_ids&#x3D;1011&#39; &#x60;&#x60;&#x60; ### Third JSON Response #### Response Body &#x60;&#x60;&#x60; {   \&quot;credential\&quot;: {     \&quot;id\&quot;: 1009,     \&quot;account_id\&quot;: 5,     \&quot;created_at\&quot;: \&quot;2013-09-16T18:41:13Z\&quot;,     \&quot;updated_at\&quot;: \&quot;2013-09-16T18:51:23Z\&quot;,     \&quot;identifier\&quot;: \&quot;3345\&quot;,     \&quot;member_id\&quot;: 1,     \&quot;store_id\&quot;: null,     \&quot;channel_id\&quot;: 456,     \&quot;offer_ids\&quot;: [       1011     ],     \&quot;annotations\&quot;: {},     \&quot;code_lifetime\&quot;: null,     \&quot;supports_barcode\&quot;: false,     \&quot;merchant_id\&quot;: null,     \&quot;offer_name\&quot;: \&quot;First Offer\&quot;,     \&quot;merchant_name\&quot;: null,     \&quot;location_address\&quot;: null,     \&quot;url\&quot;: null   },   \&quot;errors\&quot;: {} } &#x60;&#x60;&#x60; Once the credential is updated, we should see our offer ID in the offer_ids of the response, as well as our newly generated identifier (3345). At this point, the credential is valid and ready to redeem the single offer, with id 1011. &#x60;&#x60;&#x60; $ curl -i -XPUT --header &#39;X-Auth-Token: d47bfec9446fbe92ee9f7a7da3d1ab973ec0802c28b15f6d9a95ada41937b07d&#39; \         &#39;https://api.sparkfly.com/v1.0/credentials/1009.json?channel_id&#x3D;456&amp;offer_ids&#x3D;1011&#39; &#x60;&#x60;&#x60; We can repeat this process to add as many offers as needed like so: &#x60;&#x60;&#x60; $ curl -i -XPUT --header &#39;X-Auth-Token: d47bfec9446fbe92ee9f7a7da3d1ab973ec0802c28b15f6d9a95ada41937b07d&#39; \       &#39;https://api.sparkfly.com/v1.0/credentials/1.json?channel_id&#x3D;456&amp;offer_ids&#x3D;1011,1116&#39; &#x60;&#x60;&#x60; Assuming both offers are valid, the JSON response will now show both offer_ids associated with the credential. ## Validations and Errors Validation | Error | Solution -----------|-------|--------- Channel ID is not present | Channel can&#39;t be blank and must be associated with your account and offer |  Make sure you send channel_id&#x3D;&lt;your-channel-id&gt; on every request to create or update a credential. You will also need to make sure that the offers you are associating with the credential are also eligible for that same channel (shows up under the offer&#39;s eligibility). Member ID is not present |  Member can&#39;t be blank |  Make sure you send member_id&#x3D;&lt;sparkfly-member-id&gt; on every request to create a credential  Associated offer(s) are not approved |  offer &lt;offer-name&gt; must be approved to generate a credential |  When you request an offer by id, the status should show up as approved. (See the Approve Offer action under Offers). Member is not eligible for offer(s) |  Member must be eligible for offer: &lt;offer-name&gt; |  If you have a member list defined for the offer, make sure the member in question is on that list. Additionally, a member may not be eligible for an offer if you have redemption limits set. That is, an offer may have reached the total number of redemptions allowed as a whole, by channel, per member, or you may have hit the daily limit per member per day as defined on the offer itself. One or more offers is not longer active |  Offer with ID &lt;offer-id&gt; is no longer active |  An offer must have an activates_at datetime field that is before the current date, and an expires_at datetime field that is after the current date. Store is not eligible for offer(s) |  Store must be eligible for offer: &lt;offer-name&gt; |  If you have an eligible store list defined for the offer, make sure the store in question is on that list. Remember, store id is optional, and not needed to create a credential. Credential is locked |  This credential is currently locked for processing. Please try again later. |  You had a member who went through the process of attempting to redeem an offer with this credential. In some cases, there may be a slight delay in processing. If a member attempts to immediately re-use a credential before it can be processed, this error may be returned. This shouldn&#39;t ever really be seen, but it is something to be aware of during testing.  Requested offers not found |  Offer not found or not eligible by channel, with id: &lt;one-or-more-requested-ids&gt; |  You are trying to associate one or more offers that either can&#39;t be found because the ids are not correct, or the offers are not eligible to be run under the channel id you are requesting. Make sure the offers exist and are eligible to appear on the requested channel. If you see the last message that offers were not found, remember that you need to attach eligible channels with each offer that you run. This can be done in the admin portal under the Publishing Channels section of the offer. If you do not see any channels listed at all, you can enable them when creating a merchant account. You will not be able to generate a credential code without at least one channel attached to your offer. ## Channel Callback When a redemption (conversion) event occurs, it is sometimes desired to get a realtime callback posted to your system for real-time tracking purposes. This can be achieved by specifying a conversion_callback_url on a channel that you control. The Sparkfly system can send data to the conversion_callback_url every time an offer is redeemed.  File | Description -----|------------ credential_id |  The Sparkfly ID of the credential that was redeemed.  xid |  The XID for the offer that was redeemed (XID is a unique GUID for offer and channel)  offer_id |   The offer that was redeemed on this channel  offer_ids |  All offers associated with the redeemed credential  store_id |  The Sparkfly ID for the store where the credential was redeemed  redeemed_at |  When the credential was redeemed  status |  The status of the redeemed credential  sf_uid |  The external identifier of the member who redeemed the credential (for promotions that use Sparkfly&#39;s landing page)  sf_ac |  The name of the creative that was viewed to generate the credential (for promotions that use Sparkfly&#39;s landing page)  You can substitute any of the above values in your conversion_callback_url by using the %{} notation like so: &#x60;&#x60;&#x60; https://my_server.com/callback/%{credential_id}?time_of_redemption&#x3D;%{redeemed_at} &#x60;&#x60;&#x60;  You would then receive a callback on redemption, with the specific values in place of each %{}.  In addition the the parameters named above, any attributes under the \&quot;conv_ids\&quot; key of the credential&#39;s annotations will also be available for selection. See the credential creation documentation for examples of how to set the credential&#39;s annotations. Any keys that cannot be resolved will result in blank parameters in the final URL.