"
```
# Platform API Overview
Source: https://docs.fourthwall.com/guides/overview
RESTful API for managing your Fourthwall shop programmatically
The Fourthwall Platform API is a RESTful API that allows you to manage various aspects of your shop.
It can be accessed either directly using your own API key (this gives you full access to your own shop) or indirectly through OAuth for multi-shop apps.
## OpenAPI Specs
Full OpenAPI specifications are available in both JSON and YAML formats:
| Spec | JSON | YAML |
| -------------- | ------------------------------------------------- | ------------------------------------------------- |
| Platform API | [open-api.json](/open-api-docs/open-api.json) | [open-api.yaml](/open-api-docs/open-api.yaml) |
| Storefront API | [storefront.json](/open-api-docs/storefront.json) | [storefront.yaml](/open-api-docs/storefront.yaml) |
Use these to generate client SDKs, import into tools like Postman, or integrate with any OpenAPI-compatible tooling.
## Authentication Methods
Direct shop access with Basic Auth credentials
Multi-shop app authentication with access tokens
# Rate Limiting
Source: https://docs.fourthwall.com/guides/rate-limiting
API rate limits and best practices for handling rate limit errors
To ensure fair usage and maintain optimal system performance, we have implemented rate limiting for API requests. Each user is allowed a maximum of **100 requests** within a **10-second rolling window**.
## What this means for you
* You can make up to 100 requests within any 10-second period
* If you exceed this limit, any additional requests will be delayed until the 10-second window is reset
Plan your API usage accordingly to avoid interruptions and ensure the smooth functioning of our services.
# Examples
Source: https://docs.fourthwall.com/html/examples
Complete working examples of Custom Code sections
These examples demonstrate real-world patterns for building Custom Code sections. Each example is a complete, working section you can use as a starting point.
## Featured collection
A product grid that displays products from a specific collection with a heading and "View all" link.
```liquid theme={null}
{%- assign collection = collections['all'] -%}
```
***
## Membership tiers
A responsive grid displaying all membership tiers with pricing, features, and signup links.
```liquid theme={null}
Membership Tiers
{% for tier in membership_tiers %}
{% if tier.type == 'Free' %}
{% if tier.image_url %}

{% endif %}
{{ tier.name }}
Free
Join now
{% if tier.perks.size > 0 %}
{% for perk in tier.perks %}
- {{ perk }}
{% endfor %}
{% endif %}
{% elsif tier.type == 'Paid' %}
{% if tier.image %}

{% endif %}
{{ tier.name }}
{% if tier.compare_at_monthly_price %}
{{ tier.monthly_price | money }}
{{ tier.compare_at_monthly_price | money }} first month
{% else %}
{{ tier.monthly_price | money }} / month
{% endif %}
{% if tier.trial_enabled %}
Start free trial
{% else %}
Join now
{% endif %}
{% if tier.annual_price %}
Save {{ tier.annual_discount_percentage }}% annually
{% endif %}
{% if tier.features.size > 0 %}
{% for feature in tier.features %}
- {{ feature }}
{% endfor %}
{% endif %}
{% endif %}
{% endfor %}
```
***
## Image with text
A two-column layout combining an image with text content and a call-to-action button.
```liquid theme={null}
{%- assign collection = collections['featured'] -%}
{%- assign product = collection.products | first -%}
{% if product.featured_image %}
{% endif %}
```
# Array & String Filters
Source: https://docs.fourthwall.com/html/filters/array-string
Filter arrays and transform strings
## Array filters
In addition to standard Liquid array filters (`first`, `last`, `size`, `sort`, `map`, `join`, etc.):
| Filter | Description | Example |
| ------- | -------------------------------- | -------------------------------------------- |
| `where` | Filter by property value | `{{ products \| where: 'available', true }}` |
| `limit` | Return first N items | `{% for p in products \| limit: 4 %}` |
| `push` | Add item to array (non-mutating) | `{{ array \| push: item }}` |
## String filters
In addition to standard Liquid string filters (`upcase`, `downcase`, `capitalize`, `strip`, etc.):
| Filter | Description | Example |
| ---------------------- | ------------------------ | --------------------------------------------- |
| `camelize` | Convert to CamelCase | `{{ 'my-class' \| camelize }}` → `MyClass` |
| `handle` / `handleize` | Convert to URL-safe slug | `{{ 'My Title!' \| handleize }}` → `my-title` |
# Image Filters
Source: https://docs.fourthwall.com/html/filters/image
Generate optimized image URLs with predefined sizes
## img\_url
Generate optimized image URLs using predefined size shortcuts.
```liquid theme={null}
{{ product.featured_image | img_url: 'large' }}
```
### Available sizes
| Shortcode | Size (px) |
| --------- | --------- |
| `pico` | 96 |
| `icon` | 128 |
| `thumb` | 160 |
| `small` | 220 |
| `medium` | 320 |
| `large` | 480 |
| `grande` | 600 |
| `display` | 720 |
| `jumbo` | 1024 |
| `master` | 1440 |
| `desktop` | 1920 |
| `ultra` | 3000 |
### Usage examples
```liquid theme={null}
{{ product.featured_image | img_url: 'thumb' }}
{{ product.featured_image | img_url: 'large' }}
{{ product.featured_image | img_url: 'desktop' }}
```
### Responsive images
```liquid theme={null}
```
# Money Filters
Source: https://docs.fourthwall.com/html/filters/money
Format monetary values as currency strings
Format monetary values as currency strings. All money filters accept a price value and return a formatted string using the shop's currency.
| Filter | Description | Example |
| ------------------------------ | ------------------------ | ------------------------------------------------------------- |
| `money` | Standard currency format | `{{ product.price \| money }}` → `$25.00` |
| `money_without_currency` | Hide currency symbol | `{{ product.price \| money_without_currency }}` → `25.00` |
| `money_without_cents_if_whole` | Omit cents when `.00` | `{{ product.price \| money_without_cents_if_whole }}` → `$25` |
## No-exchange money filters
These variants format the price without applying currency conversion:
| Filter | Description |
| ------------------------------------------ | ---------------------------------- |
| `money_no_exchange` | Format without currency conversion |
| `money_no_exchange_without_currency` | Without exchange, no symbol |
| `money_no_exchange_without_cents_if_whole` | Without exchange, omit `.00` |
## Money arithmetic
```liquid theme={null}
{{ product.price | money_times: 2 }}
```
# Filters
Source: https://docs.fourthwall.com/html/filters/overview
Liquid filters available in Fourthwall Custom Code sections
Filters modify the output of Liquid objects. They are placed within an output tag `{{ }}` and are separated by a pipe character `|`. All [standard Liquid filters](https://shopify.github.io/liquid/filters/) are available, plus the Fourthwall-specific filters listed below.
Format prices and currency values.
Generate localized URLs and transform strings to handles.
Generate optimized image URLs with predefined sizes.
Filter, limit, push arrays. Camelize and handleize strings.
Date formatting.
All standard Liquid filters for strings, math, arrays, and more.
# Standard Liquid Filters
Source: https://docs.fourthwall.com/html/filters/standard
All standard Liquid filters for strings, math, arrays, and more
All standard Liquid filters work as documented in the [official Liquid reference](https://shopify.github.io/liquid/filters/).
## String filters
`append`, `capitalize`, `downcase`, `upcase`, `escape`, `lstrip`, `rstrip`, `strip`, `newline_to_br`, `prepend`, `remove`, `remove_first`, `replace`, `replace_first`, `slice`, `split`, `strip_html`, `strip_newlines`, `truncate`, `truncatewords`, `url_decode`, `url_encode`
## Math filters
`abs`, `at_most`, `at_least`, `ceil`, `divided_by`, `floor`, `minus`, `modulo`, `plus`, `round`, `times`
## Array filters
`compact`, `concat`, `first`, `join`, `last`, `map`, `reverse`, `size`, `sort`, `sort_natural`, `uniq`, `where`
## Other filters
`date`, `default`
# URL Filters
Source: https://docs.fourthwall.com/html/filters/url
Generate localized URLs, asset paths, and HTML tags
| Filter | Description | Example |
| ---------------------- | -------------------------- | ---------------------------------------------------------------- |
| `localized_url` | Add locale prefix to URL | `{{ product.url \| localized_url }}` → `/en/products/my-product` |
| `url_encode` | URL-encode a string | `{{ 'hello world' \| url_encode }}` → `hello+world` |
| `url_escape` | Escape URL characters | `{{ url \| url_escape }}` |
| `handle` / `handleize` | Convert to URL-safe handle | `{{ 'My Product!' \| handle }}` → `my-product` |
# Utility Filters
Source: https://docs.fourthwall.com/html/filters/utility
Date formatting
| Filter | Description | Example |
| ------ | ---------------- | ----------------------------------------------- |
| `date` | Format date/time | `{{ product.created_at \| date: '%B %d, %Y' }}` |
## date
Formats dates using `strftime` syntax in the shop's timezone.
```liquid theme={null}
{{ product.created_at | date: '%B %d, %Y' }}
{{ product.published_at | date: '%m/%d/%Y' }}
```
# collection
Source: https://docs.fourthwall.com/html/objects/collection
Access product collections and their products
Access product collections through the global `collections` object using a collection handle.
## Accessing a collection
```liquid theme={null}
{%- assign collection = collections['all'] -%}
{% for product in collection.products limit: 8 %}
{{ product.title }}
{% endfor %}
```
## Collection properties
| Property | Type | Description |
| ---------------- | ------- | -------------------------- |
| `title` | string | Collection name |
| `handle` | string | URL-safe identifier |
| `products` | array | Products in the collection |
| `products_count` | integer | Number of products |
## Example
```liquid theme={null}
{%- assign collection = collections['all'] -%}
{{ collection.title }}
{% for product in collection.products limit: 8 %}
{% if product.featured_image %}

