Skip to main content
At Snappy, we believe a reward should feel like a moment, not a transaction. We’ve watched millions of gifts get chosen, and the pattern is clear: the experiences people treasure are the ones that feel personal and tangible - a beautifully shot product they’d never have bought themselves, a “this is so me” recommendation, a weekend away to remember. The best-in-class rewards programs we see share two ingredients: a rich mix of tangible rewards and experiences alongside the usual options, and personalization that makes each person feel seen. Get those two right and a rewards storefront stops feeling like a points-cashout screen and starts feeling like a treat. This recipe walks you through building exactly that - end to end, with the Snappy API doing the heavy lifting. We’ll cover fetching the catalog, personalizing a “For You” page, browsing and searching, product detail pages with variants, checkout with address validation, placing orders, and tracking fulfillment. Along the way we’ll share the UX opinions we’ve formed from all those gift choices.
Snappy’s catalog spans physical products, experiences, gift cards, and donations - the full range, so there’s something for everyone. This guide leans into the tangible and experiential, because that’s where the delight compounds. Think of it as where to point the spotlight, with everything else close at hand.

What you’re building

A storefront with four surfaces, each backed by Snappy endpoints:

For You

A personalized landing page of curated picks.

Catalog

The full browsable store - categories, search, filters, sort.

Product detail

Rich pages with galleries and up to three levels of variants.

Checkout

Recipient details and a shipping address, validated as they type.
And underneath it all: one call to place the order, plus webhooks to track it home.

Before you start

The only real setup task on your side is billing. Snappy fulfills real products to real doorsteps, so before you can place an order you need a Billing Method configured (see Billing Methods).
1

Configure a billing method

In your Snappy account, set up a billing method under the account you’ll be ordering against. This is a one-time setup in the dashboard - no code required.
2

Grab your IDs

You’ll need three identifiers as you build:
  • accountId - the account orders are billed to. At checkout you pass it as the Snappy-Account-Id header (not in the body).
  • billingMethodId - the billing method within that account. Discover available methods with GET /v3/billing-methods.
  • collectionId - the product collection you’re merchandising from. Most partners get a collection scoped to their program; if you’re not sure which one is yours, ask your Snappy contact.
3

Get your API key

All requests authenticate with an X-Api-Key header. Your key carries scopes - for this guide you’ll want products:read, collections:read, billingMethods:read, and orders:create. See Authentication & Security.
Treat your API key like a password. Keep it server-side, never ship it in your frontend bundle or mobile app, and rotate it if it’s ever exposed. Every call in this guide should originate from your backend.
All requests share the same base URL and auth header:

Step 1 - Fetch the catalog

Your storefront needs products. Snappy gives you a collection of them through the v3 products endpoint:
There are two ways to get products onto your pages, and the right one depends on your platform:
  • Fetch live - call Snappy when you render a page (with a short cache). Simplest to build, and you never have to think about whether prices or availability are current - they always are, straight from the source.
  • Bulk import - pull the whole catalog into your own database and serve from there. A natural fit if you already have catalog infrastructure (your own search index, merchandising tools, an existing product schema). The tradeoff: your copy can drift from ours, so you’ll subscribe to webhooks to keep availability and pricing in sync (more on that below).
For this guide we’ll fetch live, because it’s the simplest path and it sidesteps the whole question of keeping prices and stock up to date. If a bulk import fits your platform better, skip to Keeping a local catalog in sync for the webhook side of the story - everything else in this guide applies either way. Here’s a paginated fetch of a collection. The endpoint uses cursor pagination - follow links.next until it’s null.
Each product comes back shaped like this:
A few fields earn their keep right away:
  • media drives your product imagery - use the first image as the card thumbnail.
  • category.fullName is a breadcrumb you can split for category filters.
  • tags are your personalization fuel (next step).
  • type tells you whether this is physical, digital, giftCard, or donation (a single value, not an array) - useful for steering merchandising.
  • priceRange is what you’ll map into points.

Keeping a local catalog in sync

If you’ve chosen the bulk-import route - pulling the catalog into your own database with the Export endpoints rather than fetching live - there’s one job you take on in return: keeping your copy fresh. A product that’s out of stock or repriced on our side should reflect that on yours, ideally within minutes. Snappy handles this with webhooks. Subscribe to the stock availability event and update your local records as changes roll in, instead of re-importing the whole catalog on a timer.
See Webhook Event Types for the full payload shape and the current handling rules.
Fetching live (the path this guide follows) skips all of this - there’s nothing to keep in sync because every render reads the source of truth. Reach for bulk import + webhooks when you have real reasons to own a local copy, not by default.

