> ## Documentation Index
> Fetch the complete documentation index at: https://docs.snappy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Embedded Marketplace (Direct fulfillment)

> Build a native gifting experience with Snappy as the invisible fulfillment engine. Catalog sync, ordering, and shipping at scale.

***"Your platform displays the catalog and collects the order, Snappy handles the fulfillment."***

Use this recipe when you want to embed Snappy's product catalog inside your own platform and let your users select items directly. Your system handles the UI, collects the shipping address, and places the order. Snappy handles fulfillment.

Common use cases include:

* Swag stores
* Procurement portals
* Rewards redemption platforms

## **Overview**

In this model, your system handles catalog display, product selection, and address collection. Once your user has made their selection, you pass all the required information to Snappy in two sequential API calls and Snappy processes the order immediately.

Your integration only needs to handle three things: retrieving products, placing orders, and listening for status updates via Webhooks.

***

## **Setting Up Your API Client**

Before making any API calls, initialize your HTTP client with your API key. This setup is used throughout all steps below.

<CodeGroup>
  ```javascript JavaScript theme={null}
  const axios = require('axios');

  const snappyClient = axios.create({
    baseURL: 'https://api.snappy.com/public-api/v2',
    headers: {
      'X-Api-Key': process.env.SNAPPY_API_KEY,
      'Content-Type': 'application/json'
    }
  });
  ```

  ```python Python theme={null}
  import requests
  import os

  session = requests.Session()
  session.headers.update({
    'X-Api-Key': os.environ.get('SNAPPY_API_KEY'),
    'Content-Type': 'application/json'
  })

  BASE_URL = 'https://api.snappy.com/public-api/v2'
  ```
</CodeGroup>

<Warning>
  Store your API key as an environment variable - never hardcode it in your source code.
</Warning>

***

## **Step 1: Identify your Campaign**

Every gift is created within a Campaign. Before making any API calls, you need the id of the Campaign you want to send under.

If you haven't created a Campaign yet, you can do so either through the Snappy Dashboard or via the API using <kbd>POST /campaigns</kbd>. When created via the API, the Campaign is automatically assigned the Account's default Billing Method.

<Note>
  Every Campaign requires a default Collection or Product to be configured. In the Embedded Marketplace model, this is not used - you will specify the exact product and variant when placing the order.
</Note>