{% endif %}
{{ product.title }}
{{ product.price | money }}
View
{% endfor %}
```
# membership_tiers
Source: https://docs.fourthwall.com/html/objects/membership-tiers
Access membership tier information, pricing, and features
Access membership tier information through the `membership_tiers` object. This contains all configured tiers for the shop, both free and paid.
## Iterating over tiers
```liquid theme={null}
{% for tier in membership_tiers %}
{{ tier.name }}
{% if tier.type == 'Paid' %}
{{ tier.monthly_price | money }} per month
{% else %}
Free
{% endif %}
{% endfor %}
```
Each tier has a `type` property that is either `"Free"` or `"Paid"`. Use this to branch your rendering logic.
## Free tier properties
| Property | Type | Description |
| ------------------ | ------- | ----------------------------------- |
| `type` | string | Always `"Free"` |
| `name` | string | Tier display name |
| `description` | string | Tier description |
| `position` | integer | Display order |
| `image_url` | string | Tier image URL |
| `members_count` | integer | Number of members |
| `perks` | array | List of perk descriptions (strings) |
| `registration_url` | string | Signup URL |
## Paid tier properties
| Property | Type | Description |
| --------------- | ------- | -------------------------------------- |
| `type` | string | Always `"Paid"` |
| `name` | string | Tier display name |
| `description` | string | Tier description |
| `position` | integer | Display order |
| `image` | string | Tier image URL |
| `members_count` | integer | Number of members |
| `features` | array | List of feature descriptions (strings) |
| `url` | string | Tier purchase URL |
| `trial_enabled` | boolean | Whether free trial is available |
Free tiers use `image_url` for the image, while paid tiers use `image`. Free tiers list benefits as `perks`, while paid tiers use `features`.
## Paid tier pricing
| Property | Type | Description |
| ---------------------------- | ------- | -------------------------------------------------- |
| `monthly_price` | money | Monthly subscription price |
| `monthly_variant` | variant | Monthly billing variant |
| `compare_at_monthly_price` | money | Original monthly price (for first-month discounts) |
| `annual_price` | money | Annual subscription price |
| `annual_variant` | variant | Annual billing variant |
| `annual_offer_id` | string | Annual offer identifier |
| `annual_url` | string | Annual subscription URL |
| `annual_discount_enabled` | boolean | Whether annual discount is active |
| `annual_discount_percentage` | integer | Annual discount percentage |
| `compare_at_annual_price` | money | Original annual price |
### Displaying prices
```liquid theme={null}
{% if tier.compare_at_monthly_price %}
{{ tier.monthly_price | money }}
{{ tier.compare_at_monthly_price | money }} first month
{% else %}
{{ tier.monthly_price | money }} per month
{% endif %}
```
## Variant discounts
Each tier variant may have discount information for specific promotions:
```liquid theme={null}
{{ tier.monthly_variant.discounts.twitch.after_discount_price | money }}
{{ tier.monthly_variant.discounts.introductory.after_discount_price | money }}
```
### Twitch discount properties
| Property | Type | Description |
| --------------------------------------- | ----- | ------------------------------- |
| `discounts.twitch.after_discount_price` | money | Price after Twitch sub discount |
### Introductory discount properties
| Property | Type | Description |
| --------------------------------------------- | -------- | --------------------------------- |
| `discounts.introductory.started_at` | datetime | Discount start date |
| `discounts.introductory.ended_at` | datetime | Discount end date |
| `discounts.introductory.after_discount_price` | money | Price after introductory discount |
## Example: Membership tiers grid
```liquid theme={null}
{% for tier in membership_tiers %}
{% if tier.type == 'Free' %}
{% if tier.image_url %}

{% endif %}
{{ tier.name }}
{% if tier.members_count %}
{{ tier.members_count }} member{% if tier.members_count != 1 %}s{% endif %}
{% endif %}
Free
Join now
{% if tier.perks.size > 0 %}
{% for perk in tier.perks %}
- {{ perk }}
{% endfor %}
{% endif %}
{% elsif tier.type == 'Paid' %}
{% if tier.image %}

