# Authentication Source: https://dev.smile.io/api/authentication Learn how to authenticate Smile REST API requests with an API key or OAuth access token. The Smile API uses [HTTP Bearer Authentication](https://swagger.io/docs/specification/authentication/bearer-authentication/) (also known as token authentication) to authenticate requests. API requests are always made on behalf of a specific store/merchant/account, however the bearer value you supply depends on the entity that is making the API request: * **Merchants -** Use an [API key](https://help.smile.io/en/articles/11328240-manage-your-api-keys). * **Apps -** Use the `OAuth Access Token` you received after the merchant completed OAuth. Once you've located your token value, supply it within an `Authorization` header of the request. These secret keys and tokens carry many privileges, so be sure to keep them a safe. Do not use or include them in publicly accessible areas such as mobile application binaries, client-side code, or in GitHub repositories. Only make API calls that include these secrets from secured backend code. All API requests must be made over HTTPS. ```bash API key theme={null} curl --location 'https://api.smile.io/v1/' \ --header 'Authorization: Bearer api_cnzGMghxTmPzK1sp' \ ``` ```bash OAuth Access Token theme={null} curl --location 'https://api.smile.io/v1/' \ --header 'Authorization: Bearer oa2_qz8mD1JdFwMgDd4o' \ ``` # Errors Source: https://dev.smile.io/api/errors Learn how the Smile REST API uses HTTP response codes and error objects to report failed requests. Smile.io uses conventional HTTP response codes to indicate the success or failure of an API request. In general, codes in the 2xx range indicate success, while codes in the 4xx or 5xx range indicate a problem and will be accompanied by a JSON error object in the response body providing more information. ## Error response codes | Status Code | Description | | :---------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 400 | The request was not accepted by the server, often due to forgotten parameters, bad syntax, or a missing `Content-Type` header. | | 401 | The required authentication credentials are missing from the request or are incorrect. | | 403 | The server is refusing to respond to the request, usually because you are requesting a resource or querying an endpoint you do not have access or permission to see/use. | | 404 | The requested resource doesn't exist. | | 422 | The request body was well-formed but contains logical or semantic errors (e.g. trying to redeem more points than the customer has). Refer to the response body for further information. | | 429 | The request was not accepted because your API consumer has exceeded it's rate limit. | | 500 | An internal error occurred in Smile. Simplify or retry your request. If the issue persists, please record any error codes, request IDs, and timestamps, and contact Smile's support. | | 503 | The server is currently unavailable. Check the [Smile Status Page](https://status.smile.io) for any reported outages. | ```json JSON theme={null} { "error": { "message": "Your request could not be completed.", "request_id": "9363e662-c1a5-4f16-a9cc-654b5ed574a9" } } ``` # Introduction Source: https://dev.smile.io/api/introduction An overview of the Smile REST API, including its conventions and base URL. The Smile API is organized around REST. It has predictable, resource-oriented URLs, and uses HTTP response codes to indicate API errors. The API uses built-in HTTP features, like HTTP authentication and HTTP verbs, which are understood by off-the-shelf HTTP clients, and supports cross-origin resource sharing, so you can interact securely with the API from a client-side web application (though you should never expose your secret API key in any public website's client-side code). All API responses return JSON, including errors. For uptime information, refer to the [Smile status page](https://status.smile.io/). ```bash Base URL theme={null} https://api.smile.io/v1 ``` # Classic pagination Source: https://dev.smile.io/api/pagination/classic-pagination Learn how to page through Smile REST API results using page numbers. With classic pagination, a collection of records is broken down into pages which are individually returned. Endpoints that use classic pagination support a `page_size` query parameter (default: `50`, maximum: `250`) which determines how many records will be included in each page of results. To move between pages of results, simply increment theΒ `page`Β query param with each subsequent request. If no `page` argument is provided, you will be returned the first page of results. When you receive a response containing fewer than `page_size` objects, you know you've reached the last page. # Cursor pagination Source: https://dev.smile.io/api/pagination/cursor-pagination Learn how to page through Smile REST API results using cursors. A cursor is an opaque string representing your current place within a collection. When making a request to an endpoint that supports cursor-based pagination, the response will include a `metadata` hash containing the cursors required to move forward or backward within the collection. If a next page is available, the metadata hash will contain a non-null `next_cursor` value. If a previous page is available, the `metadata` hash will contain a non-null `previous_cursor` value. By passing either of these values as the `cursor` argument of a subsequent request, you can retrieve the next or previous page of objects. **Note:** When paginating using cursors, results are always returned in reverse chronological order (with the most recently created first). ```json JSON theme={null} { "customers": [], "metadata": { "next_cursor": "aWQ6MixkaXJlY3Rpb246bmV4dA==", "previous_cursor": null } } ``` # Rate limits Source: https://dev.smile.io/api/rate-limits Learn about Smile REST API rate limits and how to avoid HTTP 429 errors. To ensure platform stability and efficient processing for everyone, all of our API endpoints are rate limited. All API tokens are permitted to make up to 10 requests per second, and once this limit is reached all requests will return an `HTTP 429 - Too Many Requests` error. After one second has elapsed, the token's limit will be reset and additional requests will respond normally. We actively monitor for API consumers regularly exceeding their rate limit and encourage developers to use techniques like caching, limiting the number of requests being made, and intelligent retry/backoff behavior to avoid being flagged. # Activity object Source: https://dev.smile.io/api/resources/activities/activity-object Reference for the activity object in the Smile REST API. Activities represent actions performed by customers in the loyalty program. They are used to determine when rewards should be issued, track customer behavior, and personalize the customer experience. # Create an activity Source: https://dev.smile.io/api/resources/activities/create-activity schemas/rest-api.json POST /activities Create an activity in the Smile REST API to record a customer action and trigger reward evaluation. This endpoint requires the scope. Once created, activities will be asynchronously evaluated against the loyalty program's configuration to determine if any rewards should be issued. This evaluation happens automatically and no additional API calls are required. # Create or update a customer identity Source: https://dev.smile.io/api/resources/customer-identities/create-or-update-identity schemas/rest-api.json POST /customer_identities/create_or_update Create or update a customer identity in the Smile REST API to link an external system's records to Smile customers. This endpoint requires the scope. This endpoint is only available to [apps](/guides/apps/build) making calls using an OAuth token. Attempting to use this endpoint with an API key will fail. **Creating:** If no customer identity exists with the provided `distinct_id`, a new customer identity is created. * If the provided email matches an existing customer record, the customer identity is linked to that customer. * If the provided email does not match an existing customer record, a new customer is created and a customer identity is linked to the new customer. **Updating:** If a customer identity exists with the provided `distinct_id`, it is updated with any new information provided in the request. * No changes to the linked customer record will occur. # Customer Identity object Source: https://dev.smile.io/api/resources/customer-identities/customer-identity-object Reference for the customer identity object in the Smile REST API. Customer identities represent individuals or entities in an external system and how they map to customers in a Smile loyalty program. They contain basic details like email and name, along with the external system's unique identifier for the individual. In practice, a single customer in Smile may have zero, one, or many different customer identities. # Customer object Source: https://dev.smile.io/api/resources/customers/customer-object Reference for the customer object in the Smile REST API. Customers represent individuals in the loyalty program. They have a points balance, can earn points by performing activities, and can redeem points for rewards. # List customers Source: https://dev.smile.io/api/resources/customers/list-customers schemas/rest-api.json GET /customers Retrieve a paginated list of customers in the Smile REST API, with optional lookup by email. This endpoint requires the scope. Results are sorted by `id` in descending order. To lookup customers by email, include the `email` query parameter. Note however that Smile does not enforce uniqueness on email, so multiple customer records may be returned if they have the same email address. # Retrieve a customer Source: https://dev.smile.io/api/resources/customers/retrieve-a-customer schemas/rest-api.json GET /customers/{id} Retrieve a single customer by ID in the Smile REST API. This endpoint requires the scope. To lookup customers by email, use the [list customers](/api/resources/customers/list-customers) endpoint instead. # Earning Rule object Source: https://dev.smile.io/api/resources/earning-rules/earning-rule-object Reference for the earning rule object in the Smile REST API. An earning rule represents an action that can be completed by a customer to earn a reward. Common examples include earning a dynamic number of points per dollar spent for placing an order, or earning a fixed number of points for following or liking a social media account. # List earning rules Source: https://dev.smile.io/api/resources/earning-rules/list-earning-rules schemas/rest-api.json GET /earning_rules Retrieve a list of earning rules in the Smile REST API. This endpoint requires the scope. Results are sorted by `id` in descending order. # List points products Source: https://dev.smile.io/api/resources/points-products/list-points-products schemas/rest-api.json GET /points_products Retrieve a list of points products in the Smile REST API. This endpoint requires the scope. # Points Product object Source: https://dev.smile.io/api/resources/points-products/points-product-object Reference for the points product object in the Smile REST API. Points products are rewards that customers can redeem points for. They can be fixed (a specific dollar amount off) or variable (imagine a slider where every 100 Points is an additional dollar off). # Purchase a points product Source: https://dev.smile.io/api/resources/points-products/purchase-points-product schemas/rest-api.json POST /points_products/{id}/purchase Purchase a points product on behalf of a customer in the Smile REST API. This endpoint requires the scope. # Retrieve a points product Source: https://dev.smile.io/api/resources/points-products/retrieve-points-product schemas/rest-api.json GET /points_products/{id} Retrieve a single points product by ID in the Smile REST API. This endpoint requires the scope. # Points Purchase object Source: https://dev.smile.io/api/resources/points-purchases/points-purchase-object Reference for the points purchase object in the Smile REST API. A record of the customer exchanging their points for a reward. Points purchases cannot be directly created, they are automatically generated by the system when a customer [purchases a points product](/api/resources/points-products/purchase-points-product). # Get points settings Source: https://dev.smile.io/api/resources/points-settings/get-points-settings schemas/rest-api.json GET /points_settings Retrieve an account's points program settings in the Smile REST API. This endpoint requires the scope. If the account does not have an active points program, a `404` response is returned. # Points Settings object Source: https://dev.smile.io/api/resources/points-settings/points-settings-object Reference for the points settings object in the Smile REST API. Points settings are the configuration for an account's points program. # Create a points transaction Source: https://dev.smile.io/api/resources/points-transactions/create-points-transaction schemas/rest-api.json POST /points_transactions Create a points transaction to add or deduct points from a customer's points balance in the Smile REST API. This endpoint requires the scope. Points transactions that would result in a negative points balance will be rejected. To issue points as a reward for completing an action, use the [create an activity](/api/resources/activities/create-activity) endpoint instead. # List points transactions Source: https://dev.smile.io/api/resources/points-transactions/list-points-transactions schemas/rest-api.json GET /points_transactions Retrieve a list of points transactions in the Smile REST API, such as a customer's points earning history. This endpoint requires the scope. Results are sorted by `id` in descending order. This endpoint is commonly used to display a customer's points earning history. # Points Transaction object Source: https://dev.smile.io/api/resources/points-transactions/points-transaction-object Reference for the points transaction object in the Smile REST API. Points transactions are records of a customer's points balance being changed. They can be positive (points added) or negative (points deducted), and act like a bank ledger or audit log. # Retrieve a points transaction Source: https://dev.smile.io/api/resources/points-transactions/retrieve-points-transaction schemas/rest-api.json GET /points_transactions/{id} Retrieve a single points transaction by ID in the Smile REST API. # Referral object Source: https://dev.smile.io/api/resources/referrals/referral-object Reference for the referral object in the Smile REST API. A referral is a record of a customer encouraging someone else to shop with the merchant, where both the customer and the other individual are rewarded for their actions. # List reward fulfillments Source: https://dev.smile.io/api/resources/reward-fulfillments/list-reward-fulfillments schemas/rest-api.json GET /reward_fulfillments Retrieve a list of reward fulfillments in the Smile REST API, such as a customer's discount codes. This endpoint requires the scope. Results are sorted by `id` in descending order. This endpoint is commonly used to display a list of a customer's rewards (e.g. discount codes). # Reward Fulfillment object Source: https://dev.smile.io/api/resources/reward-fulfillments/reward-fulfillment-object Reference for the reward fulfillment object in the Smile REST API. A reward fulfillment is a reward (e.g. discount code) has been issued to a customer. Reward fulfillments cannot be directly created, they are automatically generated by the system based on the merchant's loyalty program configuration when an [activity is created](/api/resources/activities/create-activity) or a [points product is purchased](/api/resources/points-products/purchase-points-product). # Reward object Source: https://dev.smile.io/api/resources/rewards/reward-object Reference for the reward object in the Smile REST API. A reward is the configuration for a type of coupon/discount/benefit/perk that can be issued to a customer. When the configuration is used to issue a reward, a reward fulfillment is generated. # VIP Tier Change object Source: https://dev.smile.io/api/resources/vip-tier-changes/vip-tier-change-object Reference for the VIP tier change object in the Smile REST API. A VIP tier change is a record of a customer moving from one VIP tier to another. # List VIP tiers Source: https://dev.smile.io/api/resources/vip-tiers/list-vip-tiers schemas/rest-api.json GET /vip_tiers Retrieve a list of VIP tiers in the Smile REST API, sorted by milestone. This endpoint requires the scope. Results are sorted by `milestone` in ascending order. This endpoint does not support pagination of any kind. # VIP Tier object Source: https://dev.smile.io/api/resources/vip-tiers/vip-tier-object Reference for the VIP tier object in the Smile REST API. A VIP tier represents a status or level that a customer can achieve as they move through a VIP program. Customers move through different VIP tiers by accruing points or making purchases. # Activities Source: https://dev.smile.io/guides/apps/activities Learn how your app can define activity types and create activities so Smile rewards customers automatically. Activities are the foundation of how Smile tracks customer behaviour and issues rewards. Apps can define new types of activities, and then notify Smile when they are performed by a customer. Once an app is installed on a merchant's account, the merchant can setup ways to earn that utilize any of the activity types defined by the app, and rewards will be issued based on the ways to earn they have configured. ## Defining a new activity type Your app should define new activity types for any kind of rewardable action that a customer can perform within your app. Common examples include things like leaving a review, subscribing to a newsletter, or rating a support conversation. To define a new activity type: 1. In the [**Partner Portal**](https://partners.smile.io), go to [**Apps**](https://partners.smile.io/apps). 2. Click on the name of the app you want to define a new activity type for. 3. Navigate to the **Activities** page for your app. 4. Click **+ Create activity type**. 5. Enter the activity type details and click **Create**. ## Creating an activity Once an activity type has been defined, use the [create an activity API endpoint](/api/resources/activities/create-activity) to notify Smile when the activity has been performed. When making API calls, be sure to use the OAuth credentials for the account on which your app is installed (not your app's private key). Creating an activity requires that you provide either the Smile Customer ID or the email address of the individual who performed the action. If you have both available, we recommend using the Smile Customer ID. # Access scopes Source: https://dev.smile.io/guides/apps/auth/access-scopes Learn how access scopes control which resources your app can read and write, and how to add new scopes. Access scopes dictate which resources an app is able to query or modify, as well as which operations an app can perform. Smile splits scopes into two main types: read and write. Traditionally, "read" operations include HTTP `GET` requests, as well as subscribing to webhooks. Meanwhile, "write" operations typically include HTTP `POST`, `PUT`, and `DELETE` requests in addition to all the "read" operations. This means that having the "write" scope for a given entity always also includes "read" privileges. The specific access scope required for each endpoint are documented as part of the [REST API Reference](/api/introduction). ## Adding access scopes As you develop your Smile app, you may wish to add more functionality. The app's settings page in the Partner Portal allows you to do just that by adding new access scopes to your app. When you add new access scopes, any accounts that have already installed the app will need to be directed back through the OAuth flow in order to grant your app the additional access scopes it now requires. It's up to you to notify your users when you want them to reauthorize your integration. To help with this, [Smile Admin](https://app.smile.io) will automatically prompt users to reauthorize if the access scopes of an app they have installed have changed. ### Effect on webhooks Your app may subscribe to webhook topics via the app's settings page in the Partner Portal. An app will only start to receive webhooks for a given topic when users grant the required scopes for that topic. For example, if your app did not have the `customer:read` scope but you would like to add the `customer/updated` webhook, you would: 1. Add the `customer/updated` webhook topic in your app's settings 2. Add the `customer:read` permission to your app 3. Prompt users to reauthorize your app As users reauthorize and grant the `customer:read` scope, your app will automatically start to receive `customer/updated`webhooks for those accounts. # OAuth errors Source: https://dev.smile.io/guides/apps/auth/oauth-errors Understand the error codes returned during Smile's OAuth flow and what to do when they happen. Errors encountered during the OAuth process conform to the [OAuth 2.0 error spec](https://tools.ietf.org/html/rfc6749#section-4.1.2.1). As a result, they have a slightly different format than the [error objects](/api/errors) used in other parts of Smile's API. OAuth errors will always contain an `error` key. They will also contain a `state` key if any state was provided by the client during the request. ```json JSON theme={null} { "error": "invalid_grant", "state": "2f64f89bf4075bb" } ``` *** ## Errors during the authorization flow If an error is encountered during the authorization phase of the OAuth flow, the user agent will be sent to the appropriate redirect URI with an `error` query parameter (as well as a `state` query parameter if state was provided). The value of the `error` query parameter will be one of the available error codes. *** ## Errors during token exchange If an error is encountered during the token exchange phase, it will be presented as an object in the request response, as seen below. The value of the `error` key will be one of the available error codes. # OAuth flow Source: https://dev.smile.io/guides/apps/auth/oauth-flow Connect and authorize your app with Smile's OAuth 2.0 flow to get credentials for making API calls. Smile has adopted an OAuth 2.0 compliant process for connecting Smile accounts to your app. This allows your app to read and write data in Smile on behalf of an account. OAuth must be used with HTTPS. OAuth is insecure if it is performed without TLS. Only use HTTPS for your client redirect URIs. *** ## Step 1: Request authorization The first step is for the client to request authorization from the user. This is done by redirecting the user to Smile's authorization page with the appropriate query parameters. ```text theme={null} GET https://connect.smile.io/oauth2/authorize?client_id={client_id}&response_type={response_type}&state={state}&redirect_uri={redirect_uri} ``` | Query parameter | Description | | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `client_id`
required | The app's Client ID. This can be found in your app's settings in the Partner Portal. | | `response_type`
required | The type of response being requested. This should always be code. | | `state`
recommended | A randomly generated value that is unique for each authorization request. During the next step, you should check that this value matches the one provided during authorization to prevent cross-site request forgery. | | `redirect_uri` | The URI to redirect the user to once the OAuth flow has been completed. This must be one of the app's whitelisted redirect URIs. Redirect URIs can be viewed and modified via the app's settings in the Partner Portal. If a redirect URI is not explicitly provided, the first one in the app's settings will be used. | *** ## Step 2: Validate authorization code After a user has approved or denied the authorization request, they will be redirected back to one of the app's whitelisted redirect URIs. If a redirect URI was provided during the authorization request, the user will be redirect there. Otherwise, they will be redirected to the first redirect URI in the app's settings in the Partner Portal. The following example demonstrates the format of the URL and the parameters that will be included when redirecting after an authorization request: ```text theme={null} https://example.com/redirect/uri?code={code}&state={state}&error={error} ``` | Query parameter | Description | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `code` | A temporary authorization code that is valid for 10 minutes and can be exchanged exactly once for an access token. | | `state` | The value of the state parameter that was provided in the authorization request. | | `error` | Only present if the user denies the authorization request, or if the authorization request was malformed in some way. See the list of errors for more details on what each one means. | Before continuing, your app should do one of two things: 1. If an `error` parameter is present, gracefully handle the error and present an informative UI to the user. 2. Otherwise, validate that the `state` parameter matches the value you passed in Step 1 when first making the authorization request. If this validation is successful, proceed to the next step to get an access token. If this validation is unsuccessful, return an error to the user and restart the OAuth process. *** ## Step 3: Get an access token To exchange an authorization code for an access token, a server-to-server request must be made. The request must be made using [Basic HTTP Authentication](https://tools.ietf.org/html/rfc2617#section-2) with the Client ID as the user-ID and the Client Secret as the password. These values can be found in the app's settings in the Partner Portal. The request will look like the following example and should also include the relevant parameters in the body: ```text theme={null} POST https://{client_id}:{client_secret}@connect.smile.io/oauth2/token?grant_type={grant_type}&code={code}&redirect_uri={redirect_uri} ``` | Parameter | Description | | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `grant_type`
required | When requesting an access token, value should be authorization\_code. | | `code`
required | The authorization code value provided in the redirect during the previous step. | | `redirect_uri`
conditional | If a redirect URI was explicitly provided when the initial authorization request was made, that same redirect URI must be present when exchanging the authorization code for an access token. | The token exchange request will respond with a JSON payload that represents an access token and looks like the following: ```json JSON theme={null} { "access_token": "oa2_925f6e2f64f89bf4075bb8172c9a471f7f8b4e331bb7bf6d9c94a8029382cf45", "token_type": "bearer", "expires_in": 3600, "refresh_token": "de4800996214d5b40188bc2f9ae6c7227155f1e11b90b4f4f1dcd01afc88fcc6", "scope": "activity:write customer:read", "smile_account_id": 1 } ``` | Key | Description | | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `access_token` | An API access token that can be used to access the account's data as long as the app is installed. The token should be stored securely and used to make authenticated requests to Smile's REST API. | | `token_type` | The type of authentication token. This will always be bearer. | | `expires_in` | The number of seconds until the access token expires. | | `refresh_token` | A refresh token that can be used to get a fresh access token when the current one expires. | | `scope` | A space-separated string containing the access scopes that the access token was granted. This describes which resources the access token is authorized for. | | `smile_account_id` | The Smile account ID for the user that authorized the client. | *** ## Step 4: Using the access token The access token received during the previous step allows the app to make authorized requests to Smile on behalf of the specified account. It will never return data for any other Smile accounts. In order to make an authorized request to Smile's [REST API](/api/introduction), include the access token in the `Authorization` header of a given request. Note that when using the access token, the authorization type is "Bearer". This corresponds to the `token_type` from the response in the previous step. For example, to retrieve a list of the account's customers, the following request could be made: ```shell cURL theme={null} curl https://api.smile.io/v1/customers \ -H "Authorization:Bearer oa2_925f6e2f64f89bf4075bb8172c9a471f7f8b4e331bb7bf6d9c94a8029382cf45" ``` *** ## Step 5: Refreshing an access token If a request is made with an expired access token, it will return a `401 Unauthorized` response. When this happens, the app can use it's refresh token to obtain a new access token. Access tokens always have an expiration. We suggest the following pattern for determining when you should refresh your access token: * Upon receiving the access token, store the time at which it will expire (represented by the `expires_in` attribute). * Before making a request, if the time the token is expected to expire at is within five minutes of the current time, refresh the token. When refreshing an access token, the request must be made using [Basic HTTP Authentication](https://tools.ietf.org/html/rfc2617#section-2) with the Client ID as the user-ID and the Client Secret as the password. The Client ID and Client Secret can be found in the app's settings in the Partner Portal. The request will look like the following example and should also include the relevant parameters in the body: ```text theme={null} POST https://{client_id}:{client_secret}@connect.smile.io/oauth2/token?grant_type={grant_type}&refresh_token={refresh_token} ``` | Parameter | Description | | --------------------------------------------- | --------------------------------------------------------------------- | | `grant_type`
required | When refreshing an access token, this value should be refresh\_token. | | `refresh_token`
required | The refresh token provided when the access token was first generated. | The token refresh request will respond with a JSON payload representing an access token. It's format and keys will be identical to Step 3. # Client secret rotation Source: https://dev.smile.io/guides/apps/auth/oauth-rotation Rotate your app's client secret with zero downtime if it's ever compromised or exposed. In the event that your app's client secret has been compromised, you should immediately rotate it. Smile makes it possible for your app to rotate it's client secret with zero downtime. ## Step 1: Generate a new client secret Generating a new client secret will allow you to start using it for authentication in the OAuth flow. 1. In the [**Partner Portal**](https://partners.smile.io/), go to [**Apps**](https://partners.smile.io/apps). 2. Click on the name of the app you want to rotate the client secret for. 3. Navigate to the **API access** page for your app. 4. In the **Credentials** card, click **Generate new client secret**. 5. Read the information presented and click **Generate**. You should now see two fields: **Client secret (old)** and **Client secret (new)**. ## Step 2: Use the new client secret for OAuth After generating the new secret, both the old and new client secrets can be used to acquire access tokens. Update your app to use the new client secret. ## Step 3: Use the new client secret to verify webhooks Webhooks are always signed with the primary client secret. Once the old secret is revoked, the new secret will automatically be promoted to primary status. Configure your application to accept webhooks signed with either the old or the new client secret. ## Step 4: Revoke the old client secret Click **Revoke** next to the old client secret. This will remove it and promote the new secret to be your primary client secret. Any access tokens that were generated using the old revoked client secret will still be valid until they expire. ## Step 5: Stop verifying webhooks with the old client secret You can now cleanup your code base to stop verifying webhooks with the old client secret. # App authorization Source: https://dev.smile.io/guides/apps/auth/overview Learn how apps use OAuth to get authorized and make API calls on behalf of a merchant's Smile account. Apps use an OAuth flow when being installed on a merchant's account. The OAuth flow results in the generation of an access token, which can then be used to make ongoing calls to Smile's [REST API](/api/introduction) on behalf of the authorized account. Read on to learn more about OAuth and how to make it work for your app. Learn how to connect and authorize your app with Smile and get credentials to make API calls on behalf of merchants. Learn errors work during the OAuth process and get clarity on what each error code means and what to do when it happens. Learn about the permission model that dictates what resources an app has access to and what actions an app can perform. Learn how to rotate your app's client secret with zero downtime in the event that it's compromised or publicly exposed. # Build an app Source: https://dev.smile.io/guides/apps/build Learn how to build an app and get it launched. Building an app is the best way to integrate your product or service with Smile in a repeatable way. OAuth authorization makes the installation flow seamless for merchants, the [REST API](/api/introduction) gives you access to the full power of Smile's platform, and [webhooks](/guides/apps/webhooks) allow you to receive real-time updates when customer data changes. Building an app is currently only available by invitation. To inquire, visit [smile.io/partners](https://smile.io/partners). ## Process for building an app After being invited to build an app, login to the [Partner Portal](https://partners.smile.io/) to create a new app and manage it's settings. Allow merchants to install your app by connecting with Smile's [OAuth flow](/guides/apps/auth/oauth-flow) . Follow our tutorials for building out the features and functionality of your app using [activities](/guides/apps/activities), [webhooks](/guides/apps/webhooks), and the [API Reference](/api/introduction). Once your app is built, ensure it meets all [app requirements](/guides/apps/submission/requirements) and then submit your app via the Partner Portal. # Integration methods Source: https://dev.smile.io/guides/apps/integrate Learn about the different ways to integrate with Smile. There are two main ways to integrate with Smile: by building an app, or using merchant credentials. The right approach depends on your specific use case and requirements. Read on to learn more about each approach and determine which one is right for you. ## Option 1: Build an app [Building an app](/guides/apps/build) is the recommended approach for cases where the integration is designed to be re-used across multiple accounts. Apps are listed within Smile Admin and installable by any merchant. If you run a SaaS product and want to allow merchants to connect their Smile accounts to your platform, you should build an app. Right now, Smile's app architecture is well-suited for apps that: 1. Issue rewards for actions that occur in external systems (like leaving a review) 2. Surface rewards information in external systems (like a help desk or ESP platform) 3. Display loyalty information to customers (like their points balance or rewards) 4. Create loyalty accounts for customers Apps use OAuth for [authorization](/guides/apps/auth/overview), have access to the [REST API](/api/introduction), and can subscribe to [webhooks](/guides/apps/webhooks). ## Option 2: Use merchant credentials Using merchant credentials is recommended when creating an integration that is specific to a single merchant and not designed to be re-used across multiple accounts. Merchant credentials provide access to the [REST API](/api/introduction) and [JavaScript SDK](/js/introduction), but cannot subscribe to webhooks. # Use merchant credentials Source: https://dev.smile.io/guides/apps/merchant-credentials Learn how to use merchant credentials to integrate with Smile. When you're creating an integration or writing code for a single merchant, the best approach is to directly use the merchant's credentials (instead of building an app). Merchant credentials provide access to the [REST API](/api/introduction) and [JavaScript SDK](/js/introduction), but cannot subscribe to webhooks. Merchant credentials are only available on the Plus or Enterprise plan. ## Using the REST API To use the REST API with a merchant's credentials, you'll need to ask them to [create an API key](https://help.smile.io/en/articles/11328240-manage-api-keys#h_d8e0b5ed35) with the scopes that your integration requires (based on which API endpoints you want to call). Once you have an API key, you can use it to make requests to the REST API on behalf of the merchant. Refer to the [API Reference](/api/introduction) for a full list of available endpoints. If your integration is designed to be re-used across multiple accounts, you should [build an app](/guides/apps/build) instead. Legacy integrations used across multiple accounts can be converted to apps by [migrating to OAuth](/guides/apps/migrate-to-oauth). ## Using the JavaScript SDK Using the JavaScript SDK on a merchant's standard Shopify or BigCommerce storefront is simple, because the SDK is already automatically included and initialized. This means you can directly use the SDK's methods and functionality, without any additional setup or asking the merchant for their credentials. To use the JavaScript SDK on a custom storefront or other web or JavaScript surface, you'll need to ask the merchant for their [publishable key](https://help.smile.io/en/articles/11878403-find-publishable-key), and they'll also need to [create a signing key](https://help.smile.io/en/articles/11878129-manage-signing-keys#h_82be7ec35b). You can then use these two values to [include](/js/including) and [initialize](/js/initializing/initialize-method) the JavaScript SDK. # Migrate from API keys to OAuth Source: https://dev.smile.io/guides/apps/migrate-to-oauth Migrate your legacy integration from using merchant credentials (API keys) to using modern OAuth instead. This guide is for partners who currently integrate with Smile using merchant-created API keys and want to migrate to an [OAuth app](/guides/apps/build) instead. It walks through what changes, how to migrate your code, and how to move your existing merchants over. ## What changes For your code, surprisingly little β€” and the experience for merchants gets much better. You still call the same Smile [REST API](/api/introduction): the same endpoints, the same requests, and the same responses. What changes is how merchants connect your app up front, and the `Authorization` header you send with each API call. At a glance, here's why OAuth is better for developers and merchants alike: | | πŸ”‘ API keys | πŸ” OAuth | | ------------------------------------ | :------------------: | :---------------: | | Smile plan required | Plus or Enterprise | Any paid plan | | Setup experience | Manual copy & paste | One-click connect | | Access scope selection | Manual (error prone) | Automatic | | Adding access scopes | Requires a new key | Added anytime | | Zero-downtime token rotation | ❌ | βœ… | | Loyalty account creation via the API | ❌ | βœ… | | Webhook access | ❌ | βœ… | | In-app integration listing | ❌ | βœ… | | Partner Portal access | ❌ | βœ… | ## Before you start This guide assumes you have an app in the [Partner Portal](https://partners.smile.io/) to configure during the migration. If you don't have one yet, refer to our guide on [building an app](/guides/apps/build) to get started. Before writing any code, it helps to take stock of your current integration: * **Create a list** of all the REST API endpoints your integration uses. For each one, take note of the required access scope, since you'll need this to configure your OAuth app. The required scope for each endpoint is listed in the [API Reference](/api/introduction). ## How to migrate ### 1. Get familiar with OAuth OAuth apps use an OAuth 2.0 flow for granting your integration access to a Smile account. If you're not already familiar with OAuth, we recommend reading our [auth guide](/guides/apps/auth/overview) to get a sense for how this works before beginning the migration. ### 2. Configure your app In the [Partner Portal](https://partners.smile.io/), open your app and configure its settings: * Add your **Install URL** and one or more **Whitelisted Redirect URLs**. * Confirm the **Access scopes** your app needs (based on the API endpoints you're using). * Copy your **Client ID** and **Client secret** β€” you'll need these for the OAuth flow. Treat your Client secret like a password and store it only in secured backend code. If it's ever exposed, you can [rotate it with zero downtime](/guides/apps/auth/oauth-rotation). ### 3. Add a Connect Smile button Swap the part of your merchant setup flow that asks for an API key with a **Connect Smile** button. When a merchant clicks it, [redirect them to Smile's authorization page](/guides/apps/auth/oauth-flow#step-1-request-authorization), passing your Client ID and a `state` value you can verify later. After they approve the connection, Smile redirects them back to your `redirect_uri` with a temporary authorization `code`. Confirm the `state` matches the one you sent, then continue to the token exchange. ### 4. Exchange for an access token [Exchange the authorization code](/guides/apps/auth/oauth-flow#step-3-get-an-access-token) for an access token via a server-to-server request authenticated by your Client ID and Client secret. The response includes an access token, a refresh token, an `expires_in` lifetime, and the `smile_account_id`. For each merchant, securely store the **access token**, **refresh token**, **token expiry** (derived from `expires_in`), and **`smile_account_id`**. The `smile_account_id` identifies which Smile account the tokens belong to β€” store it alongside the merchant record that previously held their API key. ### 5. Update API call auth Wherever your integration previously sent the merchant's API key, send their OAuth access token instead. The request is otherwise unchanged: ```shell cURL theme={null} theme={null} curl https://api.smile.io/v1/customers \ -H "Authorization: Bearer api_cnzGMghxTmPzK1sp" # [!code --] -H "Authorization: Bearer oa2_925f6e2f64f89bf4075bb8172c9a471f7f8b4e331bb7bf6d9c94a8029382cf45" # [!code ++] ``` ### 6. Refresh access tokens Unlike API keys, OAuth access tokens expire β€” a request made with an expired token returns `401 Unauthorized`. Store each access token's expiry and, before it lapses, use the stored refresh token to [request a new access token](/guides/apps/auth/oauth-flow#step-5-refreshing-an-access-token). ### 7. Use activities for actions This step only applies if you currently use the [create a points transaction](/api/resources/points-transactions/create-points-transaction) endpoint. Smile offers two ways to issue points to customers, and the right approach depends on *why* you're issuing points: * **Points transactions** directly add or remove points from a customer's balance, like a ledger entry. Use these for adjustments that aren't tied to a customer action β€” manual corrections, goodwill credits, or porting over an existing balance from a legacy system. * **Activities** tell Smile that a customer *performed a rewardable action* β€” like leaving a review or subscribing to a newsletter. Smile evaluates activities against the merchant's configured ways to earn and issues the correct number of points automatically, without you needing to do any math or additional validation. Points transactions aren't going away. If you only ever adjusted balances directly and never issued points for a customer action, no changes are required. If your integration used points transactions to **reward customer actions**, switch to using activities instead. Activities have a few benefits for merchants and customers: 1. Merchants configure how many points customers will receive in Smile Admin, centralizing where loyalty program configuration is managed. 2. Points issued via activities are correctly recorded and displayed within the merchant's loyalty program analytics β€” making it easier for them to understand how customers are earning points. 3. Customers will see how many points they'll earn for performing an activity across all loyalty surfaces (like the rewards widget and Smile's Loyalty Hub). Activities issue a fixed number of points per action, set by the merchant's ways to earn. If you need to award a variable amount based on a property of the action β€” like points per dollar on a scanned receipt β€” keep using a points transaction and calculate the amount yourself. To migrate from using points transactions to activities: Locate each place your integration calls [`POST /points_transactions`](/api/resources/points-transactions/create-points-transaction). For each one, decide whether it rewards a customer action or just adjusts a balance β€” only action-based calls need to change. For each customer action, [define a new activity type](/guides/apps/activities#defining-a-new-activity-type) for your app in the [Partner Portal](https://partners.smile.io/apps). This is the action merchants build their ways to earn around. Replace the points transaction call with a [create activity](/api/resources/activities/create-activity) call, identifying the customer by Smile Customer ID or email. You no longer send a points amount β€” the merchant's ways to earn determine the reward, and Smile issues it automatically once the activity is evaluated. Creating an activity requires the `activity:write` scope, so confirm it's included in your app's access scopes (from step 2). ### 8. Test on a dev account Once you've finished building, the next step is to verify the end-to-end flow on a development Smile account: * A merchant can connect your app through OAuth and land back in your app connected. * Your app exchanges the authorization code for an access token and stores it. * Your app refreshes an access token successfully. * Every Smile API endpoint your integration uses works with the granted scopes. * If applicable, activity behavior works end to end. Only development accounts can install unreviewed apps. If you need additional Smile accounts for testing, reach out to your partner manager. ### 9. Submit for review Once your app is built and tested, make sure it meets the [app requirements](/guides/apps/submission/requirements) and submit it for review through the Partner Portal. Refer to our [submission and review guide](/guides/apps/submission/process) for a full walkthrough of the process. ## Migrate existing merchants Merchants who already connected your integration with an API key **can't be automatically converted to OAuth** β€” each one needs to re-connect your app using the OAuth flow. To move an existing merchant over: 1. Prompt the merchant to connect your app via the **Connect Smile** flow. 2. Once they've connected and you've stored their tokens, switch to using the OAuth access token for that merchant. 3. Stop using their stored API key, and give them guidance on removing the old key from Smile if they'd like to. Because the new OAuth flow can run alongside your existing API key integration, you can migrate merchants gradually rather than all at once. ### Communicate the benefits When you prompt a merchant to reconnect, it helps to explain what they get from switching. Lead with the benefits that apply to everyone, then add the ones specific to your integration: * **More secure** β€” OAuth uses scoped access tokens with [zero-downtime token rotation](/guides/apps/auth/oauth-rotation) instead of long-lived API keys, keeping the merchant's data safe. * **New features automatically** β€” with OAuth, your integration can roll out new features without needing to ask merchants to regenerate and re-copy API keys, so they always get the latest functionality! * **Better analytics** (if you move to activities) β€” points issued via activities are recorded in the merchant's loyalty program analytics and shown across all loyalty surfaces, like the rewards widget and Smile's Loyalty Hub. * **Faster updates** (if you adopt webhooks) β€” your app can [subscribe to webhooks](/guides/apps/webhooks) instead of polling, so customer updates sync in near real time. ## Migration checklist Use this to track your migration: * [ ] I can explain what my current API key integration does. * [ ] I know which Smile API endpoints my integration calls. * [ ] I confirmed which OAuth access scopes my app needs. * [ ] I configured my app's Install URL, Whitelisted Redirect URLs, and access scopes in the Partner Portal. * [ ] I added a **Connect Smile** button to my app. * [ ] A merchant can approve my app in Smile and return to my app connected. * [ ] My app exchanges the authorization code for an access token and stores it securely. * [ ] My app calls the Smile API with the OAuth access token. * [ ] My app refreshes the access token when it expires. * [ ] I moved any action-based points transactions to activities (if applicable). * [ ] My existing merchants know they need to reconnect once. * [ ] After a merchant reconnects, my app stops using their old API key. * [ ] I tested the full flow on a development Smile account. ## Future considerations You don't need these to migrate, but they're worth revisiting once your OAuth app is live. ### Replace polling with webhooks OAuth apps can [subscribe to webhooks](/guides/apps/webhooks), so if your integration is currently polling for customer updates, this behavior can be replaced with a webhook subscription. There's no need to rewrite your syncing logic as part of the migration though β€” you can adopt webhooks whenever it's convenient. ## Where to get help If you have questions about the migration or your app setup, reach out to your partner manager or our [support team](https://help.smile.io/en/articles/4495476-how-to-get-help-with-smile). For a deeper look at the moving parts, see the [OAuth flow](/guides/apps/auth/oauth-flow), [access scopes](/guides/apps/auth/access-scopes), and [building an app](/guides/apps/build) guides. ## FAQ No. The endpoints, request formats, and responses are the same. The only change is the credential in the `Authorization` header β€” an OAuth access token (`oa2_...`) instead of an API key (`api_...`). Yes. The OAuth install flow can run alongside your existing API key setup, so you can build and test OAuth, then move merchants over gradually rather than all at once. That said, we don't recommend allowing both indefinitely as it will lead to difficulty with debugging and maintenance over time. Nothing automatic. Once a merchant has connected via OAuth and you've stored their access token and refresh token, you should advise the merchant to [delete the old API key](https://help.smile.io/en/articles/11328240-manage-api-keys#delete-an-api-key) in Smile β€” but until they do so, their API key will continue to function as normal. Request the scopes that match what your API keys do today. Smile uses the same `read`/`write` scope model for both, and the required scope for each endpoint is listed in the [API Reference](/api/introduction). Refer to our [access scopes](/guides/apps/auth/access-scopes) guide for additional information on how this model works. # App submission process Source: https://dev.smile.io/guides/apps/submission/process Learn how to submit your app for review and get it listed in Smile Admin. Once you've finished developing your app, the next step is to submit it for review. Before submitting, you should ensure: * All [app requirements](/guides/apps/submission/requirements) are being met. * You've manually tested installing the app and all app functionality. * You've completed all fields for your app listing within the [Partner Portal](https://partners.smile.io). * You have a test/developer account you can make available to Smile in order to perform an app review. When you're ready, follow the instructions within the Partner Portal to submit your app for review. Once submitted, our team will provide you with an estimated review timeline, along with any feedback or required changes. Multiple rounds of review may be needed in some cases to ensure all requirements are being met. After review is completed, your app will be listed within Smile Admin and made available for installation by merchants. # App requirements Source: https://dev.smile.io/guides/apps/submission/requirements Review the requirements your app must meet to be listed and available for installation by merchants. For an app to be listed and available for installation by merchants, it must meet the following set of requirements. These ensure a consistent, secure, and high-quality experience for merchants across the entire app lifecycle β€” from installation to daily use. These requirements are subject to change as we continue to evolve the Smile app ecosystem and developer platform. Apps must meet any new requirements to remain listed and available to merchants. ## Installation and setup Apps must provide a reliable, intuitive installation experience across all supported entry points. * The app must be installable from both Smile and the partner's platform (e.g. initiated from Smile embedded in Shopify, or initiated from the partner integration). * If a merchant denies required permissions during installation, the app must display a clear, actionable error message. * All OAuth installation and redirect URLs must be valid HTTPS links. ## Functionality and quality Apps must provide merchants with a functional, secure, and rewarding experience that meets Smile's quality standards. * The app must use [activities](/guides/apps/activities) to reward customers for completing rewardable actions (e.g. leaving a review). Apps must not issue points transactions directly unless the desired experience is not achievable using activities. * OAuth access scopes must correspond directly to the functionality the app provides β€” do not request access scopes that are not required. * Webhooks must only be configured if they directly support functionality provided by the app. * Webhook request URLs must be valid HTTPS endpoints. ## App listing App listings are the first impression merchants have of your integration. They should be professional, accurate, and consistent with Smile's brand and tone. **App icon** * The app icon must be square and between **200x200** and **1000x1000** pixels. * The icon must not have a transparent background. **App name and website** * The app name must be accurate, properly capitalized, and reflective of the integration. * The app website must be a valid HTTPS URL that directs to the app's main landing page. **Overview and features** * The app overview must be concise β€” no more than two sentences β€” and should end with a period. * Listed features must describe functionality the integration provides within Smile (not just general features of the third-party platform). * Feature descriptions should not end with punctuation. * The documentation link must go directly to a relevant article explaining how to use the integration, not a generic help center homepage. * Documentation must clearly describe how merchants can connect and use the app's core features. ## Activities Apps that create Smile activities (e.g., "made a purchase," "wrote a product review") must use clear, merchant-friendly naming conventions. * Each activity's "Name after completed" should be written in active voice and past tense to reflect the customer's action. * Activity names should be written in sentence case (capitalize only the first letter). **For example:** * βœ… Good: Made a purchase // ❌ Bad: Purchase made * βœ… Good: Wrote a product review // ❌ Bad: Review submitted * βœ… Good: Referred a friend // ❌ Bad: Friend referred # Webhooks Source: https://dev.smile.io/guides/apps/webhooks Apps can subscribe to webhook topics to receive notifications when events occur within Smile. Webhooks are currently only available to apps and cannot be configured by merchants for their own accounts. ## Configure webhook sending The first step to receiving webhooks is configuring your webhook URL. Once setup, you will then be able to specify the topics to which you'd like to subscribe. 1. In the [**Partner Portal**](https://partners.smile.io), go to [**Apps**](https://partners.smile.io/apps). 2. Click on the name of the app you want to define a new activity type for. 3. Navigate to the **Webhooks** page for your app. 4. Click **+ Create webhook**. 5. Enter the webhook details and click **Create**. ## Webhook topics Webhook topics allow an app to subscribe to specific events that occur in Smile. Below is a list of webhook topics that an app can subscribe to. | Topic | Description | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ping` | This topic is used during webhook creation to verify that your webhook endpoint is functioning properly. The data portion of this webhook is an empty object. | | `customer/updated` | Occurs when a customer is updated (including when a customer is first created). | ## Webhook bodies Webhook bodies (payloads) are in JSON format and have a consistent structure across all topics. They contain the following parameters. | Parameter | Description | | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------- | | `account_id` | The ID of the Smile account on which the event occurred. | | `data` | The specific data pertaining to the webhook topic. For example, the data in a `customer/updated` webhook will contain a customer object. | | `topic` | The webhook topic. Topics contain two components: a subject and a verb separated by a slash (e.g. `customer/updated`). | Here is an example webhook body. ```json JSON theme={null} { "account_id": 0, "data": {}, "topic": "ping" } ``` ### Customer webhooks For webhook topics with a "customer" subject (e.g. `customer/updated`), the data attribute in the webhook body will contain the following parameters. | Parameter | Description | | ---------- | ------------------------------------------------------------------------------------------------------------------- | | `customer` | An object representing the customer that was updated. See the customer object for the attributes that are included. | ```json JSON theme={null} { "data": { "customer": {} } } ``` ## Verifying webhook signatures In order to verify that a webhook contains legitimate data, Smile signs the contents of each webhook with a `sha256` [HMAC](https://en.wikipedia.org/wiki/HMAC). Webhooks are signed using the app's client secret (found in the app's settings in the Partner Portal), and the following headers must be used to verify the signature of a webhook. | Header | Description | | ----------------- | ----------------------------------------------------------------------------------------------------------------------------- | | `Smile-Signature` | The HMAC generated by Smile. An app will attempt to compute this signature in order to verify that the webhook is legitimate. | | `Smile-Timestamp` | The number of seconds since the Unix epoch (UTC). This value is used to prevent replay attacks. | To verify a webhook's signature, you must perform the following steps. 1. Concatenate the `Smile-Timestamp`, followed by a period (`.`), followed by the webhook request body. 2. Generate a `sha256` HMAC using the computed string and the app's client secret. 3. Compare the computed HMAC to the value of the `Smile-Signature` header. 4. Verify that the timestamp is within 5 minutes of the current time. In code, verifying a webhook signature looks like: ```ruby Ruby lines theme={null} require 'rubygems' require 'base64' require 'openssl' require 'sinatra' def verify_webhook_signature(body, timestamp, signature) client_secret = ENV["SMILE_CLIENT_SECRET"] prepared_string = "#{timestamp}.#{body}" # Compute the hmac using the client secret. digest = OpenSSL::Digest.new("sha256") hmac = OpenSSL::HMAC.digest(digest, client_secret, prepared_string) encoded_hmac = Base64.encode64(hmac).strip # Verify that the webhook has been sent within the last 5 minutes. timestamp_plus_five_minutes = timestamp.to_i + 5 * 60 webhook_is_fresh = timestamp_plus_five_minutes > Time.now.to_i # Check that the signature matches and the webhook is fresh. (encoded_hmac == signature) && webhook_is_fresh end post "/" do body = request.body.read timestamp = request.env['HTTP_SMILE_TIMESTAMP'] signature = request.env['HTTP_SMILE_SIGNATURE'] # Verify the webhook signature. verified = verify_webhook_signature(body, timestamp, signature) puts("Webhook verified: #{verified}") status 200 end ``` The above code snippet is functional, however in order to prevent timing attacks, a constant time string comparison function should be used when comparing signatures. ## Responses & retries A webhook will be considered successfully delivered if the endpoint responds with a `2xx` HTTP status code within 5 seconds. During periods of high traffic (such as sale seasons or other shopping-focused times of year), apps may receive a high volume of webhooks, so asynchronous webhook processing is highly recommended. If a response is not received within the allowed time period or a non-200 status code is observed, the webhook send will be considered to have failed. If a webhook fails to send for any reason, Smile will try to send the webhook again later using an exponential backoff algorithm. If the webhook cannot be sent successfully within a 24 hour period, the webhook will be disabled across all accounts with the app installed. ## Permissions Every webhook topic has corresponding OAuth permissions which determine if a webhook can be sent for a given account. For example, in order to receive `customer/updated` webhooks, the app must be granted the `customer:read` permission. It is important to understand that app permissions are granted on a per-account basis. This means that an app is not guaranteed to have the same scope on all of the Smile accounts that it is installed on. As a result, an app will only receive webhooks for the subset of Smile accounts that have granted it the required permissions. Once a webhook is created via the Partner Portal and at least one user has authorized the app with one of the required permissions, the app will start to receive webhooks. # Example usage Source: https://dev.smile.io/guides/deprecated/custom-html-emails/custom-email-examples Example custom email templates that show how to use handlebar variables to create dynamic content. This section provides a few examples that will help you get started developing your own custom notification templates. These examples are meant to get you familiar with handlebar variables and are by no means exhaustive. ## Points earned example The points earned notification provides both a `customer` and a `reward_fulfillment` object. Here's an example of how you can use these variables to create dynamic content in your emails. ```html HTML theme={null} Hey {{customer.first_name}}, You just earned {{reward_fulfillment.name}}! {{{footer}}} ``` Smile will replace the handlebar variables with the values they represent, ultimately yielding an email that looks like this: ```html HTML theme={null} Hey John, You just earned 100 Points! ``` ## VIP tier achieved example The VIP tier achieved notification provides a customer object that contains all the relevant VIP information you need. ```html HTML theme={null} Hey {{customer.first_name}}, You just achieved {{customer.vip_tier.name}}! {{#if customer.next_vip_tier.name}} Keep going to hit {{customer.next_vip_tier.name}}. {{/if}} {{{footer}}} ``` Smile will replace the handlebar variables with the values they represent, ultimately yielding an email that looks like this: ```html HTML theme={null} Hey John, You just achieved Silver! Keep going to hit Gold. ``` # Object reference Source: https://dev.smile.io/guides/deprecated/custom-html-emails/custom-email-objects Reference for the objects available in Smile's custom HTML email notification templates. Below you will find a structure description of the common objects used in templates throughout Smile's email notification system. The structure of these objects may differ slightly from their equivalent in the [API reference](/api/introduction). ## Customer object The customer object contains basic information (e.g. first name), loyalty program information (e.g. points balance), and if applicable, VIP program information (e.g. current VIP tier). | Attribute | Description | | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | `email` | The customer's email address. | | `first_name` | The customer's first name. | | `next_vip_tier` | A VIP Tier object. The next VIP tier the customer will achieve after meeting required criteria, if any. | | `points_balance` | The number of points the customer currently has. (e.g. 250) | | `points_balance_formatted` | The number of points and the points currency. (e.g. 250 Points) | | `referral_url` | The customer's unique referral URL. | | `vip_milestone_description` | An explanation of what the customer needs to accomplish to move into the next VIP tier. (e.g. Earn 250 Points by Dec 31, 2020 to reach Gold.) | | `vip_tier` | A VIP Tier object. The customer's current VIP tier, if any. | ## Reward object The reward object contains basic details about a reward. | Attribute | Description | | --------- | ----------------------- | | `name` | The name of the reward. | ## Reward fulfillment object The reward fulfillment object contains information about the reward a customer has just unlocked. | Attribute | Description | | ---------------------- | ---------------------------------------------------------------------------------- | | `action_text` | The action text associated with using a reward, if any. (e.g. Use your reward) | | `action_url` | The action URL associated with using a reward, if any. | | `code` | The code required for the customer to use this reward, if any. | | `expires_at_formatted` | The human readable date for when a reward will expire, if any. (e.g. Dec 31, 2020) | | `name` | The name of the reward given to the customer. (e.g. \$10 Off Coupon) | | `source_description` | What the customer did to achieve this reward. (e.g. Spent 100 Points) | | `usage_instructions` | Instructions that describe how to use the reward, if any. | | `terms_and_conditions` | The terms and conditions associated with the reward fulfillment. | ## VIP tier object The VIP tier object contains information about a customers current or future VIP tier status. | Attribute | Description | | --------- | ------------------------------------- | | `id` | The ID of the VIP tier. | | `name` | The name of the VIP tier. (e.g. Gold) | # Handlebar variables Source: https://dev.smile.io/guides/deprecated/custom-html-emails/custom-email-variables Reference for the handlebar variables available in each of Smile's email notification types. Smile provides several different notification types that get triggered by unique events from your loyalty program. Each notification type has a set of handlebar variables that can be leveraged to create rich, dynamic emails. This document outlines the handlebar variables that can be used in each notification type. ## Points earned | Variable | Type | Description | | -------------------- | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `customer` | [Customer object](/guides/deprecated/custom-html-emails/custom-email-objects#customer-object) | Customer information. | | `footer` | HTML | Full-width block of HTML containing stylized text and links required by anti-spam legislation. This variable contains HTML so it needs three curly braces instead of two (i.e. `{{{footer}}}`). | | `reward_fulfillment` | [Reward fulfillment object](/guides/deprecated/custom-html-emails/custom-email-objects#reward-fulfillment-object) | Completed action and points received information. | ## Reward redeemed | Variable | Type | Description | | -------------------- | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `customer` | [Customer object](/guides/deprecated/custom-html-emails/custom-email-objects#customer-object) | Customer information. | | `footer` | HTML | Full-width block of HTML containing stylized text and links required by anti-spam legislation. This variable contains HTML so it needs three curly braces instead of two (i.e. `{{{footer}}}`). | | `reward_fulfillment` | [Reward fulfillment object](/guides/deprecated/custom-html-emails/custom-email-objects#reward-fulfillment-object) | New reward information. | ## Birthday reward | Variable | Type | Description | | -------------------- | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `customer` | [Customer object](/guides/deprecated/custom-html-emails/custom-email-objects#customer-object) | Customer information. | | `footer` | HTML | Full-width block of HTML containing stylized text and links required by anti-spam legislation. This variable contains HTML so it needs three curly braces instead of two (i.e. `{{{footer}}}`). | | `reward_fulfillment` | [Reward fulfillment object](/guides/deprecated/custom-html-emails/custom-email-objects#reward-fulfillment-object) | Birthday reward information. | ## Points expiry - warning | Variable | Type | Description | | ------------------------------ | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `customer` | [Customer object](/guides/deprecated/custom-html-emails/custom-email-objects#customer-object) | Customer information. | | `footer` | HTML | Full-width block of HTML containing stylized text and links required by anti-spam legislation. This variable contains HTML so it needs three curly braces instead of two (i.e. `{{{footer}}}`). | | `points_expiry_date_formatted` | String | A human readable date for when points will be expiring. (e.g. Dec 31, 2020) | ## Points expiry - last chance | Variable | Type | Description | | ------------------------------ | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `customer` | [Customer object](/guides/deprecated/custom-html-emails/custom-email-objects#customer-object) | Customer information. | | `footer` | HTML | Full-width block of HTML containing stylized text and links required by anti-spam legislation. This variable contains HTML so it needs three curly braces instead of two (i.e. `{{{footer}}}`). | | `points_expiry_date_formatted` | String | A human readable date for when points will be expiring. (e.g. Dec 31, 2020) | ## Referral shared through Smile | Variable | Type | Description | | ------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `advocate_customer` | [Customer object](/guides/deprecated/custom-html-emails/custom-email-objects#customer-object) | Referral advocate information. | | `footer` | HTML | Full-width block of HTML containing stylized text and links required by anti-spam legislation. This variable contains HTML so it needs three curly braces instead of two (i.e. `{{{footer}}}`). | | `friend_reward` | [Reward object](/guides/deprecated/custom-html-emails/custom-email-objects#reward-object) | Information on the reward available to the friend. | | `message` | String | A personalized message from the referral advocate to the friend. (e.g. Check this store out!) | ## Friend received referral | Variable | Type | Description | | -------------------- | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `advocate_customer` | [Customer object](/guides/deprecated/custom-html-emails/custom-email-objects#customer-object) | Referral advocate information. | | `footer` | HTML | Full-width block of HTML containing stylized text and links required by anti-spam legislation. This variable contains HTML so it needs three curly braces instead of two (i.e. `{{{footer}}}`). | | `reward_fulfillment` | [Reward fulfillment object](/guides/deprecated/custom-html-emails/custom-email-objects#reward-fulfillment-object) | Information on the reward given to the friend. | ## Referral completed | Variable | Type | Description | | -------------------- | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `customer` | [Customer object](/guides/deprecated/custom-html-emails/custom-email-objects#customer-object) | Advocate customer information. | | `friend_customer` | [Customer object](/guides/deprecated/custom-html-emails/custom-email-objects#customer-object) | Referred friend information. | | `footer` | HTML | Full-width block of HTML containing stylized text and links required by anti-spam legislation. This variable contains HTML so it needs three curly braces instead of two (i.e. `{{{footer}}}`). | | `reward_fulfillment` | [Reward fulfillment object](/guides/deprecated/custom-html-emails/custom-email-objects#reward-fulfillment-object) | Information on the reward given to the referral advocate. | ## VIP tier achieved | Variable | Type | Description | | ---------- | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `customer` | [Customer object](/guides/deprecated/custom-html-emails/custom-email-objects#customer-object) | Customer information. | | `footer` | HTML | Full-width block of HTML containing stylized text and links required by anti-spam legislation. This variable contains HTML so it needs three curly braces instead of two (i.e. `{{{footer}}}`). | ## Reward expiry reminder | Variable | Type | Description | | -------------------- | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `customer` | [Customer object](/guides/deprecated/custom-html-emails/custom-email-objects#customer-object) | Customer information. | | `footer` | HTML | Full-width block of HTML containing stylized text and links required by anti-spam legislation. This variable contains HTML so it needs three curly braces instead of two (i.e. `{{{footer}}}`). | | `reward_fulfillment` | [Reward fulfillment object](/guides/deprecated/custom-html-emails/custom-email-objects#reward-fulfillment-object) | Expiring reward information. | # Overview Source: https://dev.smile.io/guides/deprecated/custom-html-emails/overview Build custom HTML email templates with handlebar variables. Deprecated and maintained as a reference only. Smile's email notification system allows you to build customized HTML email templates with the help of [handlebar](https://handlebarsjs.com/expressions.html) variables. You can personalize your emails to make them more valuable and ensure your customers continue to open them. Not familiar with handlebar variables? [Learn more about how they work](https://handlebarsjs.com/expressions.html). Custom HTML email functionality is deprecated and no longer generally available. This document is maintained as a reference only. For merchants that want to send fully custom loyalty program emails, we recommend using our Klaviyo integration. Refer to the list of available [handlebar variables](/guides/deprecated/custom-html-emails/custom-email-variables) and [objects](/guides/deprecated/custom-html-emails/custom-email-objects) as well as the [example emails](/guides/deprecated/custom-html-emails/custom-email-examples) to get started! # Customer deleted Source: https://dev.smile.io/guides/deprecated/custom-platform/custom-backend-int/custom-customer-events/custom-customer-deleted Send this event to Smile any time a customer is deleted in your ecommerce system. Send this event any time a customer is deleted in your system. This will remove the customer from Smile. The only required [customer property](/guides/deprecated/custom-platform/custom-backend-int/custom-customer-events/custom-customer-object) when sending this type of event is the `external_id` property. This endpoint requires the scope. ```shell Delete a customer theme={null} curl --request POST \ --url https://api.smile.io/v1/events/ \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer {token}' \ --data ' { "event": { "topic": "customer/deleted", "data": { "external_id": "1" } } }' ``` ```json 202 theme={null} ``` # Customer object Source: https://dev.smile.io/guides/deprecated/custom-platform/custom-backend-int/custom-customer-events/custom-customer-object Reference for the fields used to represent a customer when sending customer events to Smile. When sending customer events to Smile, the following fields are used to represent and describe the customer. The unique id of the customer in your database. This should never change for a given customer. The customer's first name. The customer's last name. The customer's email address. The date the customer what created in your system. The date the customer was last updated in your system. ```json Example theme={null} { "external_id": "1", "first_name": "Wayne", "last_name": "Rooney", "email": "wrooney@example.com", "external_created_at": "2023-11-19T21:19:30.559Z", "external_updated_at": "2023-11-20T21:09:22.509Z" } ``` # Customer updated Source: https://dev.smile.io/guides/deprecated/custom-platform/custom-backend-int/custom-customer-events/custom-customer-updated Send this event to Smile any time a customer is created or updated in your ecommerce system. Send this event any time a customer is created or updated in the external system. All [customer properties](/guides/deprecated/custom-platform/custom-backend-int/custom-customer-events/custom-customer-object) should be filled in when sending this type of request. This endpoint requires the scope. ```shell Update a customer theme={null} curl --request POST \ --url https://api.smile.io/v1/events/ \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer {token}' \ --data ' { "event": { "topic": "customer/updated", "data": { "external_id": "1", "first_name": "Wayne", "last_name": "Rooney", "email": "wrooney@example.com", "external_created_at": "2023-11-19T21:19:30.559Z", "external_updated_at": "2023-11-20T21:09:22.509Z" } } }' ``` ```json 202 theme={null} ``` # Overview Source: https://dev.smile.io/guides/deprecated/custom-platform/custom-backend-int/custom-customer-events/overview Learn how customer events keep Smile informed of changes to customer information in your ecommerce system. Customer events are how you let Smile know about changes to customer information within your ecommerce system. Customer events are a restricted part of our API only intended for use by merchants or developers building custom ecommerce platform integrations. If you are using Shopify or BigCommerce for ordering processing, this section of the documentation is not relevant to you. # Order deleted Source: https://dev.smile.io/guides/deprecated/custom-platform/custom-backend-int/custom-order-events/custom-order-deleted Send this event to Smile any time an order is deleted in your ecommerce system. Send this event any time an order is deleted in your system. This will remove the order from Smile and cancel any points the customer had been awarded for it. The only required [order property](/guides/deprecated/custom-platform/custom-backend-int/custom-order-events/custom-order-updated) when sending this type of event is the `external_id` property. If an order has been refunded and you want the associated points to be revoked, send a [customer updated event](/guides/deprecated/custom-platform/custom-backend-int/custom-customer-events/custom-customer-updated) instead. This endpoint requires the scope. ```shell Delete an order theme={null} curl --request POST \ --url https://api.smile.io/v1/events/ \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer {token}' \ --data ' { "event": { "topic": "order/deleted", "data": { "external_id": "100" } } }' ``` ```json 202 theme={null} ``` # Order object Source: https://dev.smile.io/guides/deprecated/custom-platform/custom-backend-int/custom-order-events/custom-order-object Reference for the fields used to represent an order when sending order events to Smile. When sending order events to Smile, the following fields are used to represent and describe the order. The unique id of the order in your system. This should never change for a given order. The order's subtotal (before tax, discounts, shipping, etc). The order's grand total (including tax, discounts, shipping, etc). The value of the order that should be rewarded with points. This should be less than or equal to the `grand_total`. If you have products that should not be rewarded, you can deduct them from the `rewardable_total` so they are not given points. If we receive a negative value for `rewardable_total` we will change it to 0 as we don't support a negative `rewardable_total`. The date the order was created in your system. The date the order was last updated in your system. The status of the order's payment, which determines whether rewards will be issued or revoked. Possible values: * `paid` - The order has been paid and should be rewarded. * `refunded` - The order has been refunded and any rewards should be revoked. * `null` - The order has yet to be paid and should not yet be rewarded. An array of coupons that were used on the order. Used for tracking referrals and loyalty program ROI metrics. Each coupon object in the array contains a `code` field (string) representing the coupon code. The customer that placed the order. If the order was placed by a guest, set this value to `null`. The customer object should contain the customer's information (see the [custom customer object](/guides/deprecated/custom-platform/custom-backend-int/custom-customer-events/custom-customer-object) for details). ```json Example theme={null} { "external_id": "100", "subtotal": 200, "grand_total": 230, "rewardable_total": 200, "external_created_at": "2023-11-19T21:19:30.559Z", "external_updated_at": "2023-12-03T11:17:35.159Z", "payment_status": "paid", "coupons": [ { "code": "5off-j2n5jduwjttj" } ], "customer": {} } ``` # Order updated Source: https://dev.smile.io/guides/deprecated/custom-platform/custom-backend-int/custom-order-events/custom-order-updated Send this event to Smile any time an order is created or updated in your ecommerce system. Send this event any time an order is created or updated. This would include when an order has been shipped, refunded, cancelled, etc. All [order properties](/guides/deprecated/custom-platform/custom-backend-int/custom-order-events/custom-order-object) should be filled when sending this type of event. When sending orders for guest customers, specify the `customer` as `null`. It's important that you send order events for both logged-in and guest orders, as Smile's system requires this data to accurately track referrals and other ROI and loyalty metrics. This endpoint requires the scope. ```shell Update an order theme={null} curl --request POST \ --url https://api.smile.io/v1/events/ \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer {token}' \ --data ' { "event": { "topic": "order/updated", "data": { "external_id": "100", "subtotal": 200, "grand_total": 230, "rewardable_total": 200, "external_created_at": "2015-11-19T21:19:30.559Z", "external_updated_at": "2015-12-03T11:17:35.159Z", "payment_status": "paid", "coupons": [ { "code": "5off-j2n5jduwjttj" }, { "code": "SUMMER_SHIPPING_16" } ], "customer": { "external_id": "100", "first_name": "Wayne", "last_name": "Rooney", "email": "wrooney@example.com", "external_created_at": "2015-11-19T21:19:30.559Z", "external_updated_at": "2015-11-19T21:19:30.559Z" } } } }' ``` ```json 202 theme={null} ``` # Overview Source: https://dev.smile.io/guides/deprecated/custom-platform/custom-backend-int/custom-order-events/overview Learn how order events keep Smile informed of order activity within your ecommerce system. Order events are how you let Smile know about order activity within your ecommerce system. Order events are a restricted part of our API only intended for use by merchants or developers building custom ecommerce platform integrations. If you are using Shopify or BigCommerce for ordering processing, this section of the documentation is not relevant to you. # Overview Source: https://dev.smile.io/guides/deprecated/custom-platform/custom-backend-int/overview Send customer and order events to Smile from your backend so Smile can issue rewards. In this section, you will use the Smile.io API to send customer and order events so that we can issue rewards. Heads up - send these requests in the background! Customer and Order events should be sent to Smile.io in the background so these API calls don't block any web requests. We recommend using a background job or queue to achieve this. In order to ensure successful delivery of the events to Smile.io, the background job should be built to retry the request in the event of network failure or failure to connect with Smile.io. Login to [Smile Admin](https://app.smile.io) and create a new API key under **Settings** > **Developer Tools**. The API key must have the `event:write` scope. The backend integration portion of the setup involves communicating with Smile's API. To do this, you should refer to our [API Reference](/api/introduction) to understand how authentication, errors, and rate limiting work From your backend server, send [customer events](/guides/deprecated/custom-platform/custom-backend-int/custom-customer-events) any time a customer is created, updated, or deleted. From your backend server, send [order events](/guides/deprecated/custom-platform/custom-backend-int/custom-order-events) any time an order is created, updated, or deleted. Once the backend setup is complete, you can proceed to [integrate Smile with your frontend](/guides/deprecated/custom-platform/custom-frontend-int/overview). # FAQ Source: https://dev.smile.io/guides/deprecated/custom-platform/custom-faq Answers to common questions about building a custom ecommerce platform integration with Smile. ## Do I need to code any points earning or spending logic? No, the Smile platform takes care of rewarding customers points for performing actions within your store. All you need to do is send Smile customer and order events as they happen and we'll take care of making sure customers get the correct number of points. ## Do I need to build any UI components? Smile.io provides a handful of front-end widgets that you can add to your store website. All points spending happens within a pop-up widget that we provide which can be customized by the merchant in our visual editor within Smile Admin. This means you don't need to code anything on the frontend beyond adding some Smile.io JavaScript to your website. ## Do I need to integrate with my checkout? Most rewards customers can spend their points on are in the form of coupon codes or gift card codes. Customers will spend their points through the Smile pop-up in exchange for a coupon code or gift card. Customers will apply this code on checkout which keeps things super simple. No custom cart or checkout integration needed on your end! # Points at checkout Source: https://dev.smile.io/guides/deprecated/custom-platform/custom-frontend-int/custom-points-at-checkout Add a points redemption dropdown to your custom platform checkout so customers can spend points seamlessly. Allowing customers to redeem points at checkout is a great way to promote points spending and boost engagement with your loyalty program. Adding a points dropdown to your checkout makes spending points quick, easy, and seamless. ## 1. Include our sample code in your checkout page Copy the contents of our [sweettooth-points-products-dropdown.html](https://github.com/smile-io/code-samples/blob/master/points-products-dropdown/sweettooth-points-products-dropdown.html) code sample and add it to the body of your checkout page. The code sample uses jQuery. If your site does not already include or use jQuery, you should uncomment the jQuery script at the top of the sample. ## 2. Add the dropdown to your checkout page Add the following div to your checkout page where you'd like the points dropdown to appear. ```html HTML theme={null}
``` ## 3. Customize the available rewards Sometimes there are rewards offered through your program that don't apply to the cart, such as Free Product rewards. You can hide specific points products from being shown in the dropdown menu by adding specific points product IDs to the `hidePointsProductIds` variable in the code sample. To find the ID of a points product you want to hide, visit [Smile Admin](https://app.smile.io) and navigate to your points program's rewards. Select the reward you wish to hide β€” the points product ID is the last part of the URL in the address bar. Add that ID to the `hidePointsProductIds` variable to hide it from the dropdown. ## 4. Apply the discount code to your cart In order for the dropdown to be functional, you will need to specify how the generated coupon code should be applied to the customer's cart. The method `purchasePointsProduct` is triggered by the customer when the "Redeem" button is clicked. Within the callback of this method, you'll have access to the discount code which can then be applied to the cart however you see fit. The code is available by referencing `pointsProduct.fulfilled_reward.code`. The process for applying the discount code to your customer's cart can be done via your cart's API, or by a form field input simulated with JavaScript. To complete the setup of the dropdown, you'll need to customize the contents of the `applyDiscountCodeToCart` function so that the generated code is applied to the customer's cart. ## 5. You're done! πŸŽ‰ Visit your store's checkout page to confirm the dropdown works as expected. ## Tips for the best customer experience Certain points products apply discounts to the cart in different ways. For example, gift cards can apply discounts to the total value of the cart whereas coupon codes may only apply discounts to the cart subtotal. There are other factors, including your region and cart setup, that prevent us from providing a single code sample that works flawlessly out of the box for every merchant. Our sample code is meant to get you up and running with giving your customers the option to spend their points at checkout. It allows them to purchase any points product they can afford. As a result, your customers may accidentally spend their points on a points product that has a greater value than what can actually be discounted from the cart. You may want to filter out these points products so that your customers are assured to have a great experience at checkout! # Overview Source: https://dev.smile.io/guides/deprecated/custom-platform/custom-frontend-int/overview Add Smile to your custom platform website using the JavaScript SDK or Smile UI. Adding Smile to your website can be accomplished in a couple of different ways. The first is to use the [JavaScript SDK](/js/introduction) (Smile.js), which is a lightweight JavaScript library that allows you to build a fully custom rewards & loyalty experience, without needing to manage complex API logic yourself. The second is to use [Smile UI](/ui/including), which is Smile's pre-built loyalty panel and launcher whose branding and customization is managed entirely via Smile Admin. Build a fully custom rewards & loyalty UI. Use Smile's pre-built loyalty panel and launcher. ## Optional: Allow customers to spend at checkout Allowing customers to redeem points at checkout is a great way to promote points spending and boost engagement with your loyalty program. [Adding a points dropdown to your checkout](/guides/deprecated/custom-platform/custom-frontend-int/custom-points-at-checkout) makes spending points quick, easy, and seamless. # Overview Source: https://dev.smile.io/guides/deprecated/custom-platform/overview Integrate a custom ecommerce platform with Smile using our API-based solution. Deprecated for new integrations. If your online store does not use one of our supported platforms, you can integrate with Smile using our API-based solution in just a few steps. As of March 2020, Smile is no longer supporting new custom ecommerce platform integrations. Existing merchants will continue to have access to all of the features and functionality they know and love. This documentation is maintained as a historical reference only. The steps for integrating are: Follow our [backend integration guide](/guides/deprecated/custom-platform/custom-backend-int) to send customer and order data to Smile. Follow our [frontend integration guide](/guides/deprecated/custom-platform/custom-frontend-int) to integrate Smile into your website experience. Use [Smile Admin](https://app.smile.io) to configure how your customers earn and spend points and customize the look and feel of your loyalty program. Still have questions? Check out our [custom platform FAQ](/guides/deprecated/custom-platform) for more info. # Concepts & fundamentals Source: https://dev.smile.io/guides/fundamentals Learn the core concepts and building blocks of Smile's system. In order to make loyalty programs simple to setup and effortless to maintain, Smile uses the basic model of "actions in, rewards out." This means that all of the complexity of evaluating when, where, and how to issue rewards is handled internally by Smile. From a development perspective, the only thing you need to worry about is notifying Smile that the customer has performed an action. Diagram of Smile's actions in, rewards out model: customer actions flow into Smile, and Smile issues rewards ## Customer actions In technical terms, customer actions are represented as [Activities](/api/resources/activities/activity-object), and they're created using the [create an activity endpoint](/api/resources/activities/create-activity) or [JavaScript SDK method](/js/resources/activities/create). Smile supports a variety of native activity types (like orders, social sharing, birthdays, and more), and additional custom activity types can be defined to enable [rewarding for any action](/guides/use-cases/rewarding-for-any-action). Native activity types sync automatically into Smile, requiring no custom development. Only custom activity types require explicit definition and for you to use the create an activity endpoint or JavaScript SDK method. ## Reward issuing When a new activity is created in Smile, it's processed asynchronously against the account's configured ways to earn ([Earning Rules](/api/resources/earning-rules/earning-rule-object)). These rules are setup via Smile's loyalty program management UI, Smile Admin. If a matching rule is found and the customer and activity meet all of the rewarding criteria, Smile will generate a [Reward Fulfillment](/api/resources/reward-fulfillments/reward-fulfillment-object). The fulfillment represents Smile's attempt to issue a reward to the customer. At this time, the only type of reward that can be issued when a customer performs an action are points. When a customer earns points, a [Points Transaction](/api/resources/points-transactions/points-transaction-object) record is created, and the points balance field on the [Customer](/api/resources/customers/customer-object) object is updated. ## Points redemption When they have accumulated enough points, customers can exchange their points for a reward (e.g. a discount coupon for their next order). The available redeeming options are modelled as [Points Products](/api/resources/points-products/points-product-object), and are configured by the merchant in Smile Admin. Redemption can occur via any of Smile's native UI components (like Smile UI or Shopify Checkout Extensions), by using the [JavaScript SDK method](/js/resources/points-products/purchase), or with the [purchase a points product endpoint](/api/resources/points-products/purchase-points-product) endpoint. # Introduction Source: https://dev.smile.io/guides/introduction Smile's developer platform enables you to create custom loyalty solutions that meet the needs of merchants, regardless of industry or specialization. Learn the core concepts and building blocks of Smile's loyalty program system. Explore common implementation patterns and real-world examples to jump-start development. Create custom integrations and apps that extend Smile's functionality. # Common use cases Source: https://dev.smile.io/guides/use-cases/common Explore common implementation patterns and real-world examples to jump-start development. * [Add Smile to any website](/guides/use-cases/custom-frontend) * [Loyalty landing or explainer page](/guides/use-cases/landing-page/overview) * [Rewarding for any action](/guides/use-cases/rewarding-for-any-action) * [Points spending at checkout](/guides/use-cases/points-at-checkout/overview) # Add Smile to your frontend Source: https://dev.smile.io/guides/use-cases/custom-frontend Embed loyalty functionality into any website or custom storefront using Smile's JavaScript SDK, Smile UI, or both. You can add Smile to any website or custom storefront you've built, as long as your customer and order data are powered by a supported platform (such as Shopify or BigCommerce). How you integrate depends on the experience you want. ## Choose how to integrate * **Fully custom rewards UI** β€” Use the [JavaScript SDK](/js/introduction) to build your own loyalty experience from scratch. The SDK gives you full control over the UI and works well in single-page and headless setups. * **Pre-built loyalty panel** β€” Use [Smile UI](/ui/introduction) to add Smile's popup loyalty panel and launcher with minimal code. Best when you want to include loyalty information without building or maintaining a custom UI. * **Both** β€” Use the SDK for custom flows (e.g. embedded redemption at checkout) and Smile UI for the panel. Pass `includeSdk: true` when [initializing Smile UI](/ui/initializing) to load the JavaScript SDK alongside it. ## Platform-specific guides Add Smile to any headless or Single Page Application frontend. Add Smile to a Shopify Hydrogen storefront. # Headless / Single Page Applications Source: https://dev.smile.io/guides/use-cases/custom-frontend/headless Add Smile to any headless or SPA frontend using the JavaScript SDK or Smile UI. Yes, Smile works on headless storefronts and single page applications (SPAs)! With Smile UI, you can use Smile's pre-built panel and launcher with minimal code, or use the JavaScript SDK to build your own custom loyalty experiences from scratch. ## Using the JavaScript SDK Follow the instructions below to include and initialize the JavaScript SDK. No extra steps are required for headless or SPA setups since the SDK does not rely on full page reloads to function. Follow the instructions for [including the JavaScript SDK](/js/including). You can either use the script tag approach or include the npm package using a bundler like Vite or Webpack. Use a backend service to [generate a customer token](/js/concepts/customer-tokens) that identifies the currently-logged-in customer. It's important that this occurs on the backend to avoid exposing your signing key to the general public. Read the guide on [preloading resources](/js/concepts/preloading) and identify the loyalty data that you want to use or include on every page. Preloading resources helps improve performance by reducing the number of API calls made, and is especially useful for headless or SPA setups where full page reloads occur less frequently (or not at all). Follow the instructions for [initializing the JavaScript SDK](/js/smile/initialize), passing the customer token you generated as well as the list of resources you want to preload. Once the SDK is included and initialized, you can start using it to fetch and interact with customer and loyalty data! Refer to the [JavaScript SDK](/js/introduction) documentation for details on all of the available methods and resources. ## Using Smile UI Follow the instructions below to include and initialize Smile UI, and optionally load the JavaScript SDK alongside it. ### Key considerations * **Nudges** β€” Nudges are not triggered or visible when using Smile UI on a custom storefront or on any site where the panel is manually added. * **Page reloads** β€” The panel and launcher rely on full page reloads to detect when a customer has logged in or logged out, and to refresh customer state. On sites that do not trigger a full page reload when customer data changes, you must manually trigger a full reload so that Smile UI re-initializes correctly. Calling `SmileUI.initialize()` again without a full page reload is not sufficient. * **Translations** – When using Smile UI anywhere other than a Shopify store, the panel and launcher's ability to detect which language the customer is browsing the site in and automatically present their content in that language (if supported) does not work; in this case their content will always be presented in the language configured for the program via Smile Admin. ### Integration instructions Follow the instructions for [including Smile UI](/ui/including) to add a script tag on each page where you want the panel and launcher to appear. Use a backend service to [generate a customer token](/ui/customer-tokens) that identifies the currently-logged-in customer. It's important that this occurs on the backend to avoid exposing your signing key to the general public. Follow the instructions for [initializing Smile UI](/ui/initializing), passing the customer token you generated in the previous step. If you want to load the JavaScript SDK alongside Smile UI, pass `includeSdk: true` when initializing. Since Smile UI relies on full page reloads to refresh customer data, you'll need to ensure that a full page reload occurs whenever: * A customer logs in or logs out, * An action takes place outside of the Smile loyalty panel that might change the customer's points balance or rewards status. For instance, redeeming points via the JavaScript SDK or completing a custom activity (that would cause them to earn points). You can trigger a full page reload by calling `window.location.reload()` or using a suitable equivalent for your frontend framework or setup. Reloading is only necessary when the customer is logged in; if no customer is logged in, a full page reload is not required. Once Smile UI is included and initialized, you can add links to your website (like menu items or buttons) that will open the panel to a specific page. Refer to the [deep links](/ui/panel/deep-links) guide for details on the different ways this can be accomplished. # Shopify Hydrogen Source: https://dev.smile.io/guides/use-cases/custom-frontend/hydrogen Add Smile to a Shopify Hydrogen storefront using the JavaScript SDK or Smile UI. Yes, Smile works on [Shopify Hydrogen](https://shopify.dev/docs/storefronts/headless/hydrogen)! Because a Hydrogen storefront is a custom React app backed by Shopify, the same approach used for any [headless or single page application](/guides/use-cases/custom-frontend/headless) applies. You can use the [JavaScript SDK](/js/introduction) to build a fully custom loyalty experience, or drop in [Smile UI](/ui/introduction) for a pre-built panel and launcher. This guide covers the three Hydrogen-specific things you'll need to handle: generating the customer token in a server loader, allowlisting Smile in your Content Security Policy, and accounting for Hydrogen's client-side navigation. Hydrogen is a React Router app deployed to Shopify's Oxygen edge runtime (a Workers-style runtime, not Node.js). This guide was validated against Hydrogen 2026.4 and React Router 7. The exact APIs may differ slightly between Hydrogen versions β€” check Shopify's documentation if something doesn't match your setup. ## Choose how to integrate * **JavaScript SDK (recommended for Hydrogen)** β€” Build a fully custom loyalty UI as React components. Because the JavaScript SDK refreshes customer state via its own methods, it works naturally with Hydrogen's client-side navigation and doesn't require full page reloads. * **Smile UI** β€” Add Smile's pre-built panel and launcher with minimal code. Works on Hydrogen, but the panel relies on full page reloads to refresh customer state, so you'll need to trigger reloads manually (see below). * **Both** β€” Use Smile UI for the panel and the JavaScript SDK for custom flows by passing `includeSdk: true` when [initializing Smile UI](/ui/initializing). ## Required setup Regardless of which integration approach you choose, you'll need to complete the following required setup. ### 1. Define your environment variables The rest of this guide assumes two environment variables are available to your loaders through `context.env`: * `SMILE_PUBLISHABLE_KEY` β€” identifies your Smile account and is safe to expose in client-side code. The help docs cover [where to find your publishable key](https://help.smile.io/en/articles/11878403-find-publishable-key). * `SMILE_SIGNING_KEY` β€” signs the customer tokens you'll generate in the next step. The help docs cover [how to create a signing key](https://help.smile.io/en/articles/11878129-manage-signing-keys). Add both to your local `.env` file, and set them as [environment variables on your Hydrogen storefront](https://shopify.dev/docs/storefronts/headless/hydrogen/environments) so they're available to deployed environments. The signing key is a secret. Keep it in environment variables β€” never include it in client-side code or commit it to your repository. ### 2. Generate the customer token Smile uses [customer tokens](/js/concepts/customer-tokens) to identify the currently logged-in user, and they must be generated on the backend to ensure your Smile signing key is never exposed. In Hydrogen, loaders and actions run server-side, which is the right place to generate the customer token. Generating a customer token involves two steps: using the [Customer Account API](https://shopify.dev/docs/storefronts/headless/building-with-the-customer-account-api/hydrogen) to identify the logged-in customer, then signing a JWT that contains the numeric portion of their Shopify customer ID. ```js app/root.jsx theme={null} export async function loader({ context }) { let customerToken = null; if (await context.customerAccount.isLoggedIn()) { const { data } = await context.customerAccount.query(`#graphql query { customer { id } } `); // data.customer.id is a GID, e.g. "gid://shopify/Customer/10733458" const shopifyCustomerId = data.customer.id.split('/').pop(); customerToken = await generateSmileCustomerToken(shopifyCustomerId, context.env); } return { customerToken, publishableKey: context.env.SMILE_PUBLISHABLE_KEY, // ...the rest of your existing root loader data }; } ``` The Node `jsonwebtoken` library isn't available on Oxygen, so you must use a runtime-compatible JWT library such as [`jose`](https://github.com/panva/jose) (which uses Web Crypto), or Web Crypto's `crypto.subtle` directly: ```js app/lib/smile.server.js theme={null} import { SignJWT } from 'jose'; export async function generateSmileCustomerToken(shopifyCustomerId, env) { const secret = new TextEncoder().encode(env.SMILE_SIGNING_KEY); return await new SignJWT({}) .setProtectedHeader({ alg: 'HS256', typ: 'JWT' }) .setAudience('api.smile.io') .setSubject(`ShopifyCustomer:${shopifyCustomerId}`) .setExpirationTime('1h') .sign(secret); } ``` Things to consider: * The `sub` must be the **numeric Shopify customer ID** (the tail of the `gid://shopify/Customer/…` GID), not Smile's internal customer ID β€” the two look alike, but a token signed with the wrong one is rejected. * Choose an expiry that covers a full browsing session. Smile verifies the token's expiry on every API call, and your root loader only generates a fresh token on a full page load β€” a short-lived token causes loyalty requests to start failing for customers who keep a page open longer than the expiry. One hour matches the lifetime Shopify uses for its own [Customer Account API access tokens](https://shopify.dev/docs/api/customer/latest). ### 3. Allowlist Smile in your CSP Hydrogen applies a strict [Content Security Policy](https://shopify.dev/docs/storefronts/headless/hydrogen/content-security-policy) by default, which will block Smile until you allowlist its origins. Extend the policy in `entry.server.jsx`, picking the tab that matches how you chose to integrate: ```js app/entry.server.jsx theme={null} const { nonce, header, NonceProvider } = createContentSecurityPolicy({ shop: { checkoutDomain: context.env.PUBLIC_CHECKOUT_DOMAIN, storeDomain: context.env.PUBLIC_STORE_DOMAIN, }, scriptSrc: ["'self'", 'https://cdn.shopify.com', 'https://sdk.smile.io'], connectSrc: ['https://sdk.smile.io', 'https://api.smile.io'], }); ``` ```js app/entry.server.jsx theme={null} const { nonce, header, NonceProvider } = createContentSecurityPolicy({ shop: { checkoutDomain: context.env.PUBLIC_CHECKOUT_DOMAIN, storeDomain: context.env.PUBLIC_STORE_DOMAIN, }, scriptSrc: ["'self'", 'https://cdn.shopify.com', 'https://js.smile.io'], connectSrc: [ 'https://js.smile.io', 'https://platform.smile.io', // Smile UI API β€” note: NOT api.smile.io 'https://auth.smile.io', ], styleSrc: ['https://js.smile.io'], imgSrc: [ "'self'", 'https://cdn.shopify.com', 'https://shopify.com', 'https://*.smile.io', // launcher icons 'data:', ], frameSrc: ["'self'", 'https://js.smile.io'], // panel/launcher iframes }); ``` **Smile UI does not use `api.smile.io`** β€” it calls `platform.smile.io` and `auth.smile.io`. If these are missing, Smile UI fails *silently*: no launcher, no visible CSP error, and `SmileUI.ready()` rejects with an internal error. Programs with custom reward images may serve assets from other hosts. Watch the console and extend `imgSrc` if launcher or panel images fail to load. ```js app/entry.server.jsx theme={null} const { nonce, header, NonceProvider } = createContentSecurityPolicy({ shop: { checkoutDomain: context.env.PUBLIC_CHECKOUT_DOMAIN, storeDomain: context.env.PUBLIC_STORE_DOMAIN, }, scriptSrc: [ "'self'", 'https://cdn.shopify.com', 'https://sdk.smile.io', // JavaScript SDK 'https://js.smile.io', // Smile UI ], connectSrc: [ 'https://api.smile.io', // JavaScript SDK API 'https://sdk.smile.io', 'https://js.smile.io', 'https://platform.smile.io', // Smile UI API β€” note: NOT api.smile.io 'https://auth.smile.io', ], styleSrc: ['https://js.smile.io'], imgSrc: [ "'self'", 'https://cdn.shopify.com', 'https://shopify.com', 'https://*.smile.io', // Smile UI launcher icons 'data:', ], frameSrc: ["'self'", 'https://js.smile.io'], // Smile UI panel/launcher iframes }); ``` **Smile UI does not use `api.smile.io`** β€” it calls `platform.smile.io` and `auth.smile.io`. If these are missing, Smile UI fails *silently*: no launcher, no visible CSP error, and `SmileUI.ready()` rejects with an internal error. Programs with custom reward images may serve assets from other hosts. Watch the console and extend `imgSrc` if launcher or panel images fail to load. In every directive Hydrogen [doesn't set a default value for](https://github.com/Shopify/hydrogen/blob/main/packages/hydrogen/src/csp/csp.ts) (`scriptSrc`, `imgSrc`, and `frameSrc` above), re-add `'self'` and `https://cdn.shopify.com` (and `https://shopify.com` for images) yourself. Once a directive is defined, the browser [no longer falls back to `default-src`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/default-src), so omitting them blocks your own scripts and product images. Hydrogen merges your values into the directives it does set defaults for (`connectSrc`, `styleSrc`), so those only need Smile's origins. ## Using the JavaScript SDK This is the recommended approach for Hydrogen, since it gives you complete control to create a fully custom React-based loyalty UI and doesn't rely on full page reloads. Load Smile's JavaScript SDK with Hydrogen's [`Script` component](https://shopify.dev/docs/api/hydrogen/latest/components/script), which automatically applies the CSP nonce. Always load the JavaScript SDK from Smile's CDN rather than bundling it. ```jsx app/root.jsx theme={null} import { Script } from '@shopify/hydrogen'; // ...inside // [!code --] // [!code ++] ``` ### 2. Update method calls Any call sites for the removed `Smile.*` methods should be updated to use the corresponding methods from the JavaScript SDK instead. With the exception of the `Smile.purchasePointsProduct()` method, no parameter names or values have changed, so you can simply replace method names with the corresponding equivalent from the JavaScript SDK. | Old `Smile.*` method | New JavaScript SDK method | | :--------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Smile.createActivity()` | [`Smile.activities.create()`](/js/resources/activities/create)

| | `Smile.customerReady()` | Listen for the [`smile-js-initialized`](/js/concepts/initialized-event) event instead.

| | `Smile.fetchAllCustomerPointsProducts()` | [`Smile.customerPointsProducts.get()`](/js/resources/customer-points-products/get)


| | `Smile.fetchAllPointsProducts()` | [`Smile.pointsProducts.get()`](/js/resources/points-products/get)

| | `Smile.fetchAllRewardFulfillments()` | [`Smile.rewardFulfillments.get()`](/js/resources/reward-fulfillments/get)

| | `Smile.fetchCustomer()` | Based on the data you need, use any of:
β€’ [`Smile.customer.current()`](/js/resources/customer/current) - name & referral URL
β€’ [`Smile.customerPointsWallet.get()`](/js/resources/customer-points-wallet/get) - points balance
β€’ [`Smile.customerVipStatus.get()`](/js/resources/customer-vip-status/get) - VIP tier

| | `Smile.fetchPointsProduct()` | [`Smile.pointsProducts.getById()`](/js/resources/points-products/get-by-id)

| | `Smile.purchasePointsProduct()` | [`Smile.pointsProducts.purchase()`](/js/resources/points-products/purchase) *(see note)*

**Note:** The `points_to_spend` parameter has changed to `pointsToSpend`.

| | `Smile.ready()` | Listen for the [`smile-js-initialized`](/js/concepts/initialized-event) event instead.

| ### 3. Update customer ready calls The `SmileUI.customerReady()` method has been removed, and the appropriate replacement depends on what you're trying to achieve. * To detect when it's **safe to call other `SmileUI.*` methods**, use the [`SmileUI.ready()`](/ui/events/ui-ready) method. This would typically be for situations where you want to programmatically open the loyalty panel or launcher. * To detect when it's **safe to call other `Smile.*` methods**, listen for the [`smile-js-initialized`](/js/concepts/initialized-event) event from the JavaScript SDK. This would typically be for situations where you want to programmatically access information about the customer or loyalty program (like displaying their points balance). ```javascript JavaScript lines theme={null} SmileUI.customerReady().then(() => { // [!code --] SmileUI.ready().then(() => { // [!code ++] // Safe to call SmileUI.* methods }); ``` ```javascript JavaScript lines theme={null} SmileUI.customerReady().then(() => { // [!code --] document.addEventListener('smile-js-initialized', () => { // [!code ++] // Safe to call Smile.* methods }); ``` ### 4. Update object properties The JavaScript SDK now returns all objects with `camelCase` property names (instead of `snake_case`) for improved compatibility with modern JavaScript frameworks and tooling. As such, you'll need to update any code that accesses object properties to use the new `camelCase` names. ### 5. Audit for preloading The JavaScript SDK introduces the ability to [preload resources](/js/concepts/preloading), enabling you to retrieve multiple resources on initial page load and reduce the number of roundtrip API calls being made. This helps improve performance and reduce latency, while also providing instant access to preloaded data via new `.preloaded()` methods on each resource class. Read our guide on [preloading resources](/js/concepts/preloading), and then audit your code for opportunities to switch from using `.get()` methods to the new `.preloaded()` methods instead. Often, it's as simple as replacing a single `.get()` call with a single `.preloaded()` call, like so: ```javascript JavaScript lines theme={null} await Smile.initialize({ publishableKey: 'pub_0987654321', customerToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', preload: ['pointsProducts', 'customerPointsWallet'] // [!code ++] }); // Make separate API calls for each resource // [!code --:3] const pointsProducts = await Smile.pointsProducts.get(); const pointsWallet = await Smile.customerPointsWallet.get(); // Access preloaded data instantly (no additional API calls) // [!code ++:3] const pointsProducts = Smile.pointsProducts.preloaded(); const pointsWallet = Smile.customerPointsWallet.preloaded(); ``` ### 6. Preload points settings The `Smile.formatPoints()` method is now part of the JavaScript SDK and requires that the account's points settings be preloaded before points values can be formatted. If you're using the `Smile.formatPoints()` method, you'll need to call [`Smile.preload()`](/js/smile/preload) with `pointsSettings` before formatting points. ```javascript JavaScript lines theme={null} await Smile.preload(['pointsSettings']); // [!code ++] const formattedPoints = Smile.formatPoints(1000); ``` If you're using the `Smile.formatPoints()` method, you'll need to call [`Smile.preload()`](/js/smile/preload) with `pointsSettings` before formatting points. ```javascript JavaScript lines theme={null} await Smile.preload(['pointsSettings']); // [!code ++] const formattedPoints = Smile.formatPoints(1000); ``` If you're using the `Smile.formatPoints()` method, you'll need to include `pointsSettings` in the `preload` array during [initialization](/js/smile/initialize), or call [`Smile.preload()`](/js/smile/preload) with `pointsSettings` before calling the method. ```javascript Preload during initialization lines theme={null} await Smile.initialize({ publishableKey: 'pub_0987654321', customerToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', preload: ['pointsSettings'] // [!code ++] }); const formattedPoints = Smile.formatPoints(1000); ``` ```javascript Preload later lines theme={null} await Smile.preload(['pointsSettings']); // [!code ++] const formattedPoints = Smile.formatPoints(1000); ``` You only need to preload the `pointsSettings` resource once per pageload (whether during initialization or later). Subsequent calls to `Smile.formatPoints()` will use the cached value from the preloaded resource. ### 7. Audit manual page reloads If you're including the JavaScript SDK alongside Smile UI (e.g. are still using the rewards panel and launcher), you'll need to continue to trigger full page reloads after any action that would change the customer's state or points balance (e.g. logging in or out, redeeming points, completing an activity, etc). Assuming your storefront was already using the rewards panel and launcher prior to beginning the migration process, no changes are required. If you're using the JavaScript SDK standalone (without the rewards panel and launcher / Smile UI), you can now programatically log customers in and out and refresh customer data without a full page reload. * To log a customer in, use [`Smile.customer.login()`](/js/resources/customer/login). * To log a customer out, use [`Smile.customer.logout()`](/js/resources/customer/logout). * To refresh customer data, use [`Smile.preload()`](/js/smile/preload) with the appropriate resource key(s). While the old manual page reload approach continues to work, we recommend auditing your codebase for opportunities to switch to using the new methods instead. A complete example of using the new methods in sequence might look like: ```javascript JavaScript lines theme={null} // Log a customer in and preload their points balance await Smile.customer.login({ customerToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', preload: ['customerPointsWallet', 'pointsSettings'] }); // Display the customer's current points balance const pointsBalance = Smile.customerPointsWallet.preloaded(); console.log("You've got", Smile.formatPoints(pointsBalance), "!"); // Purchase a points product (e.g. spend points) await Smile.pointsProducts.purchase(132456921); // Complete an activity (e.g. earn points) await Smile.activities.create({token: 'activity_1dDdzcKNsp84b'}); // Refresh customer points balance await Smile.preload(['customerPointsWallet']); // Show the updated points balance const pointsBalance = Smile.customerPointsWallet.preloaded(); console.log("You've got", Smile.formatPoints(pointsBalance), "!"); // Log the customer out await Smile.customer.logout(); ``` ### 8. Check for remaining errors After completing the migration steps above, you should no longer see any errors related to the removed `Smile.*` methods (e.g. `TypeError: Smile.fetchCustomer is not a function`) in the browser console when viewing your storefront. If you do, it means that you're still calling one or more of the removed methods and your code requires further updates. ## Where to get help While we aren't able to assist with updating, debugging, or reviewing custom code, our [support team](https://help.smile.io/en/articles/4495476-how-to-get-help-with-smile) can help answer questions or clarify details related to the migration process or the JavaScript SDK itself. If you need assistance updating your code, our team can help connect you with an agency partner who specializes in Smile integrations. ## FAQ No. The removal only affects custom JavaScript code that uses the bundled `Smile.*` methods (like `Smile.fetchCustomer()` or `Smile.createActivity()`). If your storefront only uses the out-of-the-box rewards panel and launcher and hasn't been customized with code that calls these methods, no action is required. This migration only applies if you (or your developer) wrote custom code using the removed methods β€” typically to embed loyalty information directly into your storefront (e.g. a customer's points balance in the site header) or to build custom loyalty pages and experiences. The easiest way to check is to open the browser console and visit every page on your website that includes loyalty information or functionality. While browsing each page, look for any errors related to the `Smile.*` methods (e.g. `TypeError: Smile.fetchCustomer is not a function`) in the browser console. If you see any, you'll need to update your code to use the corresponding methods from the JavaScript SDK instead. If you've visited every page on your website and don't see any of these errors, try searching through your codebase for any remaining references to the removed methods. If you've done both and haven't found any references to the removed methods in your codebase or seen any related errors in the browser console, no migration is required. The full list of methods that were removed is as follows: * `Smile.createActivity()` * `Smile.customerReady()` * `Smile.fetchAllCustomerPointsProducts()` * `Smile.fetchAllPointsProducts()` * `Smile.fetchAllRewardFulfillments()` * `Smile.fetchCustomer()` * `Smile.fetchPointsProduct()` * `Smile.formatPoints()` * `Smile.purchasePointsProduct()` * `Smile.ready()` If you're using a removed method, you'll need to follow the migration steps in this guide to update your code. If you need help, our team can help connect you with an agency partner who specializes in Smile integrations. Unfortunately, no. Smile doesn't have access to your storefront's code, so we aren't able to update, debug, or review custom code on your behalf. Our support team is happy to answer questions about the migration process or the JavaScript SDK itself, but the code changes need to be made by you or your developer. If you don't have a developer or agency on hand, our team can connect you with an agency partner who specializes in Smile integrations. The removed `Smile.*` methods now throw standard JavaScript errors (e.g. `TypeError: Smile.fetchCustomer is not a function`). Any custom code still calling them doesn't work β€” for example, custom points balance displays, referral URL displays, or any custom loyalty pages built using the removed methods. Following the migration steps in this guide restores that functionality. Standard Smile UI functionality (the rewards panel and launcher) is not affected and continues to work as expected. No. This migration is a change to frontend JavaScript code only. Customer points balances, VIP statuses, redemption history, and all other loyalty data are stored on Smile's servers and are completely unaffected by the migration. The standard rewards panel and launcher are unaffected and continue to work throughout the migration. However, any custom functionality built with the removed `Smile.*` methods stopped working when the methods were removed, and remains unavailable until you migrate that code to the JavaScript SDK. Each call site starts working again as soon as its migrated code is deployed, so you can migrate and ship incrementally. Forward them this migration guide. It contains the full list of removed methods, their replacements, and step-by-step migration instructions. If you don't currently have a developer or agency on hand, our support team can help connect you with an agency partner who specializes in Smile integrations. No. The old `Smile.*` methods have been removed and no longer work, so they can't run alongside their JavaScript SDK equivalents. You can still migrate one method call at a time, test as you go, and ship changes incrementally rather than all at once β€” each migrated call starts working as soon as it's deployed. We recommend testing the migration in a staging or development environment before deploying to your live storefront: * **Shopify:** Use a development store or a duplicate of your live theme to test changes before publishing. * **BigCommerce:** Use a staging or preview store. * **Custom storefront:** Use whatever staging or preview environment you typically deploy to before production. Once you've verified everything works as expected (no errors related to `Smile.*` methods in the browser console, custom features functioning correctly), deploy the changes to production. The removed `Smile.*` methods were bundled with Smile UI and were difficult to use in modern JavaScript projects. The new JavaScript SDK is a standalone library that brings several improvements: * Works with modern frameworks and bundlers (e.g. Vite, Webpack) * Includes TypeScript type definitions for autocomplete and type checking * Supports preloading multiple resources in a single API call to reduce latency * Allows logging customers in and out programmatically without a full page reload * Provides access to additional data such as detailed VIP status and program settings See the [What's new](#whats-new) section above for more details. # BigCommerce Stencil Source: https://dev.smile.io/guides/use-cases/landing-page/bigcommerce-stencil Use Smile's explainer page template to add a loyalty landing page to a BigCommerce Stencil theme. BigCommerce storefronts using Stencil themes can use our explainer page template to get started with a simple loyalty landing page. ### 1. Download `files4.zip` from GitHub Start by [downloading the zip](https://github.com/smile-io/code-samples/blob/main/bigcommerce-stencil-files/files%204.zip) containing the explainer page sample. Then, login to your BigCommerce account and go to **Storefront** > **My Themes**. Under **My Themes**, select **Advanced Options** and **Download Current Theme**. Open your terminal and run `cd ~/Downloads/` followed by the file name, e.g. `cd ~/Downloads/Cornerstone-1.9.1` After entering this, you should: `sudo npm install`, which will prompt you for your password. ### 2. Bundle the Explainer Page files and the Theme files While this is installing, go to your **Downloads** and open the `files 4.zip`. Select **Assets and Templates**, and merge them into your downloaded theme file. When this has finished loading in terminal run: `stencil bundle` to bundle the theme together. If you receive a warning in terminal while the files are being bundled such as this: `**failed -- The following template(s) are/is missing: components/pages/page-header**` Open the `loyalty-explainer-page.html` file located in **Templates** file in a text editor (such as Atom or Sublime Text) and scroll to line 28 and delete:`**{{> components/pages/page-header page_title=page.title}}**` ### 3. Upload the Explainer Page When the theme is bundled select **Upload Theme** on your BigCommerce Dashboard, just below where your **Current Theme** is located. Upload the changed theme file to your **Current Theme**. ### 4. Customize the Explainer Page Once the file with the added **Assets and Templates** is uploaded to your theme, select **Customize**, scroll down on the sidebar to the bottom and select **Edit Theme Files**. From here you will be able to make customizations to the Smile.io Explainer Page from your BigCommerce Dashboard. # Overview Source: https://dev.smile.io/guides/use-cases/landing-page/overview Create a loyalty landing page that explains your program and encourages customers to signup and participate. Loyalty landing pages (a.k.a. rewards explainer pages) are the perfect way to help customers understand your program and encourage them to signup and participate. Smile's developer tooling makes it easy to setup and configure explainer pages that match your brand and show accurate and up-to-date information about your program. ## On Shopify Follow [our guide](https://help.smile.io/en/articles/8174498-loyalty-landing-page) to using app blocks in Shopify's built-in site editor to create a loyalty landing page. The app blocks provided by Smile can be mixed and matched with all other standard blocks, in addition to blocks from other apps to create a unique page that fully suits your brand and emphasizes the aspects of the loyalty program that you care about most. ## On BigCommerce Use our [Stencil explainer page template](/guides/use-cases/landing-page/bigcommerce-stencil) to create a loyalty landing page for your store. Since this method involves direct theme file editing, unlimited customization is possible! # BigCommerce dropdown Source: https://dev.smile.io/guides/use-cases/points-at-checkout/bigc-points-dropdown Add a dropdown of redemption options to your BigCommerce checkout so customers can redeem points for rewards. This guide will show you how to create a dropdown of the available redemption options directly within your BigCommerce checkout. The dropdown will enable customers to redeem their points for a reward while checking out, and have the reward automatically applied to their cart. Animation of a customer redeeming points with a dropdown in a BigCommerce checkout ## Step 1: Include our sample code in your checkout page From the BigCommerce theme editor, copy the contents of our [smile-points-products-dropdown.html](https://github.com/smile-io/code-samples/blob/master/bigcommerce-points-products-dropdown/smile-points-products-dropdown.html) code sample into a new theme file. You will need to make sure that this file gets added to your checkout page. This code snippet requires jQuery. If you do not already use jQuery on your site and wish to use the snippet as is, you will need to uncomment the jQuery script at the top of the snippet. ## Step 2: Add your public API key to the script Once you've [located your public API key](https://help.smile.io/en/articles/4036181-find-api-keys), paste it into the `data-channel-key` attribute on the script tag at the top of the code snippet (excerpt below). ```html HTML theme={null} ``` ## Step 3: Filter out unwanted points products Sometimes there are rewards in your program that don't apply to the cart, such as email rewards or free products. You can hide points products from being shown in the dropdown by adding specific points product IDs to the `hidePointsProductIds` variable. To find the ID of a points product you want to hide, visit [Smile Admin](https://app.smile.io) and navigate to your points program's rewards. Select the reward you wish to hide β€” the points product ID is the last part of the URL in the address bar. Add that ID to the `hidePointsProductIds` variable to hide it from the dropdown. ## Step 4: Add the points dropdown to your checkout page Add the following div to your checkout page where you would like the dropdown to appear. ```html HTML theme={null}
``` ## Step 5: Apply the discount code to the cart In order for this component to function, you will need to write the code that takes the generated discount code and applies it to the cart. The `applyPointsPurchaseToCart` function is triggered when the customer clicks the "Redeem" button. Within this method, you'll have access to the discount code which will need to be applied to the cart. The code is available via `pointsPurchase.reward_fulfillment.code`. The process of applying the discount code to your customer's cart can be done via your cart's API, or by a form field input simulated with JavaScript. ## Step 6: Test it out Visit your store's checkout page to confirm your changes. If everything is working as expected, you're done! πŸŽ‰ ## Tips for the best customer experience Certain points products apply discounts to the cart in different ways. For example, gift cards can apply discounts to the total value of the cart whereas coupon codes may only apply discounts to the cart subtotal. There are other factors, including your region and cart setup, that prevent us from providing a single code sample that works flawlessly out of the box for every merchant. Our sample code is meant to get you up and running with giving your customers the option to spend their points at checkout. It allows them to purchase any points product they can afford. As a result, your customers may accidentally spend their points on a points product that has a greater value than what can actually be discounted from the cart. You may want to filter out these points products so that your customers are assured to have a great experience at checkout! # BigCommerce slider Source: https://dev.smile.io/guides/use-cases/points-at-checkout/bigc-points-slider Add a points slider to your BigCommerce checkout so customers can redeem points for a discount in increments. This guide will show you how to create a points redemption slider directly within your BigCommerce checkout. The slider will enable customers to redeem increments of points for a discount, and have the discount automatically applied to their cart. Animation of a customer redeeming points with a slider in a BigCommerce checkout ## Step 1: Include our sample code in your checkout page From the BigCommerce theme editor, copy the contents of our [smile-points-slider.html](https://github.com/smile-io/code-samples/blob/master/bigcommerce-points-slider/smile-points-slider.html) code sample into a new theme file. You will need to make sure that this file gets added to your checkout page. This code snippet requires jQuery. If you do not already use jQuery on your site and wish to use the snippet as is, you will need to uncomment the jQuery script at the top of the snippet. ## Step 2: Add your public API key to the script Once you've [located your public API key](https://help.smile.io/en/articles/4036181-find-api-keys), paste it into the `data-channel-key` attribute on the script tag at the top of the code snippet (excerpt below). ```html HTML theme={null} ``` ## Step 3: Find your points product ID Find the ID of the points product you wish to use with the points slider. You can do this by navigating to the way to earn in question within Smile Admin, and then looking at the ID in the last part of the URL. The ID will always be a number. To use a points slider, the points product it references must be a variable reward (one that supports rewarding in increments). Learn more about the [differences between fixed and variable rewards](https://help.smile.io/article/2490-differences-between-fixed-and-variable-rewards). ## Step 4: Optional - Update your snippet's language The variables that start with "translate" (i.e. `translateRedeemLabel`) are in English by default. Any "translate" variable with the note "Optional translation:" above it can be edited and translated into the language you've set in your language settings. Each phrase has a description, instructions, and an example above it. Follow the instructions to edit these phrases as needed. ## Step 5: Add the slider to your checkout page Add the following div to your checkout page where you would like the slider to appear. Update the `data-points-product-id` property with the ID that you selected in the previous step. ```html HTML theme={null}
``` ## Step 6: Apply discount to the cart In order for this component to function, you will need to write the code that takes the generated discount code and applies it to the cart. The `applyPointsPurchaseToCart` function is triggered when the customer clicks the "Redeem" button. Within this method, you'll have access to the discount code which will need to be applied to the cart. The code is available via `pointsPurchase.reward_fulfillment.code`. The process of applying the discount code to your customer's cart can be done via your cart's API, or by a form field input simulated with JavaScript. ## Step 7: Test it out Visit your store's checkout page to confirm your changes. If everything is working as expected, you're done! πŸŽ‰ # Overview Source: https://dev.smile.io/guides/use-cases/points-at-checkout/overview Let customers redeem points directly at checkout with a dropdown or slider on Shopify or BigCommerce. Allowing customers to redeem points at checkout is a great way to promote points spending and boost engagement with your loyalty program. It lets customers skip the need for copying and pasting discounts codes in favour of using a dropdown or slider interface directly on the checkout screen; making spending points spending quick, easy, and seamless! We've curated guides for each of our supported platforms with instructions on how to modify your checkout page. ## On Shopify Drag-and-drop our [Checkout Extensions](https://help.smile.io/en/articles/6596847-shopify-plus-checkout-extensions) into your checkout editor to add points earning info and redeeming capabilities. Only available on Shopify Plus. ## On BigCommerce * [Points dropdown at checkout](/guides/use-cases/points-at-checkout/bigc-points-dropdown) * [Points slider at checkout](/guides/use-cases/points-at-checkout/bigc-points-slider) # Reward for any action Source: https://dev.smile.io/guides/use-cases/rewarding-for-any-action Reward customers for custom actions unique to your brand using custom activity types and the Smile API. Smile supports rewarding customers for a variety of standard actions (like placing an order or sharing via social channels), but you can also define custom actions that are unique to your brand or setup. If your system is able to detect when the action occurs, then Smile can reward for it! Custom actions are only available on the Plus and Enterprise plans. Refer to the [pricing page](https://smile.io/pricing) for more information. Common custom actions that make great candidates for rewarding include: * Newsletter subscription * Entering a code from a physical product * Watching a video * Uploading a receipt * Filling out a survey * Scanning a QR code * Voting on new product ideas ## General flow Rewarding customers for performing a custom type of action can be broken down into four main steps: In Smile Admin, configure a [new type of custom activity](https://help.smile.io/en/articles/4036268-manage-custom-activity-types) (e.g. "Subscribe to the newsletter"). In Smile Admin, setup a [new way to earn](https://help.smile.io/en/articles/4036267-configure-ways-to-earn) based on the new custom activity type (e.g. "Earn 100 Points when you subscribe to the newsletter"). Once this is defined, the way to earn will automatically start showing up in the loyalty panel and be visible to customers. Once the rewardable action has been completed in your system (e.g. the customer has subscribed to the newsletter), use the [create an activity endpoint](/api/resources/activities/create-activity) or [JavaScript SDK method](/js/resources/activities/create) to notify Smile that the activity has occurred. You'll use the activity type/token from the definition you created in step 1. Based on the configured way to earn, Smile will automatically issue the appropriate reward(s) to the customer without any additional action required β€” no need to worry about keeping track of points or statuses on your end! # Home Source: https://dev.smile.io/index Build custom loyalty experiences with Smile's REST API, JavaScript SDK, and Smile UI.

Enterprise loyalty.
Built for developers.

Smile's full-stack tooling enables you to create unique & personalized rewarding experiences that are effortless to manage and maintain.

Learn how to use Smile's developer tooling and resources. Deeply integrate loyalty into your backend systems with our REST API. Build fully custom loyalty experiences directly in the browser. Smile's pre-built and configurable loyalty panel and launcher.
# Customer tokens Source: https://dev.smile.io/js/concepts/customer-tokens Learn how to generate the JWT customer tokens Smile.js uses to identify logged-in customers. The SDK uses customer tokens to securely recognize and identify the currently logged-in customer during [initialization](/js/smile/initialize). In practice, a customer token is a [JSON Web Token (JWT)](https://www.jwt.io/introduction) that has been generated securely on your platform's backend and is signed using the HS256 algorithm with one of your account's [signing keys](https://help.smile.io/en/articles/11878129-manage-signing-keys). Customer tokens should never be generated on the frontend or in mobile app source code, as this risks exposing your signing key (a sensitive account secret) to the general public. ## JWT headers When generating a customer token, the values in the JWT header are constant and never change: ```json JSON theme={null} { "alg": "HS256", "typ": "JWT" } ``` ## JWT payload When generating a customer token, the payload of the JWT should be dynamic, e.g. ```json JSON theme={null} { "aud": "api.smile.io", "sub": "", "exp": "