**Endpoint:** <kbd>GET /campaigns</kbd> - use this to retrieve your Campaign list and confirm the correct id.

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function getCampaigns() {
    const response = await snappyClient.get('/campaigns');
    const campaigns = response.data.results;
    console.log('Available campaigns:', campaigns);
    return campaigns;
  }
  ```

  ```python Python theme={null}
  def get_campaigns():
      response = session.get(f'{BASE_URL}/campaigns')
      response.raise_for_status()
      campaigns = response.json()['results']
      print('Available campaigns:', campaigns)
      return campaigns
  ```
</CodeGroup>

→ For the full list of Campaign configuration options, see the [API Reference](/modules/api/v2/campaigns/overview).

***

### **Step 2: Retrieve and Display the Product Catalog**

Retrieve the available products and display them in your own platform UI. Call <kbd>GET /products</kbd> to return the full product catalog available to your account.

Each Product object contains a <kbd>variants</kbd> array representing the available options (e.g. size, color). Your UI should present these variants to the user so they can make their selection.

The response includes full product details. The fields most relevant to your UI are <kbd>id</kbd>, <kbd>name</kbd>, and the <kbd>variants</kbd> array. For example:

```json theme={null}
{
  "id": "prd_12345",
  "name": "Premium Hoodie",
  "variants": [
    { "id": "var_001", "size": "M", "color": "Navy" },
    { "id": "var_002", "size": "L", "color": "Navy" },
    { "id": "var_003", "size": "M", "color": "Black" }
  ]
}
```

<Tip>
  Use **Field Projection** to optimize performance and reduce payload size. See [Request & Response Standards](/pages/request-response-standards).
</Tip>

→ For the full product object schema, see the [API Reference](/modules/api/v2/products/overview).

***

### **Step 3: User Selects an Item**

This step happens within your platform - no API calls required yet. Your user browses the catalog you've displayed, selects their preferred item and variant, and provides their shipping address if you don't already have it on file.

Once you have all three pieces of information - Campaign ID, Variant ID, and shipping address - you're ready to place the order.

<Tip>
  We recommend validating the shipping address before placing the order to reduce fulfillment failures. See [Validate Order Address](/modules/api/v2/orders/validate-order-address).
</Tip>

***

### **Step 4: Create the Gift**

Call <kbd>POST /gifts</kbd> with your Campaign ID and recipient details. This creates the Gift object and returns a <kbd>giftId</kbd> which you'll need in the next step.

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function createGift(campaignId, recipient) {
    const response = await snappyClient.post('/gifts', {
      campaignId: campaignId,
      recipients: [
        {
          firstname: recipient.firstname,
          lastname: recipient.lastname,
          email: recipient.email,
          key: recipient.key
        }
      ]
    });

    const gift = response.data.results[0];
    console.log('Gift created:', gift.id);
    return gift.id; // Store this - needed for the claim step
  }

  // Example usage
  const giftId = await createGift('cmp_12345', {
    firstname: 'John',
    lastname: 'Smith',
    email: 'john@example.com',
    key: 'john-smith-onboarding-swag'
  });
  ```

  ```python Python theme={null}
  def create_gift(campaign_id, recipient):
      response = session.post(f'{BASE_URL}/gifts', json={
          'campaignId': campaign_id,
          'recipients': [
              {
                  'firstname': recipient['firstname'],
                  'lastname': recipient['lastname'],
                  'email': recipient['email'],
                  'key': recipient['key']
              }
          ]
      })
      response.raise_for_status()
      gift = response.json()['results'][0]
      print(f"Gift created: {gift['id']}")
      return gift['id']  # Store this - needed for the claim step

  # Example usage
  gift_id = create_gift('cmp_12345', {
      'firstname': 'John',
      'lastname': 'Smith',
      'email': 'john@example.com',
      'key': 'john-smith-onboarding-swag'
  })
  ```

  ```json JSON theme={null}
  {
    "campaignId": "cmp_12345",
    "recipients": [
      {
        "firstname": "John",
        "lastname": "Smith",
        "email": "john@example.com",
        "key": "john-smith-onboarding-swag"
      }
    ]
  }
  ```
</CodeGroup>

A successful response returns the Gift object including the <kbd>giftId</kbd>. Note the <kbd>giftId</kbd> - you will need it in the next step.

<Note>
  In this approach the Gift is created without a variant or shipping address. The recipient will not receive a notification - the Claim step immediately completes the order.
</Note>

Include a unique <kbd>key</kbd> for every recipient to prevent duplicate gifts. See [Duplicate Detection](/pages/duplicate-gifts-detection).

***

### **Step 5: Claim the Gift**