Step 2 - Model your points economy

Snappy’s catalog is priced in real currency. Your users think in points - recognition points, loyalty tier points, anniversary credits, whatever your program calls them. The bridge between the two is a ratio you decide and own. Pick a ratio that feels generous and legible. A round number is your friend:
The real magic is filtering the catalog by what a person can actually afford. Translate their points balance into a max budget and pass it straight to the API, so you can recommend products that they can redeem instantly:
Showing an “almost there” rail of items slightly above someone’s balance is a lovely nudge - it gives points a sense of momentum.

Step 3 - A personalized “For You” page

The “For You” page is where you earn the user’s attention. Instead of dropping them into a 2,000-item grid, you greet them with a handful of curated rails that feel hand-picked. The raw material is tags. Snappy tags span categories (“Home & Kitchen”), occasions (“Birthday”), and values (“Sustainable”, “Women-Owned”). Pull the list:
GET /v3/product-tags returns a paginated { data, links } envelope (page-number pagination, page[size] max 100). Follow links.next if you need more than one page.
Now group a few meaningful tags into interests - human-friendly buckets like “Wellness & Self-Care,” “Travel & Adventure,” “Food & Dining,” “Home & Living.” Each interest maps to one or more tags, and each becomes a rail. Build a rail by fetching the collection with include=tags and matching on tag names client-side (the standalone GET /v3/products endpoint also supports server-side filter[tagId] / filter[brandId] if you’d rather filter the whole catalog by ID):
For the featured rail at the very top, this is your moment to set the tone: lead with a beautiful tangible product or a standout experience. You can highlight key products directly by fetching them by ID, or do a broad fetch from Snappy. Our products are sorted by relevance by default, meaning that the most trending products will be fetched first.

Step 4 - The personalization flow

A “For You” page is only as good as what it knows. The fastest way to learn is to ask - once, gently, up front. Show a short interest picker the first time someone visits: a grid of friendly, image-led cards (“Wellness,” “Travel,” “Food & Dining,” …). Ask them to pick at least two, then assemble their rails from those choices.
1

Present the interests

Render your interest buckets as tappable cards. Lead with imagery - people pick with their eyes. Require a minimum of two so you have enough signal to personalize, but keep it optional to finish: never trap someone behind this screen.
2

Store the selection

Save the chosen interest labels against the user (your database, or a cookie for a logged-out demo). That’s all the state you need.
3

Assemble their page

On the next render, build one rail per selected interest using buildRail from Step 3, plus a featured hero and a couple of evergreen rails (“Popular this month,” “Worth saving for”).
Personalization UX rules we live by:
  • Default smartly. Even before someone picks anything, show a strong generic page - never a blank one.
  • Always let them change it. Put an “Update picks” affordance somewhere visible. Tastes change; so should the page.
  • Never gate the catalog. Personalization is a shortcut, not a tollbooth. The full store is always one tap away.
Keep this flow simple and structured - a fixed set of interests, not an open-ended “tell us in your own words” text box. Structured input is faster for users, easier to map to tags, and produces more predictable rails.

Step 5 - The catalog page

Some people know exactly what they want. The catalog page is for them: the full store, with the controls to slice it down fast. Four controls cover the vast majority of needs, and the v3 endpoint backs all of them:
Your default sort and category order are a merchandising choice, not just a technical one. Leading with physical products and experiences sets the tone and surfaces the rewards people remember most. The full range stays one filter away, so anyone with something specific in mind can get there in a tap - you’re simply putting the most delightful stuff front and center.

Step 6 - Product detail pages

When someone taps a product, give them a page worth the tap: a real gallery, a clear description, and confident variant selection. Pull the full detail for a single product. The list response you’ve been using is a lightweight summary; for the detail page, request the product directly and ask for its aggregated options matrix.
This endpoint returns product-level data only - media is always present, and fields=options adds the aggregated option matrix. The full list of orderable variants comes from a separate endpoint (below).

Showing variants (up to three levels)

Many products come in variations - a hoodie in three colors and five sizes, a laptop in two configurations etc. Snappy models this across up to three option levels (for example: Color, Size, and a third axis like Sports Team or Material). Rendering it cleanly takes two pieces:
  1. The product’s aggregated options (from GET /v3/products/{productId} with fields=options) - what you render pickers from. Each option value carries a firstSelectableVariant you can default to.
  2. The product’s variants (a separate, paginated endpoint) - each concrete, orderable variant with its selectedOptions, price, and media. The order is placed against a variant, not a product.