{% endif %}
{{ tier.name }}
{% if tier.compare_at_monthly_price %}
{{ tier.monthly_price | money }}
{{ tier.compare_at_monthly_price | money }} first month
{% else %}
{{ tier.monthly_price | money }} per month
{% endif %}
{% if tier.trial_enabled %}
Start 7-day free trial
{% else %}
Join now
{% endif %}
{% if tier.annual_price %}
Save {{ tier.annual_discount_percentage }}% if you pay annually
{% endif %}
{% if tier.features.size > 0 %}
{% for feature in tier.features %}
- {{ feature }}
{% endfor %}
{% endif %}
{% endif %}
{% endfor %}
```
# Available Objects
Source: https://docs.fourthwall.com/html/objects/overview
Data objects available in Fourthwall Custom Code sections
Custom Code sections have access to a specific set of objects that provide data about your shop's collections, products, and membership tiers.
## Available objects
Product collections and their contents.
Individual product data, pricing, images, and variants.
Membership tier information, pricing, and features.
## Quick overview
| Object | Description | Access |
| ------------------ | ------------------------ | -------------------------------------- |
| `collections` | All product collections | `collections[handle]` |
| `product` | Product data | Iterating over a collection's products |
| `membership_tiers` | All membership tiers | Direct global access |
| `section` | Current section instance | `section.id` |
# product
Source: https://docs.fourthwall.com/html/objects/product
Access product data including pricing, images, and variants
Product data is available when iterating over a collection's products.
```liquid theme={null}
{%- assign collection = collections['all'] -%}
{% for product in collection.products limit: 8 %}
{{ product.title }} — {{ product.price | money }}
{% endfor %}
```
## Product types
Every product has a `type` field that determines which properties are available. The available types are:
| Type | Description |
| ---------- | ------------------------------------------- |
| `Standard` | Regular products with variants and options |
| `Combined` | Combined products with variants and options |
| `GiftCard` | Gift card products |
| `Bundle` | A bundle of multiple products sold together |
Bundle products have a different set of properties than Standard, Combined, and GiftCard products. Properties marked with specific type availability are only present on those product types.
## Common properties
These properties are available on **all** product types.
| Property | Type | Description |
| ------------------------- | ------- | -------------------------------------------------------------------------- |
| `title` | string | Product name |
| `handle` | string | URL-safe identifier |
| `description` | string | Full product description (HTML) |
| `url` | string | Product page URL |
| `available` | boolean | Whether the product is in stock |
| `type` | string | Product type (`Standard`, `Combined`, `GiftCard`, `Bundle`) |
| `featured_image` | image | Main product image |
| `images` | array | All product images |
| `price` | money | Current price (default variant for Single types, bundle price for Bundles) |
| `compare_at_price` | money | Original price (for sales) |
| `promoted_price` | money | Promotional price (if active) |
| `members_only` | boolean | Whether product requires membership |
| `available_for_all_tiers` | boolean | Available to all membership tiers |
| `required_tiers` | array | Tiers required to purchase |
| `gift` | boolean | Whether the product is a gift |
| `metafields` | object | Custom metafields |
| `promotion_badges` | array | Active promotion badges |
| `promotion_description` | string | Promotion description text |
| `promotion_descriptions` | array | All promotion descriptions |
## Standard, Combined, and GiftCard properties
These properties are only available when `product.type` is `Standard`, `Combined`, or `GiftCard`.
### Variants
| Property | Type | Description |
| --------------------- | ------- | ---------------------------------------- |
| `variants` | array | All product variants |
| `options` | array | Option names (e.g., `["Size", "Color"]`) |
| `options_with_values` | array | Options with their possible values |
| `default_variant` | variant | The default variant |
Each variant object has:
| Property | Type | Description |
| ------------------ | ------- | --------------------------- |
| `price` | money | Variant price |
| `compare_at_price` | money | Original price (for sales) |
| `available` | boolean | Whether variant is in stock |
| `option1` | string | First option value |
| `option2` | string | Second option value |
| `option3` | string | Third option value |
| `image` | image | Variant-specific image |
| `images` | array | All variant images |
### Additional pricing
| Property | Type | Description |
| ----------- | ----- | -------------------- |
| `price_min` | money | Lowest variant price |
### Other
| Property | Type | Description |
| ------------- | -------- | ---------------------------------- |
| `collections` | array | Collections the product belongs to |
| `updated_at` | datetime | When the product was last updated |
## Bundle properties
These properties are only available when `product.type` is `Bundle`.
| Property | Type | Description |
| ------------------ | ------ | ---------------------------------------------------------------------------------------------------- |
| `offers` | array | The individual products included in the bundle (each is a Standard/Combined/GiftCard product object) |
| `pricing_strategy` | object | How the bundle is priced |
### Pricing strategy
The `pricing_strategy.strategy` object has a `type` field indicating the pricing model:
| Strategy type | Description | Additional fields |
| -------------------- | ------------------------------------------------ | ------------------------------------------------- |
| `FIXED_PRICE` | Bundle has a fixed price | `strategy.price.value`, `strategy.price.currency` |
| `DISCOUNT_BASED` | Bundle applies a percentage discount | `strategy.discount_percentage` |
| `SAME_AS_INDIVIDUAL` | Bundle price equals the sum of individual prices | — |
```liquid theme={null}
{% if product.type == 'Bundle' %}
{% assign strategy = product.pricing_strategy.strategy %}
{% if strategy.type == 'DISCOUNT_BASED' %}
{{ strategy.discount_percentage }}% off when bought together
{% endif %}
Includes:
{% for offer in product.offers %}
- {{ offer.title }} — {{ offer.price | money }}
{% endfor %}
{% endif %}
```
## Images
### Image object properties
Each image object has:
| Property | Type | Description |
| -------------- | ------- | -------------------- |
| `url` | string | Image URL |
| `alt` | string | Alt text |
| `width` | integer | Width in pixels |
| `height` | integer | Height in pixels |
| `aspect_ratio` | float | Width / height ratio |
Use the [`img_url` filter](/html/filters/image) to generate optimized image URLs:
```liquid theme={null}
{{ product.featured_image | img_url: 'large' }}
```
## Formatting prices
Use [money filters](/html/filters/money) to format prices:
```liquid theme={null}
{{ product.price | money }}
{{ product.compare_at_price | money }}
```
## Membership restrictions
```liquid theme={null}
{% if product.members_only %}
{% if product.available_for_all_tiers %}
Available for all members
{% else %}
Available for:
{% for tier in product.required_tiers %}
{{ tier.name }}{% unless forloop.last %}, {% endunless %}
{% endfor %}
{% endif %}
{% endif %}
```
## Promotions
Each badge has `type` and `label` properties:
```liquid theme={null}
{% if product.promotion_badges %}
{% for badge in product.promotion_badges %}
{{ badge.label }}
{% endfor %}
{% endif %}
```
## Example: Product card
```liquid theme={null}
{% if product.featured_image %}