Call <kbd>POST /gifts//claim</kbd> with the selected Variant ID and the recipient's shipping address. This finalizes the Gift and places the Order immediately.

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function claimGift(giftId, variantId, shippingAddress) {
    const response = await snappyClient.post(`/gifts/{giftId}/claim`, {
      variantId: variantId,
      shippingAddress: shippingAddress
    });

    const order = response.data;
    console.log('Order placed:', order.id);
    console.log('Order status:', order.status);
    return order;
  }

  // Example usage
  claimGift(giftId, 'var_001', {
    line1: '123 Main Street',
    city: 'New York',
    state: 'NY',
    zip: '10001',
    country: 'US'
  });
  ```

  ```python Python theme={null}
  def claim_gift(gift_id, variant_id, shipping_address):
      response = session.post(f'{BASE_URL}/gifts/{gift_id}/claim', json={
          'variantId': variant_id,
          'shippingAddress': shipping_address
      })
      response.raise_for_status()
      order = response.json()
      print(f"Order placed: {order['id']}")
      print(f"Order status: {order['status']}")
      return order

  # Example usage
  claim_gift(gift_id, 'var_001', {
      'line1': '123 Main Street',
      'city': 'New York',
      'state': 'NY',
      'zip': '10001',
      'country': 'US'
  })
  ```

  ```json JSON theme={null}
  {
    "variantId": "var_001",
    "shippingAddress": {
      "line1": "123 Main Street",
      "city": "New York",
      "state": "NY",
      "zip": "10001",
      "country": "US"
    }
  }
  ```
</CodeGroup>

***

### **Step 6: Track Fulfillment via Webhooks**

Listen for Webhook events to track fulfillment progress. Key events to handle:

| **Event**                                                                  | **Meaning**                                        |
| :------------------------------------------------------------------------- | :------------------------------------------------- |
| <kbd>gift-delivery-status-changed</kbd> (status: <kbd>orderReceived</kbd>) | Order has been placed with the fulfillment partner |
| <kbd>gift-delivery-status-changed</kbd> (status: <kbd>inTransit</kbd>)     | Product has shipped                                |
| <kbd>gift-delivery-status-changed</kbd> (status: <kbd>delivered</kbd>)     | Product has reached its final destination          |

<CodeGroup>
  ```javascript JavaScript theme={null}
  const express = require('express');
  const app = express();
  app.use(express.json());

  app.post('/webhooks/snappy', (req, res) => {
    const { webhookData, eventData } = req.body;

    if (webhookData.eventType === 'gift-delivery-status-changed') {
      const { giftId, deliveryStatus } = eventData;
      console.log(`Delivery update for gift ${giftId}: ${deliveryStatus}`);

      switch (deliveryStatus) {
        case 'orderReceived':
          console.log('Order confirmed with fulfillment partner');
          break;
        case 'inTransit':
          console.log('Order shipped');
          break;
        case 'delivered':
          console.log('Order delivered successfully');
          break;
      }
    }

    if (webhookData.eventType === 'order-canceled') {
      console.log(`Order canceled for gift ${eventData.giftId}`);
      console.log('Reason:', eventData.cancelReason);
      // Handle cancellation - notify sender, offer replacement etc.
    }

    if (webhookData.eventType === 'order-out-of-stock') {
      console.log(`Out of stock for gift ${eventData.giftId}`);
      // Handle out of stock - offer alternative variant etc.
    }

    res.status(200).send('OK');
  });

  app.listen(3000, () => console.log('Webhook listener running on port 3000'));
  ```

  ```python Python theme={null}
  from flask import Flask, request, jsonify

  app = Flask(__name__)

  @app.route('/webhooks/snappy', methods=['POST'])
  def handle_webhook():
      payload = request.json
      webhook_data = payload['webhookData']
      event_data = payload['eventData']
      event_type = webhook_data['eventType']

      if event_type == 'gift-delivery-status-changed':
          gift_id = event_data['giftId']
          delivery_status = event_data['deliveryStatus']
          print(f"Delivery update for gift {gift_id}: {delivery_status}")

          if delivery_status == 'orderReceived':
              print('Order confirmed with fulfillment partner')
          elif delivery_status == 'inTransit':
              print('Order shipped')
          elif delivery_status == 'delivered':
              print('Order delivered successfully')

      elif event_type == 'order-canceled':
          print(f"Order canceled for gift {event_data['giftId']}")
          print(f"Reason: {event_data.get('cancelReason')}")
          # Handle cancellation - notify sender, offer replacement etc.

      elif event_type == 'order-out-of-stock':
          print(f"Out of stock for gift {event_data['giftId']}")
          # Handle out of stock - offer alternative variant etc.

      return jsonify({'status': 'ok'}), 200

  if __name__ == '__main__':
      app.run(port=3000)
  ```
</CodeGroup>

→ Configure your webhook listener - see [Webhooks: Overview & Setup](/pages/overview-and-setup).

***

### **Full Flow Summary**

1. <kbd>GET /campaigns</kbd> → retrieve your campaign ID
2. <kbd>GET /products</kbd> → retrieve catalog to display in your platform
3. \[User selects item, variant, and provides shipping address]
4. <kbd>POST /gifts</kbd> → create the gift, get back the giftId
5. <kbd>POST /gifts/claim</kbd> → claim the gift with variant and shipping address
6. Webhooks → track order fulfillment and delivery