The aggregated options come back on the product like this:
Fetch the orderable variants from their own endpoint (page-number pagination). Pass fields=price (or price,priceBreakdown) for pricing and include=brand for the variant brand:
Each variant looks like this:
The variant object carries no inline stock flag. To confirm a specific variant can ship to a country, call GET /v3/variants/{variantId}/availability.
Variant UX that respects people:
  • Pre-select each option’s firstSelectableVariant so a single-variant product needs zero taps and pickers never start on a dead-end combination.
  • Reflect the selected variant’s image and price immediately. Surprise at checkout erodes trust.
  • Confirm shippability for the recipient’s country with the availability endpoint, and show the state plainly - a graceful “Not available in your region” beats a failed checkout.

Two features make a store feel alive: a search box that finds things, and “you might also like” rows that keep people exploring. Both are first-class in v3. Product search - there’s no separate search endpoint. Use filter[search] on the catalog endpoint (Step 5) - free-text ranked across product title, category, and brand:
Related products - for “Complete the set” or “More like this” rows on a product page, use the recommendations endpoint keyed off a product ID. It returns products ordered by relevance:
Pass your collectionId to keep recommendations inside your merchandised catalog. If you filter by price, send both filter[price][gte] and filter[price][lte] together - the endpoint requires them as a pair.

Step 8 - Checkout

Checkout is where good intentions meet reality: you need a real recipient and a real address. The kindest thing you can do here is make the address easy to get right.

Validate the address as they type

Snappy’s autocomplete endpoint turns a half-typed string into clean, structured, shippable addresses - fewer failed deliveries, fewer support tickets.
A response looks like:
Debounce the input (~300ms) and only call once the user has typed a few characters. When they pick a suggestion, snap the structured fields into your form - then let them eyeball it. Autocomplete plus a quick human glance is the sweet spot for delivery accuracy.
The fields you ultimately need for an order: recipient first name, last name, email, and phone (E.164 format - required by v3 fulfillment), and a shipping address. Note the autocomplete/validate address shape (addressLine1, addressLine2, city, state, zipcode) maps onto the place-order shape (address1, address2, city, provinceCode, postalCode, plus countryCode). For verified-deliverable addresses, run a pre-flight POST /v3/orders/addresses/validate before placing the order.

Step 9 - Place the order

This is the payoff, and it’s refreshingly simple. One call creates the order:
If you’ve integrated with Snappy before, you may remember a two-step dance: create a gift, then claim it. This endpoint collapses that into a single atomic call. You hand over the variant and the recipient; you get back an order. Less code, fewer round-trips, no half-finished states.
A successful response:
The response is intentionally minimal. To retrieve the full order later - line items, fulfillments, and tracking detail - call GET /v3/orders/{orderId}.

The idempotency key matters

The top-level idempotencyKey (1 to 120 characters) makes the call safe to retry. Send the same key twice and you get the same order back - no duplicate, no double-charge. This is your safety net against the classic failure modes: a flaky network, a double-clicked “Redeem” button, a retried request.
Generate one stable key per redemption and reuse it on retries - something like user-{userId}-order-{cartId}. Don’t generate a fresh random key on each attempt, or you’ll defeat the very protection it provides.

Handle the errors people will actually hit

A few failures are worth handling gracefully, because they map to real human moments: Every failure uses the standard v3 error envelope - { message, errorCode, errors[] }. Branch on errorCode, and surface message only when it’s safe to show an end user.
The best error is the one nobody sees. Validate the address (Step 8) and re-confirm availability and affordability before the order call, so checkout itself almost always succeeds. Reserve the error UI for genuine surprises.

Step 10 - Track fulfillment with webhooks

The order is placed - now keep the recipient (and your support team) in the loop without polling. Subscribe to Snappy’s order webhooks and let updates come to you.
Preview - confirm against the final spec. The order-* webhook event names below are the pattern we expect V3 to ship with; confirm exact event names against the current Webhook Event Types reference before you build against them.
Orders move through a predictable fulfillment lifecycle, and each transition fires an event. The fulfillment status values on V3 Orders are: Every V3 webhook arrives in the same envelope - a webhookData block describing the event and an eventData block with the payload. A delivery-status webhook looks roughly like:
Handle it on your backend:
Surface the trackingLink to your recipient as soon as the order ships - a “Track your reward” button closes the loop and turns a transaction into an experience. Anticipation is part of the gift.

Putting it together

We hope this guide has been a useful introduction to how to build a rewards experience on Snappy. As always, our team is available to help you craft the best experience for your customers. Reach out to your Snappy representative for any questions, needs, or feedback.

API Reference

Full endpoint specs for everything used in this guide.
Last modified on July 14, 2026