{% endif %}
{% if product.promotion_badges %}
{% for badge in product.promotion_badges %}
{{ badge.label }}
{% endfor %}
{% endif %}
{{ product.title }}
{% if product.compare_at_price %}
{{ product.compare_at_price | money }}
{% endif %}
{{ product.price | money }}
{% if product.type == 'Bundle' %}
{{ product.offers.size }} items in this bundle
{% endif %}
{% if product.available %}
View product
{% else %}
Out of stock
{% endif %}
```
# Custom Code Sections
Source: https://docs.fourthwall.com/html/overview
Build custom storefront sections using HTML with Liquid templating
Custom Code sections allow you to build fully custom sections for your Fourthwall shop using HTML combined with [Liquid](https://shopify.github.io/liquid/) templating. You can add these sections to your shop through the theme editor.
## What is Liquid?
Liquid is an open-source template language. It uses a combination of **objects**, **tags**, and **filters** to load dynamic content into templates.
```liquid theme={null}
{{ product.title }}
{% if product.available %}
In stock!
{% endif %}
{{ product.price | money }}
```
## How it works
When you add a Custom Code section to your shop, you write an `.html` file that contains three parts:
1. **Styles** — CSS scoped to the section
2. **Markup** — HTML combined with Liquid logic
3. **Script** — JavaScript scoped to the section instance
## Available objects
In a Custom Code section, you have access to:
| Object | Description |
| ------------------ | ------------------------------------------------------ |
| `collections` | All product collections in the shop |
| `product` | Individual product data (accessed through collections) |
| `membership_tiers` | Membership tier information |
| `section` | The current section instance |
## Liquid reference guide
For comprehensive Liquid language documentation, refer to this resource:
* [Liquid template language](https://shopify.github.io/liquid/) — Official open-source Liquid documentation with basics, tags, and filters
All standard Liquid features documented in this guide work in Fourthwall, including:
* **Tags**: `if`/`elsif`/`else`, `unless`, `case`/`when`, `for`, `assign`, `capture`, `increment`, `raw`, `comment`
* **Filters**: `append`, `capitalize`, `downcase`, `upcase`, `replace`, `split`, `strip`, `truncate`, `size`, `sort`, `map`, `where`, `first`, `last`, `join`, `plus`, `minus`, `times`, `divided_by`, `modulo`, `round`, `abs`, `date`, and more
## Next steps
Learn how to structure a section with styles, markup, and script.
Explore the data objects available in your templates.
See all filters for formatting data.
Complete working examples of HTML sections.
# Section Structure
Source: https://docs.fourthwall.com/html/section-structure
How to structure a Custom Code section with styles, markup, and script
Every Custom Code section consists of three parts: **styles**, **markup**, and **script**. Together, these give you full control over appearance, content, and behavior.
## Basic structure
```liquid theme={null}
My Custom Section
{%- assign collection = collections['all'] -%}
{% for product in collection.products limit: 4 %}
{{ product.title }}
{{ product.price | money }}
{% endfor %}
```
## Styles
Place your CSS inside a `
```
Always use `section.id` in your CSS selectors. Without it, styles from one section instance will leak into others.
## Markup
The markup section contains HTML mixed with Liquid tags and objects. This is where you define the visual structure and dynamic content of your section.
```liquid theme={null}
Featured Products
{%- assign collection = collections['all'] -%}
{% for product in collection.products limit: 8 %}
{{ product.title }}
{{ product.price | money }}
View product
{% endfor %}
```
### Key objects in markup
| Object | Description |
| ------------------ | -------------------------------------------- |
| `section` | The current section instance |
| `section.id` | Unique identifier for this section instance |
| `collections` | All product collections (accessed by handle) |
| `membership_tiers` | All membership tiers |
## Script
Place your JavaScript inside a `
```
Always scope DOM queries to the section instance. Using `document.querySelector('.toggle')` without a section root will match elements across every instance of the section on the page.
## Best practices
### Scope CSS with section.id
Always include `section.id` in class names to prevent style conflicts:
```liquid theme={null}
```
### Use responsive design
Apply mobile-first styles with media queries:
```liquid theme={null}
```
### Check for blank values
Always check if values exist before rendering:
```liquid theme={null}
{% if product.featured_image %}
{% endif %}
{% if product.compare_at_price %}
{{ product.compare_at_price | money }}
{% endif %}
```
### Limit product iterations
Always use the `limit` filter when iterating over product collections to control performance:
```liquid theme={null}
{%- assign collection = collections['all'] -%}
{% for product in collection.products limit: 8 %}
{{ product.title }}
{% endfor %}
```
# Introduction
Source: https://docs.fourthwall.com/index
Build integrations with the Fourthwall platform
Welcome to the Fourthwall developer documentation. Build custom integrations, automate your shop, or create apps for the Fourthwall ecosystem.
## What can you build?
Manage orders, products, memberships, and more with full API access.
Build custom storefronts with public shop and product data.
Receive real-time notifications for orders, subscriptions, and events.
Create apps that integrate directly into creator dashboards.
## Ready to start?
Get your API key and make your first API call in minutes.
# Quickstart
Source: https://docs.fourthwall.com/quickstart
Make your first API call in minutes
Get up and running with the Fourthwall API in three steps.
## 1. Get your API credentials
Creating API credentials requires the **SUPER ADMIN** role.
1. Go to [Settings > For Developers](https://my-shop.fourthwall.com/admin/dashboard/settings/for-developers?redirect)
2. Under "Open API", click **Create API User**
3. Save the username and password that are generated
## 2. Make your first API call
Test your credentials by fetching your shop details. The Fourthwall API uses **Basic Authentication** - simply pass your username and password with each request.
```bash cURL theme={null}
# Basic Auth - the simplest way to authenticate
curl -u "your_username:your_password" \
https://api.fourthwall.com/open-api/v1.0/shops/current
```
```javascript JavaScript theme={null}
const username = "your_username";
const password = "your_password";
// Basic Auth: base64 encode "username:password"
const credentials = btoa(`${username}:${password}`);
fetch("https://api.fourthwall.com/open-api/v1.0/shops/current", {
headers: {
"Authorization": `Basic ${credentials}`
}
})
.then(res => res.json())
.then(data => console.log(data));
```
```python Python theme={null}
import requests
from requests.auth import HTTPBasicAuth
# Basic Auth is built into the requests library
response = requests.get(
"https://api.fourthwall.com/open-api/v1.0/shops/current",
auth=HTTPBasicAuth("your_username", "your_password")
)
print(response.json())
```
The `-u` flag in cURL automatically handles Basic Auth encoding for you. Under the hood, it creates an `Authorization: Basic ` header.
You should receive a response with your shop details:
```json theme={null}
{
"id": "sh_abc123",
"name": "My Shop",
"currency": "USD",
...
}
```
## 3. Explore the API
Now that you're authenticated, explore what you can build:
Fetch orders from your shop
Get your product catalog
Get real-time event notifications
Browse all available endpoints
# Community
Source: https://docs.fourthwall.com/resources/community
Join the Fourthwall developer community
Come chat with us on [Discord](https://discord.com/invite/kc6P68z3Uz)!
## Useful Channels
* **#developers-general** - General developer discussion and questions
* **#creator-resources** - Resources and tools for creators
# Storefront Showcase
Source: https://docs.fourthwall.com/resources/showcase
Example custom storefronts built with Fourthwall
Check out these example storefronts built with the Fourthwall Storefront API:
## Featured Storefronts
Official Next.js starter kit for building custom storefronts
## Build Your Own
Ready to build your own custom storefront? Get started with our [Storefront API documentation](/storefront/overview).
# Cart Checkout Endpoint
Source: https://docs.fourthwall.com/shop-apis/cart-checkout-endpoint
Full reference for /cart/checkout?products=... direct checkout links
Use this endpoint to send a shopper directly into Fourthwall checkout from a URL.
It is especially useful when you want to pass product variants directly (`products=...`) from ads, feeds, or custom links.
Base URL:
```text theme={null}
https://{shop_domain}/cart/checkout
```
Recommended direct-products example:
```text theme={null}
https://{shop_domain}/cart/checkout?products=11111111-1111-1111-1111-111111111111:1,22222222-2222-2222-2222-222222222222:2¤cy=USD
```
If you already have a Storefront API cart, `cartId` is also supported:
```text theme={null}
https://{shop_domain}/cart/checkout?cartId=22222222-2222-2222-2222-222222222222¤cy=USD
```
## Request
* Method: `GET`
* Path: `/cart/checkout`
* Intended use: Browser redirect links (ads, email, social, product feeds, etc.)
* Use the root path (`/cart/checkout`), not a locale-prefixed path.
## Query Parameters
| Param | Required | Description |
| --------------------------------------------------------- | -------- | -------------------------------------------------------------------------- |
| `products` | No | Comma-separated list of cart items in `variantId[:quantity]` format. |
| `cartId` | No | Existing cart ID to checkout. If present and valid, `products` is ignored. |
| `coupon` | No | Promo code to apply. |
| `currency` | No | Checkout currency (for example `USD`). |
| `cart_origin` | No | Cart origin metadata. |
| `utm_*`, `_ga`, `_fbp`, `_fbc`, `gclid`, `fbclid`, `FPID` | No | Marketing params are captured and propagated in checkout metadata. |
## `products` Format
Format:
```text theme={null}
products=variantId[:quantity],variantId[:quantity],...
```
Rules:
* `variantId` must be a variant UUID (not product/offer ID).
* `quantity` is optional; default is `1`.
* Invalid quantity values fall back to `1`.
* Entries with too many separators (like `id:1:extra`) are skipped.
* URL-encoded input is supported.
* If any entry has an invalid variant UUID, parsing fails and `products` becomes empty.
Examples:
```text theme={null}
?products=11111111-1111-1111-1111-111111111111
?products=11111111-1111-1111-1111-111111111111:2
?products=11111111-1111-1111-1111-111111111111:1,22222222-2222-2222-2222-222222222222:3
```
## Behavior
Checkout creation flow:
1. If `products` contains valid variant entries, the endpoint can create/update a cart from those products.
2. If `cartId` is provided and valid, checkout uses that cart and ignores `products`.
3. If both are unavailable (no valid `cartId` and no valid `products`), redirect goes to `/`.
4. On success, a checkout is created/updated and the shopper is redirected to `/checkout/{checkoutId}`.
Promo code precedence:
1. `coupon` query param
2. Manually-applied promo already on cart
3. Automatically applied promo code from a shared link
Currency precedence:
1. `currency` query param
2. Session currency
3. `USD`
## Response
This endpoint responds with redirects:
* Success: `303 See Other` -> `/checkout/{checkoutId}`
* Empty/invalid input: `303 See Other` -> `/`
* Exception path: `303 See Other` -> `/?error_message=...`
## Google Merchant Center Template
This endpoint supports the Google template pattern:
```text theme={null}
https://{shop_domain}/cart/checkout?products={id}:1
```
## Example Integration
Direct variant handoff (primary):
```javascript theme={null}
function redirectToDirectCheckout(shopDomain, variantId, quantity = 1, coupon) {
const params = new URLSearchParams({
products: `${variantId}:${quantity}`
});
if (coupon) params.set("coupon", coupon);
window.location.href = `https://${shopDomain}/cart/checkout?${params.toString()}`;
}
```
Cart ID handoff (also supported):
```javascript theme={null}
function redirectCartToCheckout(shopDomain, cartId, currency = "USD") {
const params = new URLSearchParams({
cartId,
currency
});
window.location.href = `https://${shopDomain}/cart/checkout?${params.toString()}`;
}
```
# Shop Feeds
Source: https://docs.fourthwall.com/shop-apis/shop-feeds
RSS and JSON product feeds for shop integration
## Products RSS feed
If you're just looking to read public product information, you do not need to use the Open API. All sites publish a Merchant Center Feed under an RSS address:
```
${shop_url}/.well-known/merchant-center/rss.xml
```
**Example:** for [https://shop.fourthwall.com](https://shop.fourthwall.com) the RSS feed URL would be [https://shop.fourthwall.com/.well-known/merchant-center/rss.xml](https://shop.fourthwall.com/.well-known/merchant-center/rss.xml)
## Collections JSON feed
A JSON version of the products in a collection.
```
${shop_url}/collections/{slug}.json
```
**Example:** [https://shop.fourthwall.com/collections/all.json](https://shop.fourthwall.com/collections/all.json)
**Paginated:** [https://shop.fourthwall.com/collections/all/2.json](https://shop.fourthwall.com/collections/all/2.json)
# Architecture
Source: https://docs.fourthwall.com/storefront/architecture
How the Storefront API connects your frontend to Fourthwall
## How It All Connects
Here's the complete flow from browsing to purchase:
```mermaid theme={null}
flowchart TB
subgraph Your_Frontend["Your Frontend"]
A[Browse Products]
B[Manage Cart]
end
subgraph Storefront_API["Storefront API"]
C["/collections/{slug}/products"]
D["/products/{slug}"]
E["/carts"]
end
subgraph Fourthwall["Fourthwall"]
F[Checkout Page]
G[Order Complete]
end
A -->|"Fetch"| C
A -->|"Fetch"| D
B -->|"Create/Update"| E
B -->|"Redirect with cart_id"| F
F -->|"Payment processed"| G
G -->|"Return to your site"| Your_Frontend
```
**The journey:**
1. **Browse**: Your frontend fetches products and collections from the Storefront API
2. **Cart**: Customer adds items, you manage the cart via API
3. **Checkout**: Redirect to Fourthwall checkout with the cart ID
4. **Complete**: Fourthwall handles payment, then returns customer to your site
## Deployment Options
If you build your storefront on the Fourthwall shop editor, your shop is served directly from our servers to your customers.
```mermaid theme={null}
flowchart TB
A["Fourthwall.com
shops"]
B["Customer"]
A -->|"<html>"| B
```
While this is a great way to get started, it does have some limitations. To build a truly custom experience, you can build your own frontend and use the Storefront API to pull in products and handle the cart process.
### With a proxy
The most straight-forward way to build a custom storefront is through a proxy server. We've optimized for a setup through Vercel, though other services like Netlify would work as well.
```mermaid theme={null}
flowchart TB
A["Fourthwall
storefront api"]
B["Your site
vercel"]
C["Customer"]
A -->|"API calls"| B
B -->|"API calls"| A
B -->|"<html>"| C
```
In this setup, your frontend application will make API calls to the Vercel backend. The Vercel backend acts as a proxy that will then make calls to the Fourthwall API. Your Vercel application can contain whatever code you want, allowing for a fully customizable storefront.
### Separate frontend
It is also possible to build your custom site as a static frontend only site. In this setup, the static site will make API calls directly to the Fourthwall API.
```mermaid theme={null}
graph LR
A["Your site"]
B["Fourthwall
storefront api"]
C["Customer"]
B -->|"API calls"| A
A -->|"<html>"| C
```
# Building a Cart & Checkout
Source: https://docs.fourthwall.com/storefront/cart-checkout-tutorial
End-to-end guide for implementing shopping cart and checkout
This guide walks through the complete flow from adding items to a cart through redirecting to checkout.
## Overview
```mermaid theme={null}
flowchart LR
A[Create Cart] --> B[Add Items]
B --> C[Redirect to Checkout]
C --> D[Customer Completes Purchase]
```
## Step 1: Create a Cart
First, create an empty cart. You'll get back a `cart_id` that you'll use for all subsequent operations.
```javascript theme={null}
const STOREFRONT_TOKEN = "your_storefront_token";
const API_BASE = "https://storefront-api.fourthwall.com/v1";
async function createCart() {
const res = await fetch(`${API_BASE}/carts?storefront_token=${STOREFRONT_TOKEN}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ currency: "USD" })
});
return res.json();
}
const cart = await createCart();
console.log(cart.id); // e.g., "22222222-2222-2222-2222-222222222222"
```
Store the `cart_id` in localStorage or a cookie so it persists across page refreshes.
## Step 2: Add Items to Cart
Add products to the cart using a variant ID. You can get variant IDs from the [product endpoints](/storefront/products).
```javascript theme={null}
async function addToCart(cartId, variantId, quantity = 1) {
const res = await fetch(
`${API_BASE}/carts/${cartId}/items?storefront_token=${STOREFRONT_TOKEN}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ variantId, quantity })
}
);
return res.json();
}
// Add a product to the cart
await addToCart(cart.id, "var_xyz789", 2);
```
## Step 3: View Cart Contents
Fetch the current cart to display items to the user:
```javascript theme={null}
async function getCart(cartId) {
const res = await fetch(
`${API_BASE}/carts/${cartId}?storefront_token=${STOREFRONT_TOKEN}`
);
return res.json();
}
const currentCart = await getCart(cart.id);
console.log(currentCart.items); // Array of cart items
console.log(currentCart.totals); // Price totals
```
## Step 4: Redirect to Checkout
When the customer is ready to purchase, redirect them to the Fourthwall checkout page using the cart ID:
```javascript theme={null}
function redirectToCheckout(cartId, currency = "USD") {
const checkoutDomain = "your-shop.fourthwall.com"; // Your checkout domain
const params = new URLSearchParams({ cartCurrency: currency, cartId });
const checkoutUrl = `https://${checkoutDomain}/checkout/?${params.toString()}`;
window.location.href = checkoutUrl;
}
// When user clicks "Checkout" button
redirectToCheckout(cart.id, "USD");
```
The checkout URL format is:
```
https://{checkout_domain}/checkout/?cartCurrency={currency}&cartId={cart_id}
```
Make sure to use your shop's checkout domain, not `storefront-api.fourthwall.com`.
### Alternative: Domain cart checkout URL
You can also use the domain cart-checkout entrypoint:
```text theme={null}
https://{shop_domain}/cart/checkout?cartId={cart_id}¤cy={currency}
```
### Alternative: Direct product checkout URL
If you want to skip cart creation and redirect from variant IDs directly, use:
```text theme={null}
https://{shop_domain}/cart/checkout?products={variant_id}:1
```
For multiple variants:
```text theme={null}
https://{shop_domain}/cart/checkout?products={variant_id_1}:1,{variant_id_2}:2
```
`quantity` is optional and defaults to `1`.
For full behavior details, see [Cart Checkout Endpoint](/shop-apis/cart-checkout-endpoint).
## Complete Example
Here's a complete implementation you can adapt:
```javascript theme={null}
const STOREFRONT_TOKEN = "your_storefront_token";
const API_BASE = "https://storefront-api.fourthwall.com/v1";
const CHECKOUT_DOMAIN = "your-shop.fourthwall.com";
// Cart state
let cartId = localStorage.getItem("cartId");
async function ensureCart() {
if (cartId) {
// Verify cart still exists
const res = await fetch(
`${API_BASE}/carts/${cartId}?storefront_token=${STOREFRONT_TOKEN}`
);
if (res.ok) return cartId;
}
// Create new cart
const res = await fetch(`${API_BASE}/carts?storefront_token=${STOREFRONT_TOKEN}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ currency: "USD" })
});
const cart = await res.json();
cartId = cart.id;
localStorage.setItem("cartId", cartId);
return cartId;
}
async function addToCart(variantId, quantity = 1) {
const id = await ensureCart();
const res = await fetch(
`${API_BASE}/carts/${id}/items?storefront_token=${STOREFRONT_TOKEN}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ variantId, quantity })
}
);
return res.json();
}
function checkout() {
if (!cartId) {
alert("Your cart is empty");
return;
}
const params = new URLSearchParams({ cartCurrency: "USD", cartId });
window.location.href = `https://${CHECKOUT_DOMAIN}/checkout/?${params.toString()}`;
}
```
## Next Steps
Make your checkout page match your brand
See all cart operations
# Checkout Setup
Source: https://docs.fourthwall.com/storefront/checkout
Configure Fourthwall checkout pages for custom storefronts
When the user is ready to checkout, you will redirect them to the Fourthwall checkout page. This is where they can review their order and enter their shipping and payment information.
It's best to style the checkout page so that it matches the rest of your store. This will help create a seamless shopping experience for your customers.
We recommend using a simple theme like Clean Frame and then setting the styling (colors + typography) so that it matches your main store. You can [style your checkout process here](https://my-shop.fourthwall.com/admin/dashboard/store-design/layout/index/?redirect).
You will also want to connect a custom domain like `checkout..com` to the checkout page so that the redirect from your main store is seamless. You can connect custom domains [here](https://my-shop.fourthwall.com/admin/dashboard/settings/domain/?redirect).
In the Vercel starter kit, you can add the checkout domain (custom or otherwise) to `env.local` under `NEXT_PUBLIC_FW_CHECKOUT`.
If you are building your own frontend, redirect to:
```
https:///checkout/?cartCurrency=&cartId=
```
Alternative domain entrypoint:
```
https:///cart/checkout?cartId=¤cy=
```
For `/cart/checkout` details (including direct product links like `products=variantId:quantity`), see [Cart Checkout Endpoint](/shop-apis/cart-checkout-endpoint).
## Setting up a meta redirect (recommended)
The emails sent from Fourthwall can sometimes include a link to the Fourthwall (checkout) page. When a user clicks on this link, you will want to redirect them to your domain.
You can add a redirect script to your `Head` component [here](https://my-shop.fourthwall.com/admin/dashboard/store-design/general/advanced/?redirect).
```html theme={null}
```
# Collection Handles
Source: https://docs.fourthwall.com/storefront/collection
How to get a collection handle for the Storefront API
The Storefront collection APIs use the collection handles (slugs) to identify the collection you want to display. It defaults to the "all" collection, but you can specify a different collection by providing the handle.
Looking to fetch products? See [Fetching Products](/storefront/products) for the complete guide.
## The `all` Collection
The `all` collection is a special built-in collection that contains **all public products** in your shop. Use it when you want to display your entire catalog:
```javascript theme={null}
GET /v1/collections/all/products?storefront_token=YOUR_TOKEN
```
## Finding Collection Handles
Here's how to get the handle for a collection:
1. Go to the [Collections page](https://my-shop.fourthwall.com/admin/dashboard/products/collections/?redirect)
2. Click on the collection you want to get the handle for. Make sure this collection is public.
3. The handle is the last part of the copyable URL.
# Getting Started
Source: https://docs.fourthwall.com/storefront/getting-started
Get started with Storefront API authentication and deployment
## Authentication
You can create a Storefront token in your [developer settings](https://my-shop.fourthwall.com/admin/dashboard/settings/for-developers/?redirect).
This token will be used to authenticate your requests to the Storefront API.
## Vercel
The best way to get started is with Vercel and Next.js. Vercel is a cloud platform that makes it easy to deploy websites and applications. Next.js is the React-based framework that Vercel is built on.
You can fork our open source project to get started:
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FFourthwallHQ%2Fvercel-commerce)
Once you have forked the repo, you can follow the instructions in the README to learn how to run the project locally and deploy.
The only things you will need to configure are the default environment variables.
## Static page
If you decide to build your own frontend, you can simply make fetch requests directly to the Fourthwall API to get the information you need.
```javascript theme={null}
const res = await fetch("https://storefront-api.fourthwall.com/v1/collections?storefront_token=");
```
The full set of endpoints available to you can be found in the Storefront API reference.
# Storefront API Overview
Source: https://docs.fourthwall.com/storefront/overview
Build custom storefronts on top of Fourthwall products
The Storefront API lets you build custom storefronts on top of your Fourthwall product collections. Create a fully custom shopping experience while leveraging Fourthwall's product catalog, fulfillment, and checkout.
## Quick start
Get your storefront token from [Settings > For Developers](https://my-shop.fourthwall.com/admin/dashboard/settings/for-developers/?redirect), then fetch your collections:
```bash theme={null}
curl "https://storefront-api.fourthwall.com/v1/collections?storefront_token=YOUR_TOKEN"
```
## What you can do
* **Fetch products and collections** - Display your Fourthwall products on any website or app
* **Manage shopping carts** - Create and update carts via API
* **Checkout** - Redirect customers to Fourthwall's hosted checkout page
## Next steps
Set up authentication and make your first API call
Understand how the API connects to your frontend
# Fetching Products
Source: https://docs.fourthwall.com/storefront/products
How to fetch products using the Storefront API
The Storefront API organizes products into **collections**. There is no standalone "List Products" endpoint—instead, you fetch products through collections.
## The `all` Collection
Every shop has a built-in `all` collection that contains all public products. This is the easiest way to get all products:
```javascript theme={null}
// Get all products
const res = await fetch(
"https://storefront-api.fourthwall.com/v1/collections/all/products?storefront_token=YOUR_TOKEN"
);
const { results, paging } = await res.json();
```
## Common Patterns
### Get all products (paginated)
```javascript theme={null}
async function getAllProducts(token) {
const products = [];
let page = 0;
let hasMore = true;
while (hasMore) {
const res = await fetch(
`https://storefront-api.fourthwall.com/v1/collections/all/products?storefront_token=${token}&page=${page}&size=50`
);
const data = await res.json();
products.push(...data.results);
hasMore = data.paging.hasNextPage;
page++;
}
return products;
}
```
### Get products from a specific collection
```javascript theme={null}
const res = await fetch(
"https://storefront-api.fourthwall.com/v1/collections/merch/products?storefront_token=YOUR_TOKEN"
);
```
### Get a single product by slug
Use this when you already know the product slug (e.g., from a URL like `/products/cool-t-shirt`):
```javascript theme={null}
const res = await fetch(
"https://storefront-api.fourthwall.com/v1/products/cool-t-shirt?storefront_token=YOUR_TOKEN"
);
```
## Why Collections?
Collections let you:
* Organize products into categories (e.g., "Apparel", "Accessories")
* Control which products appear on your storefront
* Create featured or seasonal groupings
The `all` collection is always available as a catch-all.
# Managing Webhooks via API
Source: https://docs.fourthwall.com/webhooks/api-management
Programmatically create, update, and manage webhook subscriptions
In addition to setting up webhooks through the [dashboard](https://my-shop.fourthwall.com/admin/dashboard/settings/for-developers?redirect), you can programmatically create and manage webhooks using the Platform API. This is useful for:
* Automating webhook setup as part of your deployment process
* Building integrations that dynamically configure webhooks
* Managing webhooks across multiple shops programmatically
## Creating a Webhook
Use the [POST /webhooks](/api-reference/platform/webhooks/create-webhook) endpoint to create a new webhook subscription:
```bash theme={null}
curl -u "your_username:your_password" \
-X POST "https://api.fourthwall.com/open-api/v1.0/webhooks" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks/fourthwall",
"allowedTypes": ["ORDER_PLACED", "ORDER_UPDATED", "DONATION"]
}'
```
The response includes the webhook configuration with its ID and secret key:
```json theme={null}
{
"id": "wcon_P-VkRfmJTBaC6_Tst22cew",
"url": "https://your-server.com/webhooks/fourthwall",
"allowedTypes": ["ORDER_PLACED", "ORDER_UPDATED", "DONATION"],
"secret": "e3f93c7c-c92b-4b8f-a9b1-5b70e0891abc"
}
```
Store the `secret` value securely - you'll need it to [verify webhook signatures](/webhooks/signature-verification).
## Complete Example: Create, Receive, and Verify
Here's a complete flow showing how to set up a webhook programmatically and handle incoming events:
**Step 1: Create the webhook**
```python theme={null}
import requests
# Create webhook subscription using Basic Auth
response = requests.post(
"https://api.fourthwall.com/open-api/v1.0/webhooks",
auth=("your_username", "your_password"),
json={
"url": "https://your-server.com/webhooks/fourthwall",
"allowedTypes": ["ORDER_PLACED"]
}
)
webhook_config = response.json()
webhook_secret = webhook_config["secret"] # Store this securely!
```
**Step 2: Receive and verify webhook events**
```python theme={null}
import hmac
import hashlib
import base64
from flask import Flask, request, jsonify
app = Flask(__name__)
WEBHOOK_SECRET = "your_stored_secret"
def verify_signature(payload, signature_header):
"""Verify the webhook signature matches."""
digest = hmac.new(
WEBHOOK_SECRET.encode('utf-8'),
payload,
digestmod=hashlib.sha256
).digest()
computed_signature = base64.b64encode(digest).decode('utf-8')
return hmac.compare_digest(computed_signature, signature_header)
@app.route('/webhooks/fourthwall', methods=['POST'])
def handle_webhook():
# Get the signature from headers
signature = request.headers.get('X-Fourthwall-Hmac-SHA256')
# Verify the signature
if not verify_signature(request.data, signature):
return jsonify({"error": "Invalid signature"}), 401
# Parse the webhook payload
event = request.json
# Handle different event types
if event["type"] == "ORDER_PLACED":
order_data = event["data"]
print(f"New order received: {order_data['friendlyId']}")
# Process the order...
# Always return 200 to acknowledge receipt
return jsonify({"status": "received"}), 200
```
## API Endpoints
The Platform API provides these endpoints for webhook management:
| Endpoint | Description |
| -------------------------------------------------------------------- | --------------------------------- |
| [POST /webhooks](/api-reference/platform/webhooks/create-webhook) | Create a new webhook subscription |
| [GET /webhooks](/api-reference/platform/webhooks/list-webhooks) | List all webhooks |
| [GET /webhooks/](/api-reference/platform/webhooks/get-webhook) | Get a specific webhook |
| [PUT /webhooks/](/api-reference/platform/webhooks/update-webhook) | Update a webhook |
| [DELETE /webhooks/](/api-reference/platform/webhooks/delete-webhook) | Delete a webhook |
# Webhook Event Types
Source: https://docs.fourthwall.com/webhooks/event-types
For detailed payload schemas, see the [Webhook Events API reference](/api-reference/order-events/order-placed).
# Getting Started with Webhooks
Source: https://docs.fourthwall.com/webhooks/getting-started
Real-time webhook notifications for shop events
Ready to boost your applications, Twitch, or YouTube live stream with Fourthwall's webhooks? This powerful tool helps your apps stay in sync with your shop in real-time, responding to key events as they happen.
Picture this: the moment a supporter purchases a product from your shop, a webhook sends a notification your way. Now it's your turn to act, using the data received through your webhook to drive the next steps.
## Example Use Cases
Webhooks offer a world of possibilities for real-time application integration:
* **Interactive IoT integrations:** Sync your webhooks with smart devices. For instance, set your lights to flash whenever an order is placed.
* **Data harvesting for internal usage**: Use webhooks to gather and process data, keeping your internal systems up-to-date.
* **External software linkage:** Connect your store with external software, such as accounting tools, to automate and streamline operations.
* **Immediate alerts for shipping partners:** Keep your shipping companies in the loop about new orders in real time.
* **Automated data clean-up:** Safeguard customer privacy by using webhooks to remove customer data from databases once it's no longer necessary.
## Example Flow
```mermaid theme={null}
sequenceDiagram
participant Client
participant Fourthwall
Client->>Fourthwall: ① Create a webhook
subscription for
events
Note over Fourthwall: ② Event happened, e.g.
order placed
Fourthwall->>Client: ③ Send JSON with
data
Client->>Fourthwall: ④ 200 HTTP OK
response
```
1. A webhook subscription is set up on the 'ORDER\_PLACED' topic for a shop, using a specified HTTPS endpoint (via the [dashboard](https://my-shop.fourthwall.com/admin/dashboard/settings/for-developers?redirect) or the [API](/api-reference/platform/webhooks/create-webhook)).
2. Someone places an order in the shop.
3. Fourthwall takes this order event and sends it, along with the order details (payload), to the subscription endpoint.
4. The client app receives this event, sends back an 'OK 200' HTTP status code, and then can perform any needed actions.
## Next Steps
Create and manage webhooks programmatically
See all available webhook events and payloads
Secure your webhook endpoint
Test webhooks during development
# Gifting Guide
Source: https://docs.fourthwall.com/webhooks/gifting-guide
Handle gift purchase webhooks for Twitch gift redemptions
This is a beta feature.
Below you will find all the information you need to know about gifting with Fourthwall.
## 1. How to enable Streamelements gifting
1. Go to [Twitch Gifting App](https://my-shop.fourthwall.com/admin/dashboard/apps/twitch?redirect) in your Fourthwall admin dashboard
2. Select Streamelements gifting radio button and ensure your shop is connected with Streamelements
3. Click on the `Enable` checkbox to enable the Twitch Gifting App
## 2. Webhook configuration
Go to [Webhook Settings](https://my-shop.fourthwall.com/admin/dashboard/settings/for-developers?redirect) and create webhooks for:
* `GIFT_DRAW_STARTED`
* `GIFT_DRAW_ENDED`
## 3. How to start Gift Draw
First, ensure your stream is live:
```bash theme={null}
PUT https://api.fourthwall.com/open-api/v1.0/streaming/start
Authorization: Basic {credentials}
Content-Type: application/json
{
"services": [
{
"type": "TWITCH",
"broadcasterId": "test-broadcaster-id",
"broadcasterLogin": "raziosx",
"thumbnailUrl": "test-thumbnail-url"
}
]
}
```
Then go to your shop main page and purchase any product as a gift. This will trigger the `Gift draw started` webhook event.
## 4. How to finish Gift Draw
Send a request to finish the draw:
```bash theme={null}
PUT https://api.fourthwall.com/open-api/v1.0/gifting/draw/{drawId}/finish
Authorization: Basic {credentials}
Content-Type: application/json
{
"participants": [
{
"service": "TWITCH",
"userId": "{twitchWinnerUserId}",
"userName": "{twitchWinnerUserName}"
}
]
}
```
The response will include the winner details along with the redeem URL. The `Gift draw ended` webhook will also be triggered.
Gift draws not finished within the configured time (+ 1 minute) will be automatically finished without winners to allow redoing the draw.
# Limitations
Source: https://docs.fourthwall.com/webhooks/limitations
Webhook ordering guarantees, delivery constraints, and best practices
## Key limitations
**Ordering is not guaranteed** between different topics for the same resource. For example, an `ORDER_UPDATED` webhook might be delivered before an `ORDER_CREATED` webhook. Your system should handle such cases.
* **At-least-once delivery**: Webhook events should be delivered at least once. This means your endpoint might receive the same event more than once. You can detect duplicate events by comparing the `id` value of received events.
* **Delivery not guaranteed**: Webhook delivery is not always guaranteed. If you require your data to be always consistent, you should implement reconciliation jobs to fetch data from Fourthwall periodically.
# Retry Policies
Source: https://docs.fourthwall.com/webhooks/retry-policies
Webhook delivery guarantees, retry logic, and timeout handling
## Response policy
Your client should respond as quickly as possible with an `OK 200 HTTP` status code. Any other response will result in our system marking the event as failed delivery.
Response times longer than **2000ms** may result in a failed delivery.
## Retry policy
In case of no response or a response different than OK 200 HTTP status code from your client, our platform will make additional attempts to redeliver the event:
* **Initial attempt**: The platform will try to deliver the message 5 times with a timeout set to 5 seconds for each attempt
# Signature Verification
Source: https://docs.fourthwall.com/webhooks/signature-verification
Verify webhook authenticity with HMAC-SHA256 signatures
If you want to verify whether the request was in fact sent from Fourthwall you can do that by calculating the webhook digital signature.
## Getting your secret key
First head to the [webhook configuration panel](https://my-shop.fourthwall.com/admin/dashboard/settings/for-developers?redirect) in your site settings and find the secret key value assigned to your shop, e.g.:
```
e3f93c7c-c92b-4b8f-a9b1-5b70e0891abc
```
## Verifying the signature
Each webhook request comes with an `X-Fourthwall-Hmac-SHA256` base64 encoded header. To verify the request:
1. Compute the HMAC-SHA256 using your secret key and the entire webhook body
2. Base64 encode the result
3. Compare with the header value
```python theme={null}
import hmac
import hashlib
import base64
SECRET = 'secret_value_from_your_shop_webhook_settings'
def verify_signature(data, hmac_header):
digest = hmac.new(SECRET.encode('utf-8'), data, digestmod=hashlib.sha256).digest()
computed_hmac = base64.b64encode(digest)
return hmac.compare_digest(computed_hmac, hmac_header.encode('utf-8'))
```
You can use any programming language as long as the algorithm follows the same principles.
## Signature verification for Platform Apps
The verification process for Platform Apps is the same, but uses a different header: `X-Fourthwall-Hmac-Apps-SHA256`.
You can get your HMAC key in your app's [settings](https://my-shop.fourthwall.com/admin/dashboard/settings/platform-apps?redirect).
# Testing Webhooks
Source: https://docs.fourthwall.com/webhooks/testing
Test your webhook integration
You can test your webhook configuration in the following ways:
1. **Manual testing**: Click the `Send test notification` button on the [webhook list](https://my-shop.fourthwall.com/admin/dashboard/settings/for-developers?redirect). You'll receive an event with test data. You can differentiate test events from normal events by checking if the `testMode` field is set to `true`.
2. **Real events**: Perform the action in your store that you've subscribed to. For example, create an order when you have configured a webhook with `ORDER_PLACED` events.
# Webhook Model
Source: https://docs.fourthwall.com/webhooks/webhook-model
Webhook event structure and fields
All webhook events share the same structure. Following is an example JSON event:
```json theme={null}
{
"testMode": false,
"id": "weve_geAva6c1RAuyb9HQxbSlmA",
"webhookId": "wcon_P-VkRfmJTBaC6_Tst22cew",
"shopId": "sh_7ad0c438-beda-4779-a885-0dc325a755c1",
"type": "ORDER_PLACED",
"apiVersion": "V1_BETA",
"createdAt": "2023-07-12T15:05:11.078089+00:00",
"data": {
// Event-specific data
}
}
```
## Fields Description
| Field | Description |
| ------------ | ---------------------------------------------------------------------------------------------------------- |
| `testMode` | If `true`, indicates the event contains only test data. See [Testing](/webhooks/testing) for more info. |
| `id` | Unique identifier of the event. If you receive multiple events with the same id, treat them as duplicates. |
| `webhookId` | Indicates which webhook configuration was used. |
| `shopId` | Your shop ID, useful if you manage multiple shops under the same webhook endpoint. |
| `type` | Indicates which data type you should expect within the event. |
| `apiVersion` | Version of the data type that was chosen. |
| `createdAt` | Date at which the event was created. |
| `data` | Contains all the actual data specific to the type of the event. |