Welcome to My Store
Discover amazing products...
` appended. Attribution lasts **30 days**, and the code can also be entered directly at checkout.
The [Embed SDK](/guides/embed-sdk) handles this automatically. A visitor
arriving at your own site with `?ref=` is attributed without any setup, the
affiliate cookie is read if present, and you can override it per checkout by
passing `affiliateCode` to `Komerza.open()`.
## How commission accrues
Commission is a percentage of the money **actually collected**, credited once the order reaches delivery. Three consequences worth knowing before an affiliate asks:
* A pending or unpaid order earns nothing yet.
* The amount is based on what the buyer actually paid, after the affiliate's own discount.
* **Refunding an order reverses the commission automatically.**
If an affiliate says their balance hasn't moved, walk that list in order - it is almost always the first item.
## Managing affiliates
Manage them under **Customers → Affiliates**, which requires the View and Update Affiliates permissions. The page shows Total Affiliates, Commissions Paid, Referral Revenue, Avg. Commission and your top earner, and lets you **Invite Affiliate** by email, search, delete or blacklist.
Affiliates see their own rate, referral link, balance and referred orders on your store's customer dashboard under **Affiliates**.
## Paying affiliates
There is no automatic cash payout. Komerza tracks balances; it does not move
money to your affiliates.
When you settle up, open the affiliate's detail page under **Customers → Affiliates** and **record a payout** against their balance. The actual transfer - PayPal, bank, whatever you agree - happens outside Komerza. A recorded payout can be **voided**, which restores the balance.
If you enabled balance conversion, affiliates can instead convert their balance into store credit and spend it with you, which costs you nothing in cash and keeps the money in your store. See [Customers & Store Credit](/guides/customers-and-store-credit).
## Doing this by API
The affiliate endpoints cover the whole lifecycle: create, invite, update, blacklist, delete, list referred orders, and record, void or list payouts.
} href="/api-reference/endpoint/affiliates/get-list">
List, search and manage affiliate profiles.
} href="/api-reference/endpoint/affiliates/record-payout">
Log a settlement against an affiliate's balance.
## Next steps
} href="/guides/customers-and-store-credit">
Where converted affiliate balances end up.
} href="/guides/coupons-and-discounts">
How affiliate discounts differ from coupon codes.
# Analytics SDK
Source: https://docs.komerza.com/guides/analytics-sdk
Bring back visitor analytics to your Komerza Dashboard with our lightweight analytics script
## Overview
The Komerza Analytics SDK allows you to track visitor behavior on your custom website and view the analytics directly in your Komerza Dashboard. With just a single script tag, you'll get comprehensive visitor insights including page views, user sessions, conversion tracking, and more.
Access your visitor analytics in the Komerza Dashboard
## Features
Automatic cookie consent management with essential/optional cookies
Just include the script with your store ID - no complex setup required
View visitor data instantly in your Komerza Dashboard
Respects user privacy with transparent cookie consent and Shadow DOM isolation
## Quick Start
### Single Script Integration
Add the analytics script to your HTML pages with your store ID:
```html theme={null}
My Store
Welcome to My Store
```
Replace `your-store-id` with your actual Komerza store ID from your dashboard.
The analytics script automatically handles GDPR compliance by showing a cookie
consent notice to visitors.
## Cookie Consent Flow
When a visitor first arrives on your site:
1. **Immediate Pageview**: An IP-only pageview is recorded immediately (GDPR compliant)
2. **Cookie Notice Display**: A GDPR-compliant notice appears in the bottom-right
3. **20-Second Timer**: If no response, "Required Only" tracking activates automatically
4. **User Choice**: Visitor can choose "Required Only" or "Accept All" at any time
5. **Preference Storage**: Choice is remembered for future visits (1-hour cookie expiry)
* Random visitor ID (not fingerprinted)
* Device type and browser name
* Referrer information
* Session tracking (1-hour expiry)
* No cross-session tracking
* Browser fingerprint for stable visitor ID
* Full analytics tracking
* Enhanced user behavior insights
* Conversion and goal tracking
* Detailed performance metrics
* Cross-session tracking
## Implementation Examples
### Basic Website
```html theme={null}
My Online Store
My Store
Welcome to My Store
Discover amazing products...
```
### E-commerce Site
```html theme={null}
Product Page - My Store
Amazing Product
$29.99
```
### React/SPA Integration
For single-page applications, add the script once in your main HTML file:
```html theme={null}
My React Store
```
## Manual Consent Control
If you want to programmatically control consent (e.g., custom cookie banner):
```html theme={null}
```
## Privacy Features
* **Random visitor IDs**: Required-only tracking uses crypto-random IDs (not fingerprinted)
* **Short expiry**: All cookies expire after 1 hour
* **No cross-domain tracking**: Cookies are site-specific
* **IP anonymization**: Essential pageviews are IP-only, no visitor ID
## Content Security Policy (CSP)
For sites with CSP headers, add the following to your CSP configuration:
```http theme={null}
Content-Security-Policy:
script-src 'self' https://cdn.komerza.com;
connect-src 'self' https://m-api.komerza.com;
```
The analytics script does not require CSP nonce integration as it loads from a
trusted CDN. However, ensure `https://cdn.komerza.com` is in your `script-src`
directive, and `https://m-api.komerza.com` is in your `connect-src` directive
to allow data transmission.
## Viewing Your Analytics
Once the script is installed and visitors start coming to your site:
1. **Login to Dashboard**: Visit [dashboard.komerza.com](https://dashboard.komerza.com)
2. **Navigate to Visitor Analytics**: Click on the "Visitors" section
3. **View Insights**: See real-time and historical visitor data
4. **Track Performance**: Monitor conversion rates and user behavior
View live visitor activity and current site performance
Access detailed reports and trends over time
## Ad Blocker Compatibility
The analytics script uses the filename `ka.min.js` which is less likely to be blocked by ad blockers compared to filenames containing "analytics" or "tracking". However, some aggressive blockers may still block analytics requests.
## Support
Access your visitor analytics and reports
Get help with analytics integration
# Setup Apple Pay with Stripe
Source: https://docs.komerza.com/guides/apple-pay-stripe
Configure Apple Pay domains in Stripe for Komerza Checkout and Embeds
To enable Apple Pay for your Komerza store, you need to verify the domains where payment requests will be initiated.
1. Go to the Stripe Dashboard settings for [Payment Method Domains](https://dashboard.stripe.com/settings/payment_method_domains).
2. Click on the **"Add a new domain"** button.
3. Enter `checkout.komerza.com` in the domain field.
## Using Embeds
If you are using Komerza Embeds on your own website, you must also add the domain where the embeds are running.
1. Click **"Add a new domain"** again.
2. Enter your website's domain (e.g., `your-website.com`).
# Integrations
Source: https://docs.komerza.com/guides/builder/integrations
Add analytics, ad pixels and chat widgets to your storefront
The integrations panel injects third-party scripts into your storefront when you publish, so you don't have to touch any code. Paste the ID or key each provider gives you and publish.
## Analytics
| Provider | You'll need |
| ------------------ | -------------- |
| Google Analytics | Measurement ID |
| Google Tag Manager | Container ID |
| Plausible | Your domain |
| Microsoft Clarity | Project ID |
| Hotjar | Site ID |
## Ad pixels
Meta, TikTok, Google Ads, Pinterest, Snapchat and Reddit - each takes its pixel or conversion ID.
## Chat and support
Crisp, Tawk.to, LiveChat, Intercom, Zendesk, Assistify, and a WhatsApp chat button.
## Other
Klaviyo, Trustpilot, and a Custom Script slot for anything not listed.
These are separate from Komerza's own [Analytics SDK](/guides/analytics-sdk),
which reports visitor data back into your dashboard. You can run both.
Every script you add runs on every page for every visitor. Three trackers and
a chat widget is a real cost to how fast your store feels - add what you'll
actually look at.
# Storefront Builder
Source: https://docs.komerza.com/guides/builder/introduction
Build your storefront visually, from the dashboard
Every store gets a storefront you build in the **Komerza Builder** - a visual editor where you assemble pages from sections, style them, and publish to your store's URL. Open it from **Online Store → Builder**.
Your real products, categories and reviews render in the canvas as you work, so what you see is what shoppers get.
## Starting a storefront
The **New storefront** picker offers three routes:
| Option | What you get |
| ------------------- | ------------------------------------------------------------------------------- |
| **Blank** | The standard pages - home, products, product detail, policies - with no styling |
| **From a template** | A finished design from the gallery. Some are Premium or Business only |
| **Import theme** | A previously exported theme `.json`, restored as-is |
A storefront created from a template is an independent copy. If the template changes later you'll see an "update available" marker, but nothing is applied automatically.
## You can have several
A store can hold multiple storefronts, but only one is live at a time - see [Publishing](/guides/builder/publishing). The others sit as drafts, which makes them a safe way to try a redesign without touching what customers see.
## Deleting one
Storefront settings has a danger zone. Deleting is permanent and takes with it all page content and design, all snapshots and version history, all uploaded media, and any published deployments.
You don't need to delete a storefront to try a different design. Create
another and publish whichever you prefer.
## Next
} href="/guides/builder/sections">
The blocks your pages are built from.
} href="/guides/builder/pages">
Adding pages, slugs and search metadata.
} href="/guides/builder/publishing">
Save vs Publish vs Snapshots, and going live.
} href="/guides/builder/integrations">
Analytics, ad pixels and chat widgets.
# Pages & SEO
Source: https://docs.komerza.com/guides/builder/pages
Adding pages, setting slugs and search metadata
A new storefront starts with the standard pages - home, products, product detail and policies. Add your own from the page manager.
## Page settings
| Field | Notes |
| -------------------- | ----------------------------------------- |
| **Page title** | Shown in navigation |
| **URL slug** | The path, e.g. `/about` |
| **Meta title** | Defaults to the page title if left empty |
| **Meta description** | The snippet shown in search results |
| **Social image** | The preview image when the page is shared |
## Writing metadata worth having
Search engines and social previews use the meta fields, not your on-page headings, so they're worth a minute each:
* **Meta title** should read as a result, not a label - "Minecraft ranks and perks" beats "Products".
* **Meta description** is the sales pitch under the link. Say what's sold and why to buy here.
* **Social image** is what appears when someone drops your link in Discord or Twitter. Without it, the preview is whatever the platform picks.
Slugs are part of your URLs, so changing one on a live page breaks any
existing links to it. Settle on a slug before you publish rather than after.
# Publishing
Source: https://docs.komerza.com/guides/builder/publishing
Save, publish and roll back your storefront
Three actions, three different effects. Mixing them up is the most common source of confusion in the builder.
}>
Stores your draft. Nothing on the live site changes.
}>
Renders your pages and puts them live at your store's URL.
}>
A restore point you can roll back to.
## One live site per store
**Publishing any storefront replaces whatever was live before - last publish
wins.** Extra storefronts are effectively drafts.
If your live site shows an old or unfamiliar design, another storefront was
published more recently.
## Snapshots
Save a snapshot before any change you might want to undo. Restoring one replaces your current draft and reloads the builder.
Restoring does **not** republish. The live site keeps serving the old version
until you press Publish again.
## Troubleshooting
| Problem | Cause |
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| Live site shows the wrong design | Another storefront was published more recently. Open the one you want and publish it |
| Changes aren't live | You saved but didn't publish, or restored a snapshot without republishing |
| Site is offline entirely | Maintenance mode is on, the owner's plan expired, or the account is suspended - none of which the builder can fix |
Once it's live, point your own domain at it with [Custom Domain](/guides/custom-domain).
# Sections
Source: https://docs.komerza.com/guides/builder/sections
The blocks your storefront pages are built from
Pages are built from sections. Add them from the sections panel, reorder them in the canvas, and edit whatever is selected in the property panel.
Sections carry style presets and theme colours, so changing the theme restyles the whole site rather than each section one at a time.
## Commerce
These pull live data from your store - no content to copy across, and nothing to keep in sync.
| Section | Shows |
| ---------------------- | ------------------------------------------------------------------- |
| Product Grid | Your catalogue, with sort options like Popular, Cheapest, Top Rated |
| Product Detail | A single product with its variants and buy button |
| Product Sections | Curated groups of products |
| Category Grid | Your categories as browsable tiles |
| Reviews | Real customer reviews from your store |
| Store Stats | Live figures from your store |
| Trust & Payment Badges | Payment logos and reassurance badges |
| Cart Modal | The cart overlay |
## Content
| Section | Shows |
| ------------ | --------------------------------------------------------------- |
| Hero Banner | Headline, sub-copy, background image and call to action |
| Features | Selling points in a row or grid |
| Text Section | Free prose |
| FAQ | Expandable question and answer list |
| Custom HTML | Your own markup, for anything the built-in sections don't cover |
## Layout and media
| Section | Shows |
| ------------------- | -------------------------------------------- |
| Store Name | The navbar |
| Announcement Banner | A strip above the page for offers or notices |
| Footer | Links, policies and social icons |
| Video | An embedded YouTube or Vimeo video |
A Video section warns you if its URL isn't a playable YouTube or Vimeo link -
click the warning to open the section and fix it.
## Generating sections with AI
Rather than starting from an empty section, you can describe what you want and have the builder generate it, or select an existing section and give it an instruction to rewrite.
AI work is billed in tokens from a balance shared across your stores, shown in the builder as you work. If it stops responding you're either out of tokens or there's a temporary service issue - the message says which.
# Setup Cash App
Source: https://docs.komerza.com/guides/cash-app-setup
Configure Cash App payments with mail forwarding on Komerza
Follow this guide to connect your Cash App account to Komerza and automate payment confirmation via Gmail forwarding.
Navigate to the [Komerza Dashboard Payments](https://dashboard.komerza.com/account?payment) page and click the **Connect** button next to Cash App.
In the dialog box, enter the following details:
* **Cash Tag**: Enter your unique Cash App tag (e.g., `$YourName`).
* **Email Address**: The email associated with your Cash App account that receives payment receipts.
We highly recommend using a **Gmail** address. Other email providers may not be supported or may experience delivery issues.
### Advanced Options
**Version 2 (Recommended)**: More realistic notes that are harder to detect, helping to reduce the risk of account bans.
**Version 1**: Basic notes. Use only if you have a specific preference.
Enable this if you want Komerza to automatically try to accept payment requests.
This feature carries some risk. Only enable it if Cash App frequently forces you to manually accept payments in the app.
After clicking Next, copy the unique forwarding email address ending in `@hermes.komerza.com`.
### Add Forwarding Address in Gmail
1. Login to Gmail and go to **Settings** > **See all settings** > **Forwarding and POP/IMAP**.
2. Click **Add a forwarding address**, paste the `@hermes.komerza.com` address, and click **Next**.
3. Complete the Google identity verification step.
4. Check your inbox (or spam) for the confirmation email and click the confirmation link.
### Configure Forwarding Filter
**Important:** Return to the **Forwarding and POP/IMAP** tab and ensure **"Disable forwarding"** is selected. Do not enable global forwarding.
1. Click **"creating a filter"** link in the "Forwarding" section.
2. Set **From** to `cash@square.com`.
3. Click **Create filter**.
4. Check **Forward it to:** and select the hermes email address.
5. Click **Create filter** to finish.
Your Cash App integration is now set up!
We strongly recommend making a **\$1 test payment** on your store to confirm that the system correctly detects the payment and marks the order as paid.
# Komerza CLI
Source: https://docs.komerza.com/guides/cli
Build and deploy Komerza storefronts from your local machine
## Overview
The Komerza CLI lets you develop storefronts locally instead of inside the browser-based Builder. It keeps your local files in sync with your Builder project in real time, and lets you deploy directly to production from the terminal.
The typical workflow is: **clone → dev → deploy**.
## Installation
```bash theme={null}
npm install -g @komerza/cli
```
Once installed, the `komerza` command is available globally.
## Authentication
### Login
```bash theme={null}
komerza login
```
Opens a browser window to authenticate with your Komerza account. Credentials are stored securely in your OS keychain - no tokens to copy and paste.
### Logout
```bash theme={null}
komerza logout
```
### Check who you're logged in as
```bash theme={null}
komerza whoami
```
Prints your account and the stores associated with it.
***
## Cloning a Project
```bash theme={null}
komerza clone [projectId]
```
Downloads a Builder project to your local machine. If you omit the project ID, you'll get an interactive list to pick from.
This creates:
* A directory named after your project
* A `.komerza/config.json` file tracking the project - **commit this file**
* A `.gitignore`
**Options:**
| Flag | Description |
| ------------------ | ------------------------------------------------------- |
| `-d, --dir ` | Use a custom directory name instead of the project name |
***
## Local Development
```bash theme={null}
komerza dev
```
Starts your local dev server and watches for file changes. Any saved file is automatically synced to the Builder within 1.5 seconds.
* Detects your package manager (`npm`, `pnpm`, `yarn`, or `bun`) automatically
* Requires a `"dev"` script in `package.json`
The dev server and file-watching run together in the same terminal process.
Stop the server (Ctrl+C) to exit both.
***
## Deploying
```bash theme={null}
komerza deploy
```
Builds the project and deploys it to production.
1. Runs your `build` script from `package.json`
2. Finds the build output (`dist`, `out`, `build`, `.next/out`, or `public/dist`)
3. Uploads it and makes it live
The live store URL is printed on success.
***
## Environment Variables
Manage env vars stored on your Builder project.
| Command | Description |
| ------------------ | ---------------------------------------------------------------- |
| `komerza env pull` | Fetches env vars and writes them to `.env.local` |
| `komerza env push` | Uploads `.env.local` to the Builder, replacing all existing vars |
| `komerza env list` | Prints all env vars currently set on the project |
***
## Other Commands
### Manual sync
```bash theme={null}
komerza push # push local files to the Builder
komerza pull # pull files from the Builder (overwrites local)
```
Both are manual escape hatches. Most workflows only need `dev` and `deploy`.
`komerza pull` will ask for confirmation if local files already exist. Use `-f` / `--force` to skip the prompt.
### List projects
```bash theme={null}
komerza projects
```
Lists all Builder projects linked to your account.
### Open in browser
```bash theme={null}
komerza open # opens the live store
komerza open --builder # opens the Builder editor
```
***
## Config File
`.komerza/config.json` is created when you run `komerza clone`. It stores:
| Field | Description |
| --------------- | ------------------------------------ |
| `projectId` | The Builder project ID |
| `storeId` | The associated store |
| `name` | Project display name |
| `deploymentUrl` | Updated after each successful deploy |
Commit this file - it is required by most CLI commands.
***
## File Size Limit
Files larger than **12 MB** are not synced.
# Coupons & Discounts
Source: https://docs.komerza.com/guides/coupons-and-discounts
Discount codes, volume pricing and pop-up offers on your Komerza store
Komerza gives you three independent ways to discount: **coupon codes** that customers enter at checkout, **volume discounts** that apply automatically as quantity increases, and **pop-up offers** shown on the storefront. They can be combined, and there is one control for stopping them from stacking.
## Coupon codes
Coupons are managed under **Products → Coupons**.
| Field | Rules |
| ----------------------- | ------------------------------------------------------------------------------------ |
| **Code** | 3-32 characters, case-insensitive, unique per store |
| **Type** | Percentage (0-100%) or fixed amount (up to 100,000, never more than the order total) |
| **Maximum uses** | Optional - the coupon auto-disables when the cap is reached |
| **Expiration date** | Optional |
| **Variant restriction** | Optional - restrict the code to specific variants |
Coupon capacity depends on your plan: **Free** 5, **Premium** 1,000, **Business** unlimited.
### What customers see when a code fails
The code does not exist, or has been disabled.
The code exists but is expired, fully used, or restricted to variants that are
not in the cart.
A coupon's usage count increments when the order is **delivered**, not when it
is paid - so the count can look low while orders are still in flight.
## Volume discounts
Volume discounts are quantity tiers set per variant, on the variant card. Each tier has a quantity threshold and one of three types:
A percentage off, shown on order pages as e.g. "10% off".
An amount off per unit, shown in your store currency, e.g. "5.00 off total".
A new unit price, shown in your store currency, e.g. "8.00 each".
Each quantity can carry only one tier - duplicates are rejected with "A volume discount tier for quantity N already exists."
Volume discounts pair well with variants that have a **maximum quantity** set
generously. If your variant caps quantity at 1, tiers above 1 can never be
reached.
## Stopping discounts from stacking
Each variant has a **Disable volume discount on coupon** toggle. When enabled, volume pricing is dropped if the customer applies a coupon code, so the two discounts never combine on the same line item.
Decide this per variant based on your margin - the default is that both can apply.
## Pop-up offers
**Products → Pop-up Offers** manages storefront discount dialogs. These are shown to visitors on your storefront and are configured separately from coupon codes.
## Affiliate discounts
Affiliate discounts are a fourth, separate mechanism: they are configured per product on the product editor's **Affiliate** tab, where you can use your store's default affiliate discount, set a custom percentage, or turn it off for that product. They apply when a customer arrives through an affiliate link rather than by entering a code.
## Applying discounts in bulk
The Products list supports bulk updates, including the **affiliate discount** across many products at once. Coupon codes themselves are store-wide by default, so a single code is usually simpler than editing many products.
## Next steps
} href="/guides/products-and-variants">
Pricing, quantity limits and stock modes.
} href="/guides/orders">
See which coupons were used on an order.
} href="/api-reference/endpoint/coupons/create">
Create and manage coupons programmatically.
} href="/api-reference/endpoint/affiliates/configure">
Configure affiliate commission and discounts.
# Custom Domain
Source: https://docs.komerza.com/guides/custom-domain
Connect your own domain to your Komerza store
To use a custom domain with your Komerza store, you must use **Cloudflare DNS**. Cloudflare provides the necessary proxy and DNS management features required for custom domains to work properly.
If you don't already have a Cloudflare account, create one at [dash.cloudflare.com/sign-up](https://dash.cloudflare.com/sign-up).
Once your account is created:
1. Log in to your Cloudflare dashboard
2. Click **Add a Site**
3. Enter your domain name (e.g., `yourstore.com`)
4. Select the **Free** plan (or any plan you prefer)
5. Cloudflare will scan your existing DNS records
6. Continue to the next step where Cloudflare will provide you with nameservers
Cloudflare will assign you two nameservers. They typically look like this:
```
aaron.ns.cloudflare.com
bella.ns.cloudflare.com
```
Your assigned nameservers will be different. Make sure to copy the exact nameservers shown in your Cloudflare dashboard.
You need to point your domain to Cloudflare's nameservers at your domain registrar.
1. Log in to your [Namecheap account](https://www.namecheap.com/)
2. Go to **Domain List** in the left sidebar
3. Click **Manage** next to your domain
4. Scroll down to the **Nameservers** section
5. Click the dropdown and select **Custom DNS**
6. Enter your Cloudflare nameservers:
* **Nameserver 1**: Your first Cloudflare nameserver (e.g., `aaron.ns.cloudflare.com`)
* **Nameserver 2**: Your second Cloudflare nameserver (e.g., `bella.ns.cloudflare.com`)
7. Click the **green checkmark** button to save your changes
The process is similar for most domain registrars:
1. Log in to your domain registrar's control panel
2. Navigate to your domain's settings or DNS management
3. Look for **Nameservers** or **DNS Servers** settings
4. Change from the default nameservers to **Custom Nameservers**
5. Enter both Cloudflare nameservers provided to you
6. Save your changes
If you're unsure how to change nameservers with your specific registrar, search for "\[Your Registrar] change nameservers" or contact their support.
Once your nameservers have been updated and Cloudflare shows your domain as active:
1. Go to your domain in the Cloudflare dashboard
2. Click **DNS** in the left sidebar
3. Click **Add Record**
4. Configure the record:
* **Type**: `CNAME`
* **Name**: `@` (or enter a subdomain like `shop` if you want `shop.yourstore.com`)
* **Target**: `fallback.mykomerza.com`
* **Proxy status**: **Proxied** (orange cloud icon - this is required!)
5. Click **Save**
**Important**: Make sure the proxy status shows the orange cloud icon (Proxied). If it shows a grey cloud (DNS only), click on it to enable proxying. The custom domain will not work without proxying enabled.
**Existing DNS Records**: If you have existing DNS records (like A, CNAME, or NS records) for the same name, remove them to avoid conflicts before adding the new CNAME record.
If you want visitors to `www.yourstore.com` to be redirected to `yourstore.com`, follow these additional steps:
### Add a DNS Record for www
1. In Cloudflare DNS settings, click **Add Record**
2. Configure the record:
* **Type**: `A`
* **Name**: `www`
* **IPv4 address**: `192.0.2.0` (this is a reserved placeholder IP)
* **Proxy status**: **Proxied** (orange cloud)
3. Click **Save**
### Create a Page Rule for the Redirect
1. In Cloudflare, go to **Rules** > **Page Rules**
2. Click **Create Page Rule**
3. Enter the URL pattern: `www.yourstore.com/*`
4. Click **Add a Setting** and choose:
* **Setting**: Forwarding URL
* **Status code**: 301 - Permanent Redirect
* **Destination URL**: `https://yourstore.com/$1`
5. Click **Save and Deploy Page Rule**
DNS changes can take time to propagate across the internet.
DNS propagation typically takes **24-48 hours** to complete worldwide, though it often happens much faster. During this time, some visitors may still see the old DNS settings while others see the new ones.
You can check if your DNS has propagated using tools like [dnschecker.org](https://dnschecker.org/) or [whatsmydns.net](https://www.whatsmydns.net/).
## Troubleshooting
| Issue | Solution |
| --------------------------------- | -------------------------------------------------------------------- |
| Domain not working after 48 hours | Verify the CNAME record is correct and proxy is enabled |
| SSL certificate errors | Ensure Cloudflare's SSL/TLS mode is set to "Full" or "Full (Strict)" |
| www redirect not working | Check that the Page Rule is active and the A record for www exists |
| Cloudflare shows "Pending" | Your nameservers haven't propagated yet - wait and check again |
# Custom Payment Methods
Source: https://docs.komerza.com/guides/custom-payment-methods
Accept bank transfers, Zelle, manual crypto or any other payment you settle yourself
Custom payment methods let you accept anything Komerza doesn't integrate with - bank transfer, Zelle, Wise, a manual crypto address, cash on collection. You define the method, the customer follows your instructions and submits proof of payment, and you approve or reject the order yourself.
They are configured in the dashboard under **Online Store → Settings → Payment Methods**.
Custom methods are **manually settled**. Komerza never sees the money, cannot
confirm that payment arrived, and cannot refund it. Everything depends on you
verifying payment before you approve.
## The two types
}>
The customer is shown text you write - bank details, a wallet address, a
reference to include - and pays outside Komerza.
}>
The customer is sent to a URL you specify to complete payment, then returns
to confirm.
Each method has a **name**, an **icon** (PNG, JPG or WebP up to 2 MB) and a **sort order** controlling where it appears in the checkout list.
## How the customer experience works
It appears alongside your other gateways, using the name and icon you set.
Payment happens entirely outside Komerza.
Proof is a transaction reference, a screenshot (up to 5 MB), or both.
It shows as **Awaiting seller review** with a Proof of Payment card on the
order page.
## Approving or rejecting
Open the order under **Home → Orders** and review the Proof of Payment card:
* **Approve & Deliver** - the order runs the normal delivery flow and becomes **Delivered**.
* **Reject** - the order becomes **Cancelled**.
Verify the money actually arrived in your account before approving. Approval
triggers real delivery - stock is consumed, license keys are issued, dynamic
delivery webhooks fire and the customer is emailed. A screenshot is not proof
of settlement.
Turn on the **Order Pending** notification under **Store Settings →
Notifications** so a submitted proof doesn't sit unnoticed. Slow approvals are
the most common complaint about manual methods.
## Refunds
Orders paid with a custom payment method can only be refunded to **customer balance**, because there is no gateway to reverse the charge through. Refunding the actual money is something you do yourself, through whatever channel the customer paid with.
Balance refunds require your store's customer balance feature to be enabled under **Settings → Payment Options** - otherwise you will see "Customer balance is not enabled for this store."
See [Managing Orders](/guides/orders#refunds) for the full refund flow.
## Fees, minimums and labels
Custom methods sit alongside every other gateway in **Settings → Payment Options**, where you can set:
* A per-gateway **percentage and fixed fee** passed on to the customer
* A **minimum order** amount (never below the system floor of 0.50 in your store currency for non-crypto methods)
* A **checkout label** that renames the method at checkout
## When to use a custom method - and when not to
Custom methods are the right tool for payment rails Komerza doesn't support, low volumes, or high-trust B2B sales where you already know the buyer. They scale poorly: every order needs a human decision, and there is no automatic matching of incoming payments.
If you need automated crypto acceptance rather than a manual wallet address, use a supported processor instead - see [Self-Custody](/guides/self-custody-setup), [NOWPayments](/guides/nowpayments-setup) or [Heleket](/guides/heleket-setup). For automated card payments, see [Supported Payment Methods](/guides/payment-methods).
## Next steps
} href="/guides/payment-methods">
Every integrated gateway and cryptocurrency.
} href="/guides/orders">
Approve, deliver, refund and export orders.
} href="/api-reference/payment-methods">
Payment method values used by the API.
} href="/api-reference/payment-status">
What Pending, Delivered and Cancelled mean.
# Customers & Store Credit
Source: https://docs.komerza.com/guides/customers-and-store-credit
Customer records, the customer dashboard, and how store credit works
Every buyer who orders from your store gets a customer record, and every store can optionally issue **store credit** - a per-store balance customers spend at your checkout. Credit is the answer to a lot of awkward situations: refunds on payment methods that cannot be reversed, goodwill, prepaid top-ups, and affiliate balances converted into spend.
## Customer records
Customers are created automatically at checkout and are managed under **Customers** in the dashboard. Records can be searched by email, and the [Customers API](/api-reference/endpoint/customers/get-list) supports listing, searching, creating and adjusting balance programmatically.
Team members need customer permissions to view or edit them. See [Teams & Permissions](/guides/teams-and-permissions).
## The customer dashboard
Every store includes a customer-facing dashboard on the store's own domain at `/dashboard` - for example `https://yourstore.mykomerza.com/dashboard`, or `/dashboard` on your custom domain.
This is separate from your merchant dashboard, and there is no central Komerza
login for buyers. Customer accounts exist per store.
Sign-in is passwordless: the customer enters their email and receives a **6-digit code**, valid for 5 minutes and usable once. Signing in creates the account if it doesn't exist, so there is nothing for them to register first. There are no passwords to reset and no signup form to fill in.
Inside, customers get their orders, delivered items, a Downloads centre collecting every file and key across their orders, support tickets, their store balance, and the affiliate area if you have affiliates enabled. The dashboard is available in nine languages.
## Enabling store credit
Store credit is the **Customer Balance** payment method, enabled under **Online Store → Settings → Payment Options**. Once enabled:
* Customers can pay with their balance at checkout.
* Balance becomes available as a refund destination on orders.
* Customers can top up their balance through checkout, paying the amount plus the gateway fee.
* Affiliates can convert commission into credit, if you allowed that in [affiliate settings](/guides/affiliates).
Store credit is **not cashable**. It can be spent at your store and never
expires, but it cannot be withdrawn as money. Issuing credit is a commitment
to supply goods, not a cash liability you can settle later.
## Credit as a refund destination
For several payment methods, balance is the **only** refund destination Komerza can process, because the gateway has no refund API or the payment was settled manually:
* All cryptocurrencies
* Custom payment methods, Cash App, PayPal Friends & Family, legacy PayPal
* NMI, MoneyMotion, Pandabase, Storrik, VenPayr
* Any order that was delivered manually
In those cases you can still return the original payment yourself, outside Komerza. Two rules to remember:
1. A **top-up order can never be refunded to balance** - that would double-credit the customer.
2. Refunding a top-up to the original card also **claws back the credited balance**.
For split payments (balance plus a gateway), a refund returns each part to its own source. The full flow is in [Managing Orders](/guides/orders#refunds).
## Adjusting a balance directly
Balances can be adjusted through the [update balance endpoint](/api-reference/endpoint/customers/update-balance), which is how you would script goodwill credits, promotional balances, or a migration of account credit from another platform.
Credit is a useful service recovery tool precisely because it is instant and
costs you no gateway fees. A customer whose crypto order went wrong is usually
happier with immediate credit than with a manual refund that takes days.
## What customers can't do themselves
* **Change their email address.** The email is the account; there is no self-serve change or account merge.
* **Manage subscriptions.** There is no self-service billing area, so cancellations and changes come to you. See [Selling Subscriptions](/guides/subscriptions).
* **Delete their account or export their data** from the dashboard. Those requests are handled manually - direct them to Komerza support.
## Next steps
} href="/guides/orders">
Refunds, destinations and split payments.
} href="/guides/payment-methods">
Enabling Customer Balance at checkout.
} href="/guides/affiliates">
Converting commission into store credit.
} href="/api-reference/endpoint/customers/get-list">
List, search, create and adjust balances.
# How Delivery Works
Source: https://docs.komerza.com/guides/delivery/introduction
Understanding Komerza's automated delivery system for digital goods
Komerza's delivery system is designed to handle digital goods efficiently, ensuring your customers receive their products immediately after purchase. The platform supports several delivery methods which can be used individually or combined to suit your specific product needs.
## Delivery Methods
Each product variant can be configured with one or more of the following delivery types:
### 1. License Keys (`license_keys`)
Ideal for selling unique codes such as software licenses, game activation keys, gift cards, or account credentials.
* **Mechanism:** You upload a pool of keys (one per line). When an order is placed, the system automatically reserves the required number of keys and assigns them to the customer.
* **Stock Control:** Your stock level is determined by the number of keys remaining in your pool.
* **Security:** Each key is issued only once. The system prevents duplicate assignments inherently.
* **Customer Experience:** Keys are displayed immediately on the order confirmation page and sent via email.
### 2. File Downloads (`files`)
Perfect for static content like eBooks, music albums, software installers, or design assets.
* **Mechanism:** You upload files directly to the product variant.
* **Access:** Customers receive secure download links generated specifically for their order. These files are permanently accessible via the customer dashboard.
* **Storage:** Files are hosted securely by Komerza, removing the need for you to manage bandwidth or servers.
### 3. Dynamic Delivery (`dynamic`)
Designed for developers and advanced use-cases requiring real-time generation of goods, such as on-demand accounts, custom license generation, or external system integrations.
* **Mechanism:** Komerza sends a webhook POST request to your specified endpoint with order details.
* **Response:** Your server processes the request (e.g., creates an account) and returns the delivery content in the response body.
* **Automation:** Komerza relays your server's response directly to the customer as their delivered item.
For detailed implementation steps on Dynamic Delivery, please refer to our
[Dynamic Delivery Guide](/guides/dynamic-delivery).
### 4. Instructions (`instructions`)
Useful for providing context, setup guides, or manual service details alongside other delivery items.
* **Mechanism:** A simple text field where you can write custom messages (Markdown supported).
* **Usage:** "How to redeem" guides, links to Discord communities, or support contact details.
* **Visibility:** This message appears prominently in the delivery email and order page.
## Combining Delivery Methods
You are not limited to a single delivery type. Komerza allows you to mix and match methods to create a complete product experience.
**Common Combinations:**
* **Software Product:** `License Key` (activation code) + `File Download` (installer).
* **Service:** `Dynamic` (account generation) + `Instructions` (login URL and welcome message).
* **Game Key:** `License Key` (Steam code) + `Instructions` (activation steps).
## The Delivery Process
The automated flow ensures zero-touch fulfillment for your digital business:
1. **Purchase:** Customer completes payment.
2. **Confirmation:** Komerza verifies the transaction (instant for cards/crypto).
3. **Assignment:**
* **Keys:** Reserved from your uploaded pool.
* **Files:** Secure links generated.
* **Dynamic:** Webhook fired and response captured.
4. **Fulfillment:** An email is sent immediately to the customer containing all delivered items, and the order status is updated to "Delivered".
# Discord Roles
Source: https://docs.komerza.com/guides/discord-roles
Grant Discord server roles automatically when customers buy, using the Komerza bot or your own Discord application
Connect a Discord server to your store and Komerza grants roles automatically when an order is delivered. Customers link their Discord account at checkout, get added to your server, and receive the role attached to the variant they bought. Refunds and chargebacks can take the role back.
You can use the **Komerza bot** (one click, nothing to configure) or your **own Discord application** (your branding, your bot, full control). This guide covers both, with the custom app in full.
Discord is managed in the dashboard under **Discord**.
## Komerza bot vs custom application
| | Komerza bot | Custom application |
| ------------------------------ | ----------------------------- | ------------------------------------------------------- |
| Setup | One click, authorize and done | Create a Discord app, invite the bot, paste four values |
| Bot identity in your server | Komerza's bot | Yours: your name, avatar, branding |
| What customers see at checkout | Authorizing "Komerza" | Authorizing your application |
| Redirect URL registration | Handled for you | **You must add it yourself - see below** |
Use a custom app if you want the bot in your server to carry your own branding, or you already run a bot and want one application for everything.
## Setting up a custom Discord application
1. Go to the [Discord Developer Portal](https://discord.com/developers/applications) and click **New Application**
2. Give it a name - this is what customers see on the authorization screen - and create it
3. Open the **OAuth2** tab and copy the **Client ID** and **Client Secret**
Still on the **OAuth2** tab, under **Redirects**, add this URL exactly, with no trailing slash:
```
https://checkout.komerza.com/callback/discord
```
This is the step people miss. Customer account linking runs through
Komerza's checkout using **your** application's client ID, so Discord
rejects the login with an invalid redirect URI error unless this exact URL
is registered on your app. Nothing in the setup form will warn you - it
fails later, at checkout, for your customers.
1. Open the **Bot** tab and add a bot to the application
2. Click **Reset Token**, then copy the **Bot Token**
Keep the token secret. Anyone holding it controls the bot.
Build an invite URL with your client ID and the permissions Komerza needs:
```
https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&scope=bot&permissions=268435459
```
| Permission | Why it's needed |
| ------------------------- | ---------------------------------------------------- |
| **Manage Roles** | Granting and removing the roles customers buy |
| **Create Instant Invite** | Discord requires it to add a customer to your server |
| **Kick Members** | Removing a blacklisted customer from your server |
Kick Members is only ever used when you turn on **kick from Discord on
block** in your fraud settings. Leave that off and the bot never removes
anyone - but grant the permission anyway, or turning it on later fails
silently.
Open the URL, pick your server, and authorize.
**Drag the bot's role above every role it will hand out.** Discord only
lets a bot manage roles below its own highest role, so a bot sitting at
the bottom of the list silently fails to grant anything.
In Discord, enable **Settings → Advanced → Developer Mode**, then right-click
your server and choose **Copy Server ID**.
In the dashboard go to **Discord**, click **Use Custom Bot**, and fill in:
* **Server (Guild) ID**
* **Client ID**
* **Client Secret**
* **Bot Token**
Komerza checks the token can reach your server before saving. If it can't,
you'll see "Could not access the guild. Ensure the bot is added to the
server and has the correct permissions." - that means the bot isn't in the
server, or the token is wrong.
## Using the Komerza bot instead
On the **Discord** page, click the connect button and authorize the Komerza bot into your server. There is nothing to register and no credentials to paste. You can switch to a custom app later by disconnecting first.
## Settings
Three store-wide settings on the Discord page, each overridable per product.
* **Add and grant after payment** (default) - the customer is added to your server and given the role only once payment completes.
* **Add on link, grant after payment** - the customer joins your server as soon as they link Discord at checkout, but the role waits for payment.
The second is useful if you want buyers in your server early, in a
welcome or support channel, before they have paid.
* **Required** - the customer cannot check out without linking Discord.
* **Optional** (default) - offered, not enforced.
* **Disabled** - not shown at checkout.
Set **Required** only for products that are worthless without the role.
Every extra step at checkout costs you conversions.
* **Revoke and remove** (default) - the role is removed and the customer is kicked from the server.
* **Revoke role only** - the role is removed, the customer stays in the server.
* **Do nothing** - the customer keeps the role.
## Assigning roles to what you sell
Roles attach to **variants**, not products, so different tiers can grant different roles. Komerza reads the role list straight from your server, so create the roles in Discord first.
Per-product overrides let one product require Discord while the rest of your catalogue doesn't, or change the join mode for a single product. Anything left unset falls back to the store default.
## Grants
The **Grants** tab lists every role Komerza has handed out, with the customer, the role and the order behind it. You can **Revoke** a grant manually - useful when someone breaks your rules but hasn't refunded.
## Troubleshooting
| Problem | Fix |
| -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| Customers get an invalid redirect URI error when linking | `https://checkout.komerza.com/callback/discord` is missing from your application's **Redirects** list, or has a typo or trailing slash |
| "Could not access the guild" when connecting | The bot isn't in the server, the bot token is wrong, or the Server ID belongs to a different server |
| Roles are never granted | The bot's role is below the role it's granting - move it up - or the bot is missing **Manage Roles** |
| Customers aren't added to the server | The bot is missing **Create Instant Invite**, which Discord requires for adding members |
| No roles appear in the picker | The roles don't exist in the server yet, or the bot cannot see them |
| Nothing happens after a refund | Check **Revocation Behavior** - it may be set to Do nothing |
## Next steps
} href="/guides/products-and-variants">
Roles are assigned per variant.
} href="/guides/orders">
Refunds drive role revocation.
} href="/guides/notifications">
Discord alerts for store events are configured separately.
} href="/guides/subscriptions">
Expiring subscriptions can revoke access too.
# Dynamic Delivery
Source: https://docs.komerza.com/guides/dynamic-delivery
Create custom fulfillment workflows by handling product delivery programmatically via webhooks
## Overview
Dynamic Delivery allows you to programmatically handle product fulfillment by implementing a webhook endpoint that receives order information and returns delivery content. This is ideal for delivering digital products, license keys, game codes, or integrating with external fulfillment systems.
When a customer purchases a product with dynamic delivery enabled, Komerza immediately sends a POST request to your configured webhook URL with order details, and your endpoint responds with the content to deliver to the customer.
Dynamic Delivery is configured per product variant in your product settings
under delivery methods.
## Use Cases
Deliver license keys, download links, or access codes in real-time
Connect with external fulfillment systems or inventory management
Implement complex delivery rules based on customer, product, or order data
Deliver game keys, activation codes, or in-game items automatically
## How It Works
1. **Customer Purchases** - A customer completes checkout for a product with dynamic delivery enabled
2. **Webhook Triggered** - Komerza sends a POST request to your configured webhook URL
3. **Your Response** - Your endpoint processes the request and returns the delivery content
4. **Customer Receives** - The returned content is delivered to the customer, either as a
single item or as one item per unit - see [Response Format](#response-format)
## Configuration
### Setting Up Dynamic Delivery
1. Navigate to your product in the Komerza Dashboard
2. Select the variant you want to configure
3. Choose **Dynamic Delivery** as the delivery method
4. Enter your webhook endpoint URL
5. Generate and save your webhook secret
Keep your webhook secret secure. It's used to verify that requests are
genuinely from Komerza.
## Webhook Request
### Request Headers
```
Content-Type: application/json
Accept: application/json
User-Agent: Komerza/1.0
X-Signature:
```
### Signature Verification
All webhook requests include an `X-Signature` header containing an HMAC SHA256 signature. You should verify this signature to ensure the request is from Komerza.
**Signature Calculation:**
```
HMAC-SHA256(secret, request_body) -> HEX encoded
```
```javascript Node.js theme={null}
const crypto = require("crypto");
function verifySignature(secret, body, signature) {
const calculatedSignature = crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex")
.toUpperCase();
return calculatedSignature === signature.toUpperCase();
}
// Express middleware example
app.use("/webhook", express.raw({ type: "application/json" }));
app.post("/webhook", (req, res) => {
const signature = req.headers["x-signature"];
const body = req.body.toString("utf8");
if (!verifySignature(process.env.WEBHOOK_SECRET, body, signature)) {
return res.status(401).send("Invalid signature");
}
// Process webhook...
});
```
```python Python theme={null}
import hmac
import hashlib
def verify_signature(secret: str, body: str, signature: str) -> bool:
calculated_signature = hmac.new(
secret.encode('utf-8'),
body.encode('utf-8'),
hashlib.sha256
).hexdigest().upper()
return calculated_signature.upper() == signature.upper()
# Flask example
from flask import Flask, request, abort
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Signature')
body = request.get_data(as_text=True)
if not verify_signature(os.environ['WEBHOOK_SECRET'], body, signature):
abort(401, 'Invalid signature')
# Process webhook...
```
```php PHP theme={null}
Webhook()
{
using var reader = new StreamReader(Request.Body);
var body = await reader.ReadToEndAsync();
var signature = Request.Headers["X-Signature"].ToString();
if (!VerifySignature(_configuration["WebhookSecret"], body, signature))
{
return Unauthorized("Invalid signature");
}
// Process webhook...
}
```
### Payload Structure
The webhook receives a JSON payload with the following structure:
```json theme={null}
{
"storeId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"customerId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"lineItemId": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"productId": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"variantId": "8f7e6d5c-4b3a-2918-7654-3210fedcba98",
"quantity": 1,
"order": {
"id": "5f3a8b2c-1d4e-5f6a-7b8c-9d0e1f2a3b4c",
"totalPrice": 29.99,
"currency": "USD",
"customer": {
"email": "customer@example.com",
"name": "John Doe"
},
"items": [
{
"id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"productName": "Premium License",
"variantName": "1 Year",
"quantity": 1,
"price": 29.99
}
],
"createdAt": "2025-11-27T10:30:00Z"
}
}
```
### Payload Fields
The unique identifier of your store
The unique identifier of the customer who made the purchase
The unique identifier of the specific line item in the order
The unique identifier of the product being delivered
The unique identifier of the product variant being delivered
The quantity of items purchased for this line item
Complete order information including customer details, all items, and payment
information. See the Order object in the [API
Reference](/api-reference/endpoint/orders/get) for full schema details.
## Webhook Response
### Response Format
Your endpoint can answer in one of two ways. Which you choose matters when a single
purchase covers more than one unit.
#### Plain text - one delivery
Respond with **plain text** (`text/plain`) containing the delivery content. It is shown
to the customer exactly as returned.
```http theme={null}
HTTP/1.1 200 OK
Content-Type: text/plain
LICENSE-KEY-ABC123-XYZ789-PREMIUM
Download: https://example.com/download/abc123
Valid until: 2026-11-27
```
A plain-text body is always treated as **one** delivered item, however many lines it
contains. That is deliberate: a multi-line body is usually one thing - an account with
its password on the next line - and splitting it would hand the customer half a login.
Because a plain-text body is one delivery, a customer who buys a quantity of ten
receives a single item. If you want them to receive ten, use the JSON form below.
#### JSON - one delivery per unit
Respond with a JSON object containing an `items` array, and each entry becomes a
separate delivered item. Use this when a purchase of ten should hand over ten distinct
keys rather than one block of text.
```http theme={null}
HTTP/1.1 200 OK
Content-Type: application/json
{
"items": [
"LICENSE-KEY-ABC123",
"LICENSE-KEY-DEF456",
"LICENSE-KEY-GHI789"
]
}
```
The request payload tells you how many to return: read the top-level `quantity` field
(see [Payload Fields](#payload-fields)) and return that many entries.
This form is required if resellers buy your stock in bulk. Buying stock up front only
works when each unit arrives as its own item, so a supplier answering in plain text
can only ever deliver one, and the rest of the purchase is refunded.
How the JSON form is detected, exactly:
* The body must begin with `{` (after any leading whitespace) and parse as a JSON object
containing an `items` array. Anything else is treated as plain text.
* Entries that are JSON strings are delivered as-is. Entries of any other type - an
object, for example - are delivered as their raw JSON.
* Blank and whitespace-only entries are skipped. If that leaves no usable entries, the
whole body is treated as plain text instead.
### Response Requirements
`text/plain` for a single delivery, or `application/json` for the `items` form. The
body is inspected either way, so a mislabelled content type does not by itself break
delivery.
Must be `200` for successful delivery. Any other status code will be treated
as a failure and trigger retry logic.
The actual delivery content to show the customer. Can be multi-line. Each delivered
item is stored up to 10,000 characters and anything beyond that is cut off, so keep
individual items comfortably below it.
### Example Implementations
```javascript Node.js/Express theme={null}
const express = require("express");
const crypto = require("crypto");
const app = express();
// Important: Use raw body parser for signature verification
app.use("/webhook", express.raw({ type: "application/json" }));
app.post("/webhook", async (req, res) => {
const signature = req.headers["x-signature"];
const body = req.body.toString("utf8");
// Verify signature
if (!verifySignature(process.env.WEBHOOK_SECRET, body, signature)) {
return res.status(401).send("Invalid signature");
}
// Parse the payload
const payload = JSON.parse(body);
// Generate or fetch delivery content based on your logic
const licenseKey = await generateLicenseKey(
payload.productId,
payload.customerId,
);
const downloadUrl = await createDownloadLink(payload.variantId);
// Return plain text delivery content
res.setHeader("Content-Type", "text/plain");
res
.status(200)
.send(
`License Key: ${licenseKey}\n` +
`Download: ${downloadUrl}\n` +
`Valid for: 1 Year\n` +
`Customer: ${payload.order.customer.email}`,
);
});
function verifySignature(secret, body, signature) {
const calculatedSignature = crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex")
.toUpperCase();
return calculatedSignature === signature.toUpperCase();
}
app.listen(3000);
```
```python Python/Flask theme={null}
from flask import Flask, request, abort
import hmac
import hashlib
import json
import os
app = Flask(__name__)
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Signature')
body = request.get_data(as_text=True)
# Verify signature
if not verify_signature(os.environ['WEBHOOK_SECRET'], body, signature):
abort(401, 'Invalid signature')
# Parse payload
payload = json.loads(body)
# Generate delivery content
license_key = generate_license_key(payload['productId'], payload['customerId'])
download_url = create_download_link(payload['variantId'])
# Return plain text response
response = f"""License Key: {license_key}
Download: {download_url}
Valid for: 1 Year
Customer: {payload['order']['customer']['email']}"""
return response, 200, {'Content-Type': 'text/plain'}
def verify_signature(secret: str, body: str, signature: str) -> bool:
calculated = hmac.new(
secret.encode('utf-8'),
body.encode('utf-8'),
hashlib.sha256
).hexdigest().upper()
return calculated == signature.upper()
if __name__ == '__main__':
app.run(port=3000)
```
```php PHP theme={null}
### Honouring Quantity
The examples above return one delivery regardless of how many units were bought. To hand
over one item per unit, read `quantity` from the payload and return that many entries.
```python Python theme={null}
@app.route('/webhook', methods=['POST'])
def dynamic_delivery():
payload = json.loads(request.get_data(as_text=True))
quantity = payload.get('quantity', 1)
keys = [
generate_license_key(payload['productId'], payload['customerId'])
for _ in range(quantity)
]
return jsonify({'items': keys}), 200
```
```php PHP theme={null}
$keys]);
```
```go Go theme={null}
func handleWebhook(w http.ResponseWriter, r *http.Request) {
var payload WebhookPayload
json.NewDecoder(r.Body).Decode(&payload)
quantity := payload.Quantity
if quantity < 1 {
quantity = 1
}
keys := make([]string, 0, quantity)
for i := 0; i < quantity; i++ {
keys = append(keys, generateLicenseKey(payload.ProductID, payload.CustomerID))
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string][]string{"items": keys})
}
```
Return exactly `quantity` entries. Returning fewer means the customer is charged for
units they did not receive, and for a reseller buying stock the shortfall is refunded
rather than delivered.
## Timeout and Retry Logic
### Timeout
Your webhook endpoint has **20 seconds** to respond. If your endpoint doesn't respond within this timeframe, the request will be considered failed and will be retried.
For operations that take longer than 20 seconds, consider using an
asynchronous pattern: 1. Immediately return a success response 2. Process the
delivery in the background 3. Use the Komerza API to update the delivery
status when ready
### Retry Policy
Komerza implements an automatic retry mechanism with exponential backoff for failed webhook deliveries:
* **Retry Attempts:** 3 automatic retries
* **Backoff Strategy:** Exponential (2^retry seconds)
* 1st retry: after 2 seconds
* 2nd retry: after 4 seconds
* 3rd retry: after 8 seconds
### Circuit Breaker
To protect your endpoint from being overwhelmed, Komerza implements a circuit breaker:
* **Threshold:** 5 consecutive failures
* **Break Duration:** 30 seconds
* **Behavior:** After 5 consecutive failures, requests are paused for 30 seconds before attempting again
Ensure your endpoint is highly available. Repeated failures may trigger the
circuit breaker, temporarily preventing delivery webhooks from being sent.
## Error Handling
### Common Error Scenarios
**Cause:** The signature verification failed
**Solution:**
* Verify you're using the correct webhook secret
* Ensure you're reading the raw request body (not parsed JSON)
* Check your HMAC implementation matches the algorithm (SHA256, HEX encoded)
**Cause:** Your endpoint didn't respond within 20 seconds **Solution:** -
Optimize your delivery generation logic - Use caching for frequently accessed
data - Consider asynchronous processing for complex operations
**Cause:** Your endpoint returned an error response **Solution:** - Check your
application logs for errors - Implement proper error handling and logging -
Validate the payload structure before processing
**Cause:** Too many consecutive failures (5+)
**Solution:**
* Check your server health and availability
* Review error logs to identify the root cause
* Implement health checks and monitoring
* The circuit breaker will automatically reset after 30 seconds
### Logging and Debugging
Komerza logs all webhook requests and responses for debugging purposes:
* **Request Body:** Stored (truncated to 8,192 characters)
* **Response Body:** Stored (truncated to 900 characters)
* **Response Code:** Recorded for each attempt
* **Signature:** Stored for verification
You can view these logs in your Komerza Dashboard under Webhook Execution Logs.
## Testing
### Test Webhook Locally
Use a tool like [ngrok](https://ngrok.com/) or [cloudflared](https://github.com/cloudflare/cloudflared) to expose your local development server:
```bash theme={null}
# Start your local server
npm start # or your framework's dev command
# In another terminal, start ngrok
ngrok http 3000
# Use the ngrok URL (e.g., https://abc123.ngrok.io/webhook) in your Komerza product configuration
```
### Manual Testing
You can manually test your webhook endpoint by simulating a request:
```bash theme={null}
# Generate signature
SECRET="your-webhook-secret"
PAYLOAD='{"storeId":"...","customerId":"...","order":{...}}'
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print toupper($2)}')
# Send test request
curl -X POST https://your-endpoint.com/webhook \
-H "Content-Type: application/json" \
-H "X-Signature: $SIGNATURE" \
-d "$PAYLOAD"
```
### Production Testing
Test in your development/staging environment first before enabling dynamic
delivery in production.
1. Create a test product with dynamic delivery enabled
2. Point it to your staging webhook endpoint
3. Make a test purchase (use test mode if available)
4. Verify the delivery content is generated correctly
5. Check webhook execution logs in your dashboard
## Support
Browse our knowledge base and guides
Get help from the community and Komerza team
Check webhook and API system status
Join our Telegram community for support
# Elements
Source: https://docs.komerza.com/guides/elements
Build checkout into your own page - cart, email, payment methods and the pay button as separate pieces you place and style yourself
**Elements is in public beta.** It works and it takes real payments, but the API can still change
and a few things are missing (see [Known limits](#known-limits) at the bottom). If you build on it
now, pin a copy of your integration code somewhere you can find it again, and tell us what breaks.
## What this is
The [Embed SDK](/guides/embed-sdk) opens our checkout in a modal on top of your page. Elements is the
other option: you get the checkout as separate pieces - the cart summary, the email field, the
payment method list, the pay button - and you decide where each one goes and what it looks like.
The obvious question is why you'd want that when the modal already works. Two reasons, and if
neither applies to you, use the Embed SDK, it's less work:
* **The checkout is part of your page**, not a layer over it. It sits in your layout, in your
column, next to your product photography.
* **It wears your brand.** Your colours, your typeface, your corner radius, your spacing.
The trade-off is that you're now responsible for the layout, and there's more to get wrong.
A pretend shop with the all-in-one element in its sidebar, wearing that shop's brand
## Quick start
One script tag, no build step, no package to install:
```html theme={null}
```
Then a div to mount into, and four lines:
```html theme={null}
```
That's a complete, working checkout: cart, email, payment methods, terms, and a pay button. It's the
`checkout` element - everything in one piece. Most integrations should start here and only split it
up if they actually need to.
## The elements
`elements.create(type)` takes one of these:
| Type | What it renders |
| ---------------- | -------------------------------------------------------------------------- |
| `checkout` | All of it in one element - cart, email, payment methods, terms, pay button |
| `summary` | The cart: line items, quantity controls, discounts, total |
| `email` | The buyer's email address, with validation |
| `payment` | The payment method list, and the chosen gateway rendered in place |
| `coupon` | A coupon code field |
| `consent` | Terms acceptance and the marketing opt-in |
| `billingAddress` | Billing address, where the store or gateway requires one |
| `customFields` | Any custom fields configured on the product |
Every element you create from the same `elements(...)` call shares one cart. Change the quantity in
`summary` and the total in `checkout` updates on its own - you don't wire that up.
```js theme={null}
const elements = komerza.elements({ items: [...] });
elements.create("summary").mount("#summary");
elements.create("email").mount("#email");
elements.create("payment").mount("#payment");
```
## How it works
Worth two minutes, because it explains most of the rules further down.
Each element is an iframe served from `checkout.komerza.com`. Card numbers, the buyer's email and
the order itself never touch your page or your JavaScript - which is the point, and it's what keeps
your PCI scope where it is.
Alongside your elements we mount one hidden frame, the **controller**. It owns the cart, talks to our
backend, and is the only thing allowed to create an order. Your elements don't talk to each other
directly; they send changes to the controller and it tells everyone what the new state is. That's why
two elements can never disagree about the total.
Two consequences you'll actually notice:
* **Your JavaScript can't reach inside an element.** Cross-origin, deliberately. Everything you need
comes through `elements.on(...)` and the methods below.
* **Elements paint no background of their own**, in any theme. They inherit whatever's behind them,
so a checkout in a white card looks like part of that card.
## Styling
This is the part Elements exists for.
### Tokens
`tokens` are the broad strokes. Set an accent colour and we regenerate the whole primary ramp from
it, so it reaches everything that reads that ramp rather than just the pay button.
```js theme={null}
const elements = komerza.elements({
items: [...],
brand: {
mode: "light", // "light" | "dark" | "auto" (default)
tokens: {
accent: "#b2452a", // hex - the full ramp is derived from this
text: "#221f1c",
textMuted: "#6b635a",
border: "#ddd5c9",
surface: "#ffffff",
surfaceSunken: "#f7f5f2",
danger: "#b91c1c",
success: "#15803d",
warning: "#b45309",
radius: "2px",
gap: "1rem",
font: "Inter, system-ui, sans-serif",
fontMono: "ui-monospace, Menlo, monospace"
}
}
});
```
Set `mode` explicitly if your page is always light or always dark. The default follows the
buyer's OS setting, so a permanently light page viewed by someone in dark mode gets a dark
checkout on a white panel.
### Fonts
An element is a separate document from your page, so a font your page loads is **not** available to
it - the checkout will quietly fall back to a system face. Load fonts into the elements explicitly:
```js theme={null}
brand: {
fonts: [
"Inter:wght@400;500;600",
{ family: "Fraunces", weights: [500, 700] },
"https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@700"
],
tokens: { font: "Inter, system-ui, sans-serif" }
}
```
Google Fonts only, and we rebuild the URL from the parts rather than using what you pass. Any other
stylesheet would be applied to a payment page without passing through the rules sanitizer below,
which would defeat the whole thing.
### Rules
`rules` is CSS, scoped to a fixed vocabulary of class names:
```js theme={null}
brand: {
rules: {
".kmrza-button": { "border-radius": "2px", "letter-spacing": "0.04em" },
".kmrza-field-input": { "border-radius": "2px", "border-color": "#ddd5c9" },
".kmrza-method": { "border-radius": "2px" },
".kmrza-method[data-selected]": {
"border-color": "#b2452a",
"background-color": "#fbf3f0"
},
".kmrza-total-amount": { "font-family": "Fraunces, Georgia, serif" }
}
}
```
Selectors must be a single class from the list below, optionally narrowed by one state attribute
(`[data-selected]`, `[data-disabled]`, `[data-loading]`, `[data-invalid]`, `[data-locked]`) and one
pseudo-class (`:hover`, `:focus`, `:focus-visible`, `:active`, `:disabled`, `:checked`). No
descendant selectors, no element names, no ids.
That's narrower than real CSS on purpose. A checkout that can be restyled arbitrarily can be made to
lie about what it charges - hide the total, cover the pay button, dress a decline up as a success. So
a handful of properties are refused (`position`, `content`, `z-index`, `pointer-events`, `transform`,
`clip-path`, and anything that fetches a URL), and `!important` is stripped. Everything cosmetic is
yours.
**Anything we refuse is reported, never dropped silently:**
```js theme={null}
elements.on("trace", (e) => console.warn(e.message));
// → brand: Rule ".foo" refused - .foo is not a Komerza hook.
```
### The class names
| Group | Classes |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Fields | `.kmrza-field` `.kmrza-field-label` `.kmrza-field-input` `.kmrza-field-hint` `.kmrza-field-error` |
| Buttons | `.kmrza-button` `.kmrza-button-primary` `.kmrza-button-secondary` `.kmrza-button-outline` `.kmrza-button-ghost` `.kmrza-button-danger` |
| Cart | `.kmrza-summary` `.kmrza-line-item` `.kmrza-line-item-image` `.kmrza-line-item-name` `.kmrza-line-item-option` `.kmrza-line-item-qty` `.kmrza-line-item-price` |
| Totals | `.kmrza-discount` `.kmrza-total` `.kmrza-total-label` `.kmrza-total-amount` |
| Payment | `.kmrza-payment` `.kmrza-method-list` `.kmrza-method` `.kmrza-method-icon` `.kmrza-method-label` `.kmrza-method-badge` `.kmrza-method-panel` |
| Gateway UI | `.kmrza-panel` `.kmrza-heading` `.kmrza-subheading` `.kmrza-alert` `.kmrza-alert-title` `.kmrza-alert-text` `.kmrza-badge` `.kmrza-spinner` |
| Element roots | `.kmrza-checkout` `.kmrza-coupon` `.kmrza-consent` `.kmrza-billing` `.kmrza-custom-fields` `.kmrza-protection` `.kmrza-skeleton` |
| Misc | `.kmrza-email` `.kmrza-disclosure` `.kmrza-interval` `.kmrza-overlay` `.kmrza-overlay-panel` `.kmrza-overlay-close` |
These are a promise. Once a class is on this list it keeps existing and keeps meaning the same thing,
even as we rewrite what's behind it.
### Starting from nothing
If you'd rather build the whole appearance yourself, `preset: "bare"` strips our cosmetics down to
structure - no borders, no backgrounds, no radius, no padding:
```js theme={null}
brand: { preset: "bare", rules: { /* all yours */ } }
```
### Changing the brand later
```js theme={null}
elements.update({ brand: { tokens: { accent: "#0f766e" } } });
```
Restyles in place. No remount, no flash.
## Events
```js theme={null}
elements.on("error", (e) => {
console.error(e.type, e.code, e.message);
});
```
| Event | When |
| ----------- | --------------------------------------------------------------------------- |
| `ready` | An element has loaded and is interactive |
| `change` | The buyer changed something - method chosen, quantity edited, email entered |
| `error` | Anything went wrong, anywhere |
| `resize` | An element changed height |
| `focus` | An element took focus |
| `blur` | An element lost focus |
| `navigate` | The buyer is being handed to a gateway that needs its own window |
| `trace` | Diagnostics, including refused brand rules |
| `loaderror` | An element's frame failed to load at all |
`error` is the one to handle. Everything that can fail arrives there in one shape, so you don't have
to watch each element separately:
```js theme={null}
elements.on("error", (e) => {
// e.type - "load" | "session" | "validation" | "order" | "payment"
// e.code - the backend's own code, when there is one: "OUT_OF_STOCK",
// "EMAIL_DOMAIN_REJECTED", "COUPON_INVALID"
// e.element - which element raised it, if it came from one
// e.recoverable - true when the buyer can just try again
});
```
Handle `e.code` where you can. It's the part that tells you what to actually do - "out of stock"
wants a different response from "that card was declined".
## Taking payment
The `checkout` element has its own pay button and drives the whole thing. Nothing else to do.
If you've split the elements up and want your own button, call `confirmPayment` yourself:
```js theme={null}
const result = await komerza.confirmPayment({
elements,
returnUrl: "https://yourshop.com/thank-you"
});
if (result.error) {
console.error(result.error.code, result.error.message);
} else {
console.log("order", result.order.id);
}
```
`returnUrl` matters for the gateways that take over the whole window - without it there's nowhere to
send the buyer back to. Most gateways render inline and never leave your page, but some can't.
### Letting the buyer change their mind
Once an order exists the cart is locked, so a buyer who spots a typo isn't stuck:
```js theme={null}
elements.edit(); // unlocks the cart and returns to the form
```
## A complete example
```html theme={null}
Feldspar Supply
Kiln-fired stoneware
```
## Content Security Policy
If your site sends a CSP, elements need two directives:
```
frame-src https://checkout.komerza.com;
script-src https://checkout.komerza.com;
```
Using nonces? Pass yours in and we'll put it on every frame we create:
```js theme={null}
const komerza = Komerza(undefined, { nonce: "abc123" });
```
## Known limits
Straight list of what isn't there yet, so you don't find out the hard way.
* **No npm package or React wrapper.** Script tag only for now.
* **No currency selector element.** Adaptive currency still works; there just isn't a picker.
* **The API can change.** It's a beta. We'll tell you before anything breaks, but treat it as
provisional.
## Which one should I use?
You want a buy button that opens checkout in a modal. It's a script tag and a data attribute,
and it's done in five minutes.
You want checkout inside your page, laid out and styled as part of your design, and you're
willing to own the layout.
# Email Marketing
Source: https://docs.komerza.com/guides/email-marketing
Send campaigns to your customers from a domain you own (Business plan)
Email Marketing is Komerza's built-in bulk email tool, in the dashboard sidebar under **Email Marketing** with pages for Overview, Campaigns, Templates, Recipients, Domains and Unsubscribed.
Email Marketing requires the **Business plan**. There is no application or
approval step: upgrading unlocks it instantly, and downgrading locks it
instantly. On the native mobile app the feature is hidden for non-Business
accounts, so use the web dashboard.
Team members need the **View Email Marketing** permission to see the pages and **Update Email Marketing** to change anything.
## Your monthly allowance
* Business includes **50,000 marketing emails per month per account**, shared across all your stores rather than per store.
* The allowance resets on your plan's billing anniversary. Unused emails do not roll over, and extra cannot be purchased.
* Only marketing campaigns consume it. Transactional customer emails - order and subscription emails - are unlimited and never count against it.
**Email Marketing → Overview** shows Emails Sent, Credits Remaining, Usage and your reset date. "Credits" simply means units of the monthly allowance; there are no credit packs to buy.
A campaign that would exceed your remaining allowance is refused up front: "Insufficient email quota. Need `{N}` but only `{M}` remaining this month."
## Verifying a sending domain
Campaigns must be sent from an address on a domain your store has verified.
**Email Marketing → Domains** → **Add Domain**.
The page lists **3 DKIM CNAME records**, **1 MX record** and **1 SPF TXT
record** (both on `mail.`), plus a recommended **DMARC TXT
record**.
Status updates automatically as DNS propagates, or press the check button. A
domain shows **Verified** only once every check passes; until then it is
**Pending**, with per-check guidance.
Both the From address and any Reply-To must use a verified domain, and verification is re-checked when a campaign actually sends.
Domain names are unique across the platform, so "This domain is already
registered." can mean another store claimed it - contact support if you own
it. You also cannot delete a domain while campaigns using it are Sending or
Scheduled.
## Templates and recipients
Design emails on **Templates** with the drag-and-drop editor, which has desktop, tablet and mobile previews, undo/redo and a code view.
Upload images as assets (up to 5 MB each) rather than pasting them into the
design. Embedded images are the usual cause of "This template is too large to
save".
Personalise with lowercase merge tags:
```
{{first_name}} {{last_name}} {{email}}
{{store_name}} {{store_url}} {{unsubscribe_url}}
```
**Recipients** holds your lists. **All Customers** is the default list, auto-synced from customers who opted into marketing at checkout. It re-syncs before every send and cannot be deleted. You can also create custom lists and add recipients by email, deduplicated automatically. A campaign cannot be sent to an empty list.
## Campaigns
Create a campaign with a name, subject, preview text, From name, From email on a verified domain, an optional Reply-To, a template and a recipient list. It starts as **Draft**, and from there you can **Schedule** it (at least five minutes ahead) or **Send Now**.
Editable and not yet queued.
Queued for a future time. Still cancellable.
In progress. **Cannot be cancelled from the dashboard.**
Finished. Note that a campaign which exhausts your allowance mid-send still
shows Sent, so it can reach fewer people than the list size.
Held by automated screening before sending. Only Komerza staff can release it.
The reason is not shown in the dashboard; support has to look it up.
At send time Komerza re-checks your plan, allowance, domain verification and list contents. Emails go out gradually in small batches to protect deliverability, so a large campaign can take hours and a long "Sending" state is normal.
Every campaign email automatically includes an unsubscribe footer and one-click unsubscribe support. You cannot remove it and do not need to add your own.
Per-campaign stats cover recipients, sent, delivered, opened, clicked, bounced and complaints.
## Suppressions
The **Unsubscribed** page (titled "Suppressions") lists addresses that will not receive your campaigns - unsubscribes, bounces and complaints, with type, reason and date. Suppressed addresses are skipped silently during sends, and suppression is per store.
You can remove **Unsubscribe**-type entries only: "Only unsubscribe
suppressions can be removed. Bounces and complaints are permanent." Sending to
addresses that bounce or complain damages the deliverability of every campaign
you send afterwards, which is why they are permanent.
## When to contact support
* "This domain is already registered." for a domain you own.
* A domain stuck **Pending** more than 48 to 72 hours after the DNS records are set exactly as shown.
* A campaign showing **Failed** or stuck **Under Review**.
* A "Sent" campaign where recipients report nothing arrived.
* A recipient reporting that an unsubscribe link does not work.
* A GDPR or data-erasure request from a recipient.
# Embed SDK
Source: https://docs.komerza.com/guides/embed-sdk
Integrate Komerza checkout directly into your website with our lightweight Embed SDK
## Overview
The Komerza Embed SDK allows you to seamlessly integrate our checkout experience directly into your website. Instead of redirecting customers to a separate checkout page, the embed creates a modal overlay that keeps users on your site while providing a secure, optimized payment flow.
See the Embed SDK in action with our interactive demo
Also integrate our analytics script to bring back visitor analytics on your
custom site for complete insights into your customer journey.
## Features
Modal overlay keeps customers on your site during checkout
Minimal JavaScript bundle with no external dependencies
Light, dark, and auto themes to match your site design
Full Content Security Policy support with nonce integration
## Quick Start
Add the following script to your website's `` or before the closing `` tag:
```html theme={null}
```
### Method 1: Data Attributes (Recommended for Simple Use Cases)
The easiest way to add checkout buttons to your site. Just add data attributes to any button or element:
```html theme={null}
```
#### Data Attributes Reference
| Attribute | Required | Description | Example |
| ------------------------ | -------- | --------------------------------------------------------------------------- | --------------------------------------------- |
| `data-kmrza-product-id` | ✅ Yes | The product ID from Komerza | `"0f6294be-25d6-4ced-96df-7d500608c54d"` |
| `data-kmrza-variant-id` | ❌ No | The variant ID from Komerza. Omit it to let the customer choose - see below | `"96f846e5-5f22-46a2-97b3-a1c88f685577"` |
| `data-kmrza-quantity` | ❌ No | Quantity to purchase (default: 1) | `"2"` |
| `data-kmrza-theme` | ❌ No | Theme mode: `light`, `dark`, or `auto` (default: `auto`) | `"dark"` |
| `data-kmrza-return-url` | ❌ No | Custom URL to redirect to after payment | `"https://example.com/thank-you"` |
| `data-kmrza-email` | ❌ No | Prefill the customer's email address | `"customer@example.com"` |
| `data-kmrza-coupon-code` | ❌ No | Prefill and auto-apply a coupon code | `"SAVE20"` |
| `data-kmrza-metadata` | ❌ No | JSON string of custom key-value metadata | `"{"campaign":"summer"}"` |
#### Letting the customer choose a variant
`data-kmrza-variant-id` is optional. If you omit it, checkout opens with a **variant picker** so the customer selects which variant they want before paying:
```html theme={null}
```
This is useful for a single buy button on a product with several editions, tiers or durations, where you would otherwise need one button per variant. Pass `data-kmrza-variant-id` when you want the customer to land on one specific variant with no choice to make.
### Method 2: JavaScript API (For Advanced Use Cases)
For more control, use the JavaScript API to programmatically open the checkout:
```html theme={null}
```
#### Multiple Items
You can add multiple products to the checkout at once:
```javascript theme={null}
Komerza.open({
items: [
{
productId: "0f6294be-25d6-4ced-96df-7d500608c54d",
variantId: "96f846e5-5f22-46a2-97b3-a1c88f685577",
quantity: 2,
},
{
productId: "54d6adb4-c160-4f73-b9c0-6c16970fff41",
variantId: "37e70705-c603-4ab2-8a2e-d16e80288c8c",
quantity: 1,
},
],
theme: "light",
});
```
## Complete Implementation Guide
### HTML Setup
Here's a complete HTML page example:
```html theme={null}
My Store
Amazing Product
Price: $29.99
```
### JavaScript Integration
```javascript theme={null}
// Initialize when page loads
document.addEventListener('DOMContentLoaded', function() {
Komerza.init();
// Bind to your buy button
document.getElementById('buy-now').addEventListener('click', function() {
Komerza.open({
items: [
{
productId: 'prod_123',
variantId: 'var_456',
quantity: 1
}
]
});
});
});
```
```javascript theme={null}
// Advanced integration with error handling and multiple products
class CheckoutManager {
constructor() {
this.isInitialized = false;
this.init();
}
init() {
// Initialize with CSP nonce
Komerza.init({
nonce: this.getCSPNonce()
});
this.isInitialized = true;
this.bindEvents();
}
bindEvents() {
// Bind to all buy buttons
document.querySelectorAll('[data-buy-button]').forEach(button => {
button.addEventListener('click', (e) => {
e.preventDefault();
this.handlePurchase(button);
});
});
// Optional: Bind to close events
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
Komerza.close();
}
});
}
handlePurchase(button) {
const productId = button.dataset.productId;
const variantId = button.dataset.variantId;
const quantity = parseInt(button.dataset.quantity) || 1;
if (!productId || !variantId) {
console.error('Missing product or variant ID');
return;
}
try {
Komerza.open({
items: [{
productId,
variantId,
quantity
}],
theme: this.getTheme()
});
} catch (error) {
console.error('Failed to open checkout:', error);
// Fallback to direct checkout URL
this.fallbackCheckout(productId, variantId, quantity);
}
}
getTheme() {
// Auto-detect theme based on user preference or site theme
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
return 'dark';
}
return 'light';
}
getCSPNonce() {
// Extract nonce from script tag or meta tag
const script = document.querySelector('script[nonce]');
return script ? script.getAttribute('nonce') : null;
}
fallbackCheckout(productId, variantId, quantity) {
// Fallback to direct checkout page if embed fails
const checkoutUrl = `https://checkout.komerza.com/product/${productId}/${variantId}?qty=${quantity}`;
window.location.href = checkoutUrl;
}
}
// Initialize when DOM is ready
document.addEventListener('DOMContentLoaded', function() {
new CheckoutManager();
});
```
```jsx theme={null}
import React, { useEffect, useState } from 'react';
// Declare the global Komerza object
declare global {
interface Window {
Komerza: {
init: (options?: { nonce?: string }) => void;
open: (options: EmbedOptions) => void;
close: () => void;
};
}
}
interface EmbedOptions {
items: Array<{
productId: string;
variantId: string;
quantity?: number;
}>;
theme?: 'light' | 'dark' | 'auto';
}
const ProductPage: React.FC = () => {
const [isEmbedReady, setIsEmbedReady] = useState(false);
useEffect(() => {
// Load the embed script
const script = document.createElement('script');
script.src = 'https://checkout.komerza.com/embed/embed.iife.js';
script.onload = () => {
window.Komerza.init();
setIsEmbedReady(true);
};
document.head.appendChild(script);
return () => {
// Cleanup script on unmount
document.head.removeChild(script);
};
}, []);
const handleBuyClick = () => {
if (!isEmbedReady) {
console.warn('Embed SDK not ready yet');
return;
}
window.Komerza.open({
items: [
{
productId: 'prod_123',
variantId: 'var_456',
quantity: 1
}
],
theme: 'auto'
});
};
return (
Amazing Product
Price: $29.99
);
};
export default ProductPage;
```
## API Reference
### `Komerza.init(options?)`
Initializes the embed and automatically binds all elements with `data-kmrza-*` attributes.
```javascript theme={null}
Komerza.init({
nonce: "YOUR_CSP_NONCE", // Optional: for Content Security Policy
});
```
**Options:**
* `nonce` (optional): CSP nonce for injected styles and scripts
**Note:** This is called automatically when using the JS API (`Komerza.open()`), but should be called explicitly if you're using data attributes.
### `Komerza.open(options)`
Opens the checkout modal programmatically.
```javascript theme={null}
Komerza.open({
items: [
{
productId: string, // Required
variantId?: string, // Optional, omit to show a variant picker
quantity?: number // Optional, default: 1
}
],
theme?: 'light' | 'dark' | 'auto', // Optional, default: 'auto'
affiliateCode?: string, // Optional, override affiliate tracking code
returnUrl?: string, // Optional, custom return URL after payment
email?: string, // Optional, prefill customer email
couponCode?: string, // Optional, prefill and apply coupon code
metadata?: Record // Optional, custom order metadata
});
```
**Parameters:**
* `items` (required): Array of products to add to checkout
* `productId` (required): Product identifier
* `variantId` (optional): Product variant identifier. Omit it and checkout shows a variant picker so the customer chooses
* `quantity` (optional): Number of items (default: 1)
* `theme` (optional): Color theme for the checkout modal
* `'auto'` - Matches user's system preference (default)
* `'light'` - Light mode
* `'dark'` - Dark mode
* `affiliateCode` (optional): Override affiliate tracking code, this will override any other affiliate tracking code whether that be from cookies or the URL.
* `returnUrl` (optional): Custom URL to redirect customers to after payment completion instead of the default store URL.
* `email` (optional): Prefill the customer's email address in the checkout form.
* `couponCode` (optional): Prefill and automatically apply a coupon code at checkout.
* `metadata` (optional): Attach custom key-value metadata to orders for tracking purposes (e.g., campaign tracking, user IDs, referral sources).
### `Komerza.close()`
Closes the checkout modal programmatically.
```javascript theme={null}
Komerza.close();
```
### Basic Button
```html theme={null}
```
### Dark Theme with Custom Quantity
```html theme={null}
```
### Full Options Example (Data Attributes)
```html theme={null}
```
### Full Options Example (JavaScript API)
```javascript theme={null}
Komerza.open({
items: [{ productId: "...", variantId: "...", quantity: 2 }],
theme: "dark",
affiliateCode: "partner123",
returnUrl: "https://example.com/thank-you",
email: "vip@example.com",
couponCode: "VIP25",
metadata: {
campaign: "vip-promotion",
source: "landing-page",
},
});
```
### Dynamic Cart with JavaScript
```html theme={null}
```
### React Integration
```jsx theme={null}
import { useEffect } from "react";
function CheckoutButton({ productId, variantId, quantity = 1 }) {
useEffect(() => {
// Load the script
const script = document.createElement("script");
script.src = "https://checkout.komerza.com/embed/embed.iife.js";
script.defer = true;
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
};
}, []);
const handleClick = () => {
if (window.Komerza) {
window.Komerza.open({
items: [{ productId, variantId, quantity }],
theme: "auto",
});
}
};
return ;
}
```
### Next.js Integration
```jsx theme={null}
// components/CheckoutButton.jsx
"use client";
import { useEffect } from "react";
import Script from "next/script";
export default function CheckoutButton({ productId, variantId, quantity = 1 }) {
const handleClick = () => {
if (window.Komerza) {
window.Komerza.open({
items: [{ productId, variantId, quantity }],
});
}
};
return (
<>
>
);
}
```
## TypeScript Support
Type definitions are included. You can use them like this:
```typescript theme={null}
import type {
EmbedOptions,
InitOptions,
} from "https://checkout.komerza.com/embed/embed.iife.js";
interface EmbedOptions {
items: EmbedItem[];
theme?: "light" | "dark" | "auto";
affiliateCode?: string;
returnUrl?: string;
email?: string;
couponCode?: string;
metadata?: Record;
}
const options: EmbedOptions = {
items: [
{
productId: "abc123",
variantId: "xyz789",
quantity: 1,
},
],
theme: "auto",
returnUrl: "https://example.com/thank-you",
email: "customer@example.com",
couponCode: "SAVE20",
metadata: {
campaign: "summer-sale",
source: "landing-page",
},
};
window.Komerza.open(options);
```
## Troubleshooting
Full troubleshooting guide: console errors, CSP, ad blockers, framework
gotchas, and what to send support.
### The checkout doesn't open
1. Make sure the script is loaded (`defer` attribute is recommended)
2. Check browser console for errors
3. Verify your product and variant IDs are correct
4. If using data attributes, ensure `Komerza.init()` is called after DOM is loaded
### Modal appears behind other elements
The modal uses a high z-index (9999). If it's still behind elements, check your CSS for competing z-index values.
### Theme doesn't apply
The `theme` parameter accepts only `'light'`, `'dark'`, or `'auto'`. Check for typos.
## Affiliates
The Komerza Embed SDK supports affiliate tracking out of the box:
* By default, the embed will automatically support deep linking using the `?ref` query parameter on any URL where it runs. For example, a visitor arriving at `https://example.com/?ref=aff_12345` will have `aff_12345` attributed as the affiliate code.
* The SDK will also read the `kmrza_affiliate` cookie (if present) and pass that affiliate code to the order automatically, unless you override it.
* You can override the automatic behavior by passing an explicit `affiliateCode` to `Komerza.open()`.
No manual setup is required for affiliate tracking - just use the standard initialization and checkout flow.
***
## Content Security Policy (CSP)
For enhanced security, configure your CSP headers to allow the Komerza embed:
```http theme={null}
Content-Security-Policy:
frame-src https://checkout.komerza.com;
script-src 'self' 'nonce-YOUR_NONCE' https://checkout.komerza.com;
style-src 'self' 'nonce-YOUR_NONCE';
```
Then pass the nonce to the SDK:
```javascript theme={null}
Komerza.init({
nonce: "YOUR_NONCE",
});
```
## Best Practices
* Load the SDK script asynchronously when possible
* Initialize the SDK early but only open modals on user interaction
* Implement proper loading states for buy buttons
* Provide clear loading indicators - Handle network failures gracefully with
fallback URLs - Support keyboard navigation (ESC to close)
* Always use CSP headers in production
* Validate product IDs and variant IDs before opening checkout
* Never expose sensitive data in client-side code
## Analytics Integration
Also integrate our analytics script to bring back visitor analytics on your
custom site
Track the complete customer journey by combining the Embed SDK with our analytics script. This gives you insights into visitor behavior, conversion rates, and checkout abandonment on your custom site.
## Support
Need help with integration?
Interactive demo with source code examples
Get technical support for integration issues
# Embed Not Loading
Source: https://docs.komerza.com/guides/embed-troubleshooting
Fix a Komerza checkout embed that doesn't open, shows an error, or never appears
If a buy button does nothing, the checkout modal never appears, or the embed shows an error instead of loading, this page walks through every common cause in the order worth checking.
} href="https://status.komerza.com">
If Komerza checkout is degraded, nothing below will help - status.komerza.com
shows live availability for checkout and the API.
## Are you a shopper, or the site owner?
You're on a store's website and checkout won't open. Try these, in order:
1. **Reload the page.** A partly-loaded page is the most common cause.
2. **Disable ad blockers and privacy extensions** for this site, then reload. Blockers frequently block checkout scripts.
3. **Try a private/incognito window**, or a different browser.
4. **Turn off any VPN or proxy** and retry.
5. **Check [status.komerza.com](https://status.komerza.com)** in case checkout is down platform-wide.
If none of that works, **contact the store you're buying from** - their support page or the contact details on their site. Komerza provides the checkout technology, but the store is run by an independent seller who handles their own orders, payments and refunds.
Work through the checklist below. The fastest first move is the isolation
test: open the [live demo
page](https://checkout.komerza.com/embed/demo.html) in the same browser.
* **The demo works, your site doesn't** → the problem is your page. Continue with the checklist.
* **The demo also fails** → the problem is the browser, the network, or the platform. Check extensions, network restrictions and [status.komerza.com](https://status.komerza.com).
## Site owner checklist
Open your browser's developer tools → **Network** tab, reload, and look for
`embed.iife.js`. It should return **200**.
```html theme={null}
```
Then check the global exists - in the **Console**, run:
```javascript theme={null}
typeof window.Komerza; // should be "object"
```
If it returns `"undefined"`, the script didn't load, was blocked, or your
code ran before it finished.
Data attributes are only bound when `init()` runs. If you use
`data-kmrza-*` buttons, this is required:
```javascript theme={null}
document.addEventListener("DOMContentLoaded", () => {
Komerza.init();
});
```
With `Komerza.open()` this happens automatically, so a quick way to isolate
a binding problem is to call `open()` directly from a click handler and see
whether the modal appears.
Buttons added later - by a framework, a slider, infinite scroll, a
single-page-app route change - were not present when `init()` ran, so they
have no handler. Call `Komerza.init()` again after rendering, or drive those
buttons with `Komerza.open()` instead.
In React or Next.js, run `init()` inside `useEffect` so it fires after the
component mounts, not during server rendering.
`productId` is required and is a UUID. `variantId` is optional - omit it and
checkout shows a variant picker - but when you do pass it, it must be a UUID
belonging to that product **in the store the ID came from**.
Copying an ID from a different store is a common cause of a modal that opens
and immediately errors. If a button with a variant ID fails, try the same
button without one: if the picker opens fine, the variant ID is the problem.
The embed cannot check out a product your storefront wouldn't sell either. A
product that is **Private**, **On Hold**, or out of stock will fail here
too, as will a store with no payment method that fits the cart - see
[Products & Variants](/guides/products-and-variants) and [Supported Payment
Methods](/guides/payment-methods).
Both show up in the **Console** as blocked-resource errors. See the table
below for which is which.
## Console errors and what they mean
| Error in the console | Cause | Fix |
| --------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `Komerza is not defined` | The script hasn't loaded yet, or was blocked | Load the script before your code runs; keep `defer` and initialise on `DOMContentLoaded` |
| `KomerzaEmbed is not defined` | The global is `Komerza` - some older examples name it `KomerzaEmbed` | Use `Komerza.init()` and `Komerza.open()` |
| `net::ERR_BLOCKED_BY_CLIENT` | An ad blocker or privacy extension blocked the request | Test in a clean profile; ask affected shoppers to allowlist the site |
| `Refused to load the script … violates the following Content Security Policy directive` | Your CSP blocks the embed script | Add `https://checkout.komerza.com` to `script-src` |
| `Refused to frame 'https://checkout.komerza.com'` | Your CSP blocks the checkout iframe | Add `https://checkout.komerza.com` to `frame-src` |
| `Refused to apply inline style` | Your CSP blocks the embed's styles | Pass a nonce to `Komerza.init({ nonce })` and allow it in `style-src` |
| `Mixed Content: … requested an insecure resource` | Your page is served over HTTP | Serve the page over HTTPS |
| A 404 on `embed.iife.js` | Wrong script URL | Use `https://checkout.komerza.com/embed/embed.iife.js` exactly |
A working CSP for the embed looks like this:
```http theme={null}
Content-Security-Policy:
frame-src https://checkout.komerza.com;
script-src 'self' 'nonce-YOUR_NONCE' https://checkout.komerza.com;
style-src 'self' 'nonce-YOUR_NONCE';
```
## The modal opens but looks wrong
The modal uses a z-index of 9999. Something on your page is stacking above
it - usually a sticky header, a cookie banner or a chat widget with a higher
z-index. Lower theirs rather than raising the modal's.
An ancestor with `overflow: hidden`, a CSS `transform`, or a `filter`
creates a new containing block that traps fixed-position content. Move the
buy button out of that container, or remove the property.
`theme` accepts only `light`, `dark` or `auto`. Any other value - including
a typo or an empty string - falls back to the default.
`init()` ran more than once and bound the same button twice. In frameworks,
make sure your effect isn't re-running on every render.
## Still stuck?
Contact support with the following - it's the difference between a same-day fix and a week of back-and-forth:
* The **page URL** where the embed fails
* Your **store ID**, and the **product and variant IDs** you're passing
* **Browser and OS**, and whether it fails in a private window
* The **exact console errors**, copied as text
* Whether the [demo page](https://checkout.komerza.com/embed/demo.html) works in the same browser
If you're a customer of a store rather than its owner, contact that store's
own support - Komerza runs the checkout technology but doesn't handle
individual sellers' orders or refunds.
## Related
} href="/guides/embed-sdk">
Full setup, API reference and framework examples.
} href="https://status.komerza.com">
Live availability for checkout and the API.
} href="/guides/payment-methods">
A cart with no available method can't check out.
} href="https://checkout.komerza.com/embed/demo.html">
A known-good embed to test your browser against.
# Setup Heleket
Source: https://docs.komerza.com/guides/heleket-setup
Connect your own Heleket account to accept crypto on Komerza
Accept crypto through your own **Heleket** account. You bring the account and funds settle to your Heleket balance - Heleket charges a **0.4%** service fee, and Komerza adds nothing on top.
Komerza registers the payment webhook automatically, so there's no callback URL for you to configure.
## Prerequisites
* A [Heleket account](https://heleket.com/).
* A website URL for your store - Heleket verifies your domain and moderates the merchant before activating API access.
Heleket issues payment credentials per **merchant**, and a merchant must be approved before its keys work.
1. Log into Heleket and go to **Business → Merchants**.
2. Click **Create merchant** and give it a name.
3. Open the merchant's **Settings**, click **Leave a request**, enter your **website URL**, and complete **domain verification**.
4. Wait for **moderation** - the payment key and Merchant ID become usable once the merchant is approved.
Once the merchant is approved, in its **Settings** tab:
1. Copy the **API key for payments** (regenerate it if needed).
2. Copy the **Merchant ID** (a UUID) shown in the same tab.
Don't confuse this with the **payout API key** under your personal-account **Settings → API** (protected by 2FA) - that key is for withdrawals, not for accepting payments.
1. In your dashboard, open **Settings** under **Online Store**, then go to **Cryptocurrency**.
2. Under **Processors**, find **Heleket** and click **Connect**.
3. Paste your **API key** and **Merchant ID**, then save.
Komerza automatically registers the payment webhook with Heleket - you don't need to add a callback URL anywhere.
On the Cryptocurrency page, open **Coin routing** and set each coin you want
Heleket to handle to **Heleket** - or confirm default routing already points
there.
Make a small test payment on your store to confirm Heleket processes it and the order is marked as paid before you go live.
## How it works
1. A customer picks a Heleket-backed coin at checkout.
2. Heleket handles the payment and funds settle to your **Heleket balance**.
3. Heleket sends a webhook; Komerza validates it and marks the order paid, triggering delivery.
## Troubleshooting
| Problem | Fix |
| -------------------------- | ----------------------------------------------------------------------------------------------------------- |
| Credentials rejected | Confirm your merchant has passed **moderation** - the payment key doesn't work until it's approved. |
| Using the wrong key | Use the **API key for payments** from the merchant's Settings, not the personal-account **payout API key**. |
| A coin isn't using Heleket | Check **Coin routing** - the coin may be routed to another processor or to self-custody. |
# Migrating to Komerza
Source: https://docs.komerza.com/guides/migrating-to-komerza
Import your products, orders and customers from another platform
Komerza can import your store data from another platform in one guided step. The migration tool supports **Sellhub**, **SellAuth**, **SellApp** and **Shoppex**. Coming from anywhere else - Sellpass, Sell.sn, Paylix or another platform - contact support and we will advise on the best path.
You start the import, watch it run live, and get an email when it finishes. A large catalogue can take twenty minutes or more, and you can close the tab while it works.
## What imports
Every platform brings across products and their variants, serials and license keys, reviews, orders and customers. What differs is the detail each platform's API is willing to hand over.
| Platform | Source store | Notable gaps |
| ------------ | ---------------------- | ------------------------------------------------ |
| **Sellhub** | Single store | - |
| **SellAuth** | Pick from a list | Hidden, pending and disputed reviews stay hidden |
| **SellApp** | Type your store handle | Product images, store credit balances |
| **Shoppex** | Single store | Delivery files, subscriptions |
**Never migrated, from any platform:** coupons, categories, payment gateway configuration, custom domains, balances and funds, and subscriptions. Plan to set those up on Komerza after the import.
The dashboard shows the full list for the platform you pick - both what will come across and what will not - before you press Start. That list is the authoritative one; the table above is the summary.
**Imports:** products with images, descriptions, pricing, slugs and visibility; variants with their own price, stock, delivery setup and descriptions; serials and license keys per variant; checkout questions; volume pricing tiers, and whether a coupon cancels them; product badges with their wording and colour; delivery setup including instructions, dynamic URLs and blocked payment methods; reviews with ratings, messages, replies and original dates; orders with line items, statuses, delivered goods and payment references; customers folded together by email, with their store credit.
**Left behind:** reviews that were hidden, pending or disputed on SellAuth stay hidden here too; custom field validation rules and prefilled defaults; quantity deals and bundle offers, because Komerza prices bulk by tier rather than buy-one-get-one; coupons, categories, whitelists and downloadable files.
SellAuth supports multiple stores, so you pick the source store from a list after entering your key.
**Imports:** products with descriptions, slugs and visibility; variants with their own price, stock and delivery setup; serials and license keys per variant; checkout questions; bulk pricing tiers, and minimum and maximum order quantities; reviews with ratings, messages and replies; orders with line items, totals, statuses and payment references; customers folded together by email.
**Left behind:** product images, because SellApp serves them from its own storage - re-upload those; store credit balances; coupons, sections, groups and blacklist rules.
SellApp has no way to list your stores from an API key, so you type the **store handle** instead of picking from a list. It is the handle in your SellApp dashboard URL.
**Imports:** products with images, descriptions, pricing, custom fields and visibility; variants as first-class products, each with their own price and stock; serials and license keys for both base products and variants; delivery setup including instructions, dynamic webhooks and service text; reviews with ratings, messages and replies; orders with line items, statuses and delivered goods; customers with their join date and store credit balances.
**Left behind:** delivery files, because Shoppex's API has no download endpoint for them - re-upload those manually; subscriptions, since recurring billing cannot be transferred between platforms; coupons, categories and quantity deals.
Shoppex rate limits hard. See [If the migration stops early](#if-the-migration-stops-early).
**Imports:** products and their variants, reviews, orders and customers.
A Sellhub API key belongs to one store, so there is nothing to pick.
A migration stops at 1,000 products, which is the ceiling on every plan including Free. Anything past
it is not imported, and both the completion screen and the email tell you exactly how many were left
out. Almost no genuine catalogue reaches this.
## Before you start
If you are new to Komerza, create your account at [dashboard.komerza.com](https://dashboard.komerza.com) - signing up creates your account and your first store together, on a free `yourstore.mykomerza.com` URL. Confirm your email, and your account starts on the Free plan.
Already have an account? You don't need another one. Add a store from the sidebar store selector → **Create New Store**, within your plan's store limit: one store on Free, five on Premium, unlimited on Business.
You need to be the store's owner to run a migration.
## Running the migration
In the dashboard, open the store selector → **Store settings → Advanced →
Migration** ("Import your store data from another platform").
Select the source platform and paste that platform's API or authorization
key, then press **Continue**. The key is used for the import and is not
stored permanently. Generate a fresh key on the source platform rather than
reusing one from an old email or note.
SellAuth gives you a list to pick from. SellApp asks you to type your store
handle. Sellhub and Shoppex are single-store, so this step is skipped.
The screen lists what comes across for that platform and what does not. Read
the second list - it is where the surprises are.
Press **Start Migration**. The import runs on our side, so you can close the
tab. You will get a "Migration completed" email with counts of what was
imported, or a "Migration failed" email.
## Watching it run
Once started, the tab shows live progress: the phase it is in (products, reviews, orders, customers, then saving to your store), a running count, the item it is working on, and a rough estimate of the time left once there is enough to base one on.
**"Next in line" or "3 migrations ahead of yours"** means the run is queued. Only one migration runs at a time across the whole platform, so you can genuinely be waiting behind other merchants' shops. The queue shows your position but never an estimated wait, because the run ahead of you might be fifty products or fifty thousand.
**"Connection lost" or "Live progress unavailable"** is about the picture, not the migration. The import keeps running on our side and the email still arrives. The dashboard reconnects on its own, and there is a **Try again now** button if you would rather not wait.
Refreshing the page or reopening the tab rejoins a run in progress.
## How it can finish
A finished migration is not simply pass or fail, and the screen colour tells you which of three you got.
Everything that could be imported was. The screen shows how many items are
now in your store.
The import ran, but something is worth knowing about. The warnings are
listed in full on screen and in the email.
Nothing further will be imported. Your existing store data has not been
changed.
Warnings are specific and worth reading. Typical ones are "12 product(s) could not be imported from SellAuth and are missing from your store", "4 review(s) could not be matched to a product and were left out", or "Shoppex sent 40 product(s) but none could be imported, including 40 subscription product(s), which cannot carry recurring billing across". A count of products that quietly failed is exactly the thing you want to see before you open for business.
If a phase returned nothing at all, the email says so plainly rather than reporting a successful migration of zero - so "Sellhub reported no products for this store, so there was nothing to import" means the key or the store was wrong, not that the importer broke.
## How conflicts are handled
* **Duplicate product slugs** are de-duplicated with `-2`, `-3` suffixes, both within the import and against products already in your store.
* **Customers with the same email address** are merged into one customer record. A customer who already exists in Komerza gains a relationship to this store rather than being duplicated.
## If the migration stops early
**"Migration could not start: ..."** means the source platform rejected the key. The fix is nearly always the same: generate a brand-new API key on the source platform and run the migration again. Keys copied from old emails or notes are frequently expired or revoked. If you are on a multi-store platform, check the key belongs to the store you meant.
**"Migration stopped - rate limited"** means the source platform throttled us past what we could wait out. Nothing is wrong with your key and there is nothing to fix - come back later and run it again. Anything imported before the limit was hit has been kept. Shoppex is the strictest of the supported platforms, so this is most likely there; retrying immediately can keep the key blocked, so leave it a while.
**Anything else** is a failure on our side. Contact support with your store name and roughly when it ran.
## After the import
Migration brings your catalogue; it does not bring your storefront or your ability to take money. Work through these:
} href="/guides/products-and-variants">
Confirm counts, then check delivery items and stock modes on each variant.
} href="/guides/payment-methods">
Gateway configuration never migrates. Nothing sells until this is done.
} href="/guides/embed-sdk">
Use the builder, or embed checkout in a site you already have.
} href="/guides/custom-domain">
Requires the Premium plan or above.
Press **Reload store** on the completion screen to see the imported data, or **Migrate another store** to run a second import.
## When to contact support
* A freshly generated, valid API key from the source platform still fails.
* Imported counts look wrong and the warnings don't explain the difference.
* You are migrating from a platform that isn't listed.
* You need data the tool doesn't cover, such as coupons, categories or subscriptions.
# Notifications & Alerts
Source: https://docs.komerza.com/guides/notifications
Choose which store events reach you by email, Discord and mobile push
You control which alerts you and your team receive for a store under **Store Settings → Notifications**. The page is a grid of store events, each with an **Email** switch and a **Discord** switch, plus mobile push on the native app.
These toggles affect **merchant and team alerts only**. Your customers' emails
* receipts, delivery notices, subscription reminders - are not controlled here
and always send.
## What you can toggle
| Group | Events |
| ------------------ | --------------------------------------------------------------------------- |
| **Orders** | Order Delivered, Order Pending, Order Undeliverable |
| **Tickets** | Ticket Created, Ticket Response Added |
| **Reviews** | Review Added |
| **Inventory** | Low Stock, Out Of Stock |
| **Disputes** | Order Dispute, Dispute Created, Dispute Resolved, Dispute Evidence Required |
| **Payouts** | Payout Completed, Payout Failed, Auto Payout Failed |
| **Payment Config** | Payment Method Misconfiguration, Chargeback Received |
| **Other** | Configuration Success |
If you turn on nothing else, turn on **Order Undeliverable** and **Out Of
Stock**. Those two are the ones where a delay costs you a sale that has
already been paid for.
## Discord alerts
To receive alerts in Discord, paste a Discord webhook URL under **Destinations** on the same tab. The badge switches from "Not connected" to "Connected", and the Discord switches stay disabled until a webhook is set ("Configure a Discord webhook below to enable").
Discord receives store-activity events only, delivered as embeds: orders, tickets, reviews, stock, disputes, payouts and payment-configuration events. Account, login, plan and customer-facing emails are never sent to Discord.
### If Discord alerts stop arriving
**Store Settings → Notifications → Destinations**.
Email and Discord are toggled independently per event.
Deleting a channel or webhook in Discord silently breaks delivery.
Preference changes are cached, so allow a short delay before judging a
change by the next event.
## Mobile push
On the native mobile app you can enable or re-register push notifications per device from the same Notifications settings.
## Who receives store alerts
Store alerts such as Order Delivered and Order Undeliverable go to team members who hold the relevant store permission. If a team member isn't receiving something they should, check their permissions before assuming the notification is broken. See [Teams & Permissions](/guides/teams-and-permissions).
## Emails that arrive regardless
Account-level email is not controlled by this page. These always send:
* Account verification and security alerts (new logins, password and email changes)
* Plan and billing lifecycle emails, and wallet balance notices
* Data-export notifications and team invitations
* Buyer Protection invoices and claim alerts
## Notifications vs webhooks
Notifications are for humans; [webhooks](/guides/webhooks) are for software. If you want an alert in a channel someone reads, use this page. If you want to sync orders into your own system, fire fulfilment, or drive automation, use webhooks - they are signed, logged, retried and resendable, none of which applies to a Discord message.
Discord notification destinations are configured here, not on the Webhooks
page. The Webhooks page carries a banner saying exactly that.
## Next steps
} href="/guides/webhooks">
Machine-readable order events for your own systems.
} href="/guides/orders">
What to do when an alert tells you something went wrong.
# Setup NOWPayments
Source: https://docs.komerza.com/guides/nowpayments-setup
Connect your own NOWPayments account to accept crypto on Komerza
Accept crypto through your own **NOWPayments** account. You bring the account and funds settle to your NOWPayments balance - NOWPayments charges a **0.5-1%** service fee, and Komerza adds nothing on top.
Komerza connects using your **API key** and **IPN secret**, and registers the payment webhook automatically - there's no callback URL for you to configure.
## Prerequisites
* A [NOWPayments account](https://nowpayments.io/) (free to create).
* A **payout wallet address** - NOWPayments won't issue an API key until one is saved on your account.
NOWPayments requires a payout wallet before it will generate API keys.
1. Log into your [NOWPayments dashboard](https://account.nowpayments.io/).
2. Go to **Store Settings → Payments**.
3. Enter your **payout (outcome) wallet address** and click **Save**.
4. Scroll up to the **API keys** section and click **Add new key**.
5. Copy the new **API key**.
1. Still under **Store Settings → Payments**, scroll to **Instant Payment Notifications (IPN)**.
2. Generate the **IPN Secret key** and copy it.
NOWPayments shows the IPN secret **only once**. Copy it before leaving the page - if you lose it, generate a new one and update it in Komerza.
1. In your dashboard, open **Settings** under **Online Store**, then go to **Cryptocurrency**.
2. Under **Processors**, find **NOWPayments** and click **Connect**.
3. Paste your **API key** and **IPN secret**, then save.
Komerza automatically registers the payment webhook with NOWPayments - you don't need to add a callback URL anywhere.
On the Cryptocurrency page, open **Coin routing** and set each coin you want
NOWPayments to handle to **NOWPayments** - or confirm default routing already
points there.
Make a small test payment on your store to confirm NOWPayments processes it and the order is marked as paid before you go live.
## How it works
1. A customer picks a NOWPayments-backed coin at checkout.
2. NOWPayments handles the payment and funds settle to your **NOWPayments balance**.
3. NOWPayments sends an IPN callback; Komerza validates it with your **IPN secret** and marks the order paid, triggering delivery.
## Troubleshooting
| Problem | Fix |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| Can't create an API key | Add a **payout wallet** in **Store Settings → Payments** and click **Save** first - NOWPayments blocks key creation until one exists. |
| Payments not marked as paid | Re-check the **IPN secret** in Komerza. If it was regenerated in NOWPayments, paste the new value. |
| A coin isn't using NOWPayments | Check **Coin routing** - the coin may be routed to another processor or to self-custody. |
| Lost your IPN secret | Generate a new one in **Store Settings → Payments → Instant Payment Notifications** and update it in Komerza. |
# Managing Orders
Source: https://docs.komerza.com/guides/orders
Find, deliver, refund and export orders from your Komerza dashboard
Every order your store takes lives under **Home → Orders** in the dashboard. From there you can search and filter orders, open any order for full detail, deliver or re-deliver it, resend the receipt, issue a refund, and export your order history.
## Finding an order
The Orders page has tabs - **All / Delivered / Pending / Undeliverable / Disputed / Refunded** - each with a live count.
The search box understands several inputs:
| Input | Behaviour |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| Full order ID | Exact match |
| Full customer email | Exact match - partial emails fall back to fuzzy matching |
| Payment note (`xxx-yyy`) | Exact match |
| Payment method or status name | Matches orders using that method or status |
| Anything else | Fuzzy match against customer emails, product names, coupon codes, delivered key/text content, and Discord username, display name or ID |
The **Filter** dropdown narrows results by date range, status, payment method, customer email and Discord.
## Order statuses
Every order carries one status describing where it sits between "cart created" and "goods delivered". The full reference is on the [Order Status](/api-reference/payment-status) page; here is what each one means operationally:
A cart was created but never paid. It remains editable and the customer can
still complete payment. Unpaid orders are automatically cancelled roughly 12
hours after creation, which releases any reserved stock.
Payment has been received or detected but funds are still settling. Typical
for bank transfers and cryptocurrency. Orders paid with a [custom payment
method](/guides/custom-payment-methods) show "Awaiting seller review" and
**do** need you to approve or reject the proof of payment.
Funds withheld for verification. Treat as payment in flight.
The customer underpaid a crypto invoice. The order is **not** fulfilled. The
customer has already been emailed the expected amount, the received amount
and the exact shortfall. Arrange the difference with them directly, then
deliver manually.
A guard status while delivery executes. It should pass quickly on its own.
Paid and delivered. The customer received a delivery email with their items.
**Resend receipt** and **Re-deliver** are available from the order page.
Paid, but delivery failed - most commonly missing stock, but also a dynamic
delivery endpoint returning a non-200 response. The warning banner on the
order page names the cause. Fix it, then use **Deliver manually**.
Expired or cancelled. A cancelled cart is not dead: it can be revived,
edited and paid again.
A refund was issued, or a chargeback was opened with the payment provider.
Disputes are handled in your payment provider's own dispute tooling.
Once a payment is in flight, cart items lock. A customer who tries to change
items sees: "A payment for this order has already been detected and is being
confirmed - items can no longer be changed." This protects the amount the
payment was matched against.
## Delivering an order manually
Open the order and use **Deliver manually**, available when the order is Pending, Incomplete, Cancelled, Undeliverable or PartiallyPaid. It runs the normal delivery flow exactly as if the order had been paid: stock is consumed, the receipt email is sent, the `order.completed` webhook fires, and Discord roles are granted. The order is stamped **Delivered Manually** with the team member's email.
Manual delivery does **not** verify payment. Confirm you were actually paid
first. Manually delivered orders can only be refunded to customer balance,
never back to the gateway.
**Re-deliver order** appears only on Delivered orders. It reruns the entire original delivery: it consumes new license keys from inventory, re-fires dynamic delivery webhooks, and re-sends emails. Use it deliberately.
Both actions require the deliver-orders permission, and a second attempt while a delivery is in flight is quietly ignored, so orders cannot be double-delivered by accident.
## Resending a receipt
**Resend receipt** re-emails the already-delivered items, with no new fulfilment. It is available for Delivered and Disputed orders and is rate-limited.
If an order was created with receipts turned off - for example via the
send-receipt toggle on a payment link - resend reports success but sends
nothing.
## Refunds
Refunds are issued from the order page with **Process refund**. Refunds are only possible on **Delivered** orders and are always for the **full amount**; there are no partial refunds.
The modal asks for two things:
Fraudulent, Requested by customer, Duplicate, or Other. The reason is stored
with the order, included in notifications, and may be forwarded to the
gateway. See [Refund Reason](/api-reference/enums#refund-reason) for the API
values.
**Original payment method** - the gateway processes the refund
automatically. Available for Customer Balance, Mollie, Square,
Authorize.net, Stripe, SumUp, PayPal (modern integration), Shopify, Paddle
and FastSpring.
**Customer balance** - instant store credit that never touches the gateway.
The destination is locked to balance when the order was paid with customer
balance, was delivered manually, used a gateway with no refund API (NMI,
MoneyMotion, Pandabase, Storrik, VenPayr), or used a method that can only be
settled manually (all cryptocurrencies, custom payment methods, Cash App,
PayPal Friends & Family, legacy PayPal).
A successful refund sets the order to **Refunded**, reverses any affiliate commission, and fires the `order.refunded` webhook. If the gateway rejects the request, the order is safely restored to Delivered and the UI reports the failure. Simultaneous refund attempts are blocked, so two team members cannot double-refund the same order.
**Cryptocurrency refunds move no funds.** Marking a crypto order refunded
changes its status only - you must return the cryptocurrency to the customer
manually. The same applies to custom payment methods, Cash App and PayPal
Friends & Family.
Two balance-specific rules: a balance top-up order can never be refunded to balance (it would double-credit), and refunding a top-up to the card also claws the credited balance back. For split payments, each part returns to its own source.
## Exporting orders
**Export CSV** on the Orders page opens a modal with a start and end date, defaulting to the last 30 days. The file has one row per order with these columns:
```
OrderId, DateCreated, Status, Gateway, Amount, Products,
AmountPaid, CurrencyCode, TransactionId, CustomerEmail
```
Exports require a logged-in dashboard session - this endpoint cannot be called
with API keys - and are rate-limited. For a single order, **Export PDF** on
the order page produces a printable invoice.
## Order data for developers
* Orders created through the API are marked **Source: API** in the order's Technical Details.
* You can attach custom **metadata** at creation: up to 20 keys, each key up to 40 characters and each value up to 500 characters. Metadata is merchant-only and is always stripped from anything the customer can see.
* The **Webhook Execution Logs** card on an order page shows the most recent 100 webhook deliveries for that specific order.
## Next steps
} href="/guides/webhooks">
Get order events pushed to your own server.
} href="/guides/custom-payment-methods">
Approve or reject manually-paid orders.
} href="/api-reference/payment-status">
Every status and its meaning.
} href="/api-reference/endpoint/orders/get-list">
Query, deliver and refund orders programmatically.
# Setup Paddle
Source: https://docs.komerza.com/guides/paddle-setup
Configure Paddle payments on your Komerza store
Accept payments through Paddle on your Komerza store. Paddle handles the checkout UI - your customers are redirected to a secure Paddle-hosted payment page, then returned to your store once payment is complete.
## Prerequisites
* A [Paddle account](https://www.paddle.com/) (free to create)
1. Log into your [Paddle Dashboard](https://vendors.paddle.com/)
2. Go to **Developer Tools → Authentication**
3. Click **Generate API Key**
4. Copy the key - it starts with `pdl_live_`
**Testing first?** Use the [Sandbox Dashboard](https://sandbox-vendors.paddle.com/) instead. Sandbox keys start with `pdl_test_` and no real money is charged.
1. In your Komerza dashboard, go to **Payment Methods**
2. Find **Paddle** and click **Configure**
3. Paste in your **API Key** (from Step 1)
4. Click **Save**
Komerza automatically creates the webhook endpoint on your Paddle account. No manual webhook configuration needed.
Go to **Payment Methods → Paddle** in your dashboard. The status will show:
* **Active** - Everything is connected and working
* **Inactive** - Check that your API key is correct
If the status shows **Inactive**, double-check that your API key starts with `pdl_live_` (or `pdl_test_` for sandbox).
## How It Works
1. A customer selects Paddle at checkout
2. They're redirected to Paddle's secure hosted checkout page
3. After paying, they're sent back to your store with a confirmation
4. Komerza receives a webhook from Paddle confirming the payment and delivers the order automatically
## Managing Payments
* **Refunds** - You can issue full refunds through Komerza. The refund is processed via Paddle's system and the customer is credited back.
* **Disputes** - If a customer opens a dispute with their bank, you'll receive a notification in Komerza. The order is marked as disputed and you can respond through Paddle's dashboard.
## Sandbox Testing
If you want to test before going live:
1. Create a [Sandbox account](https://sandbox-vendors.paddle.com/)
2. Use your sandbox API key (`pdl_test_...`)
3. No real charges are made - you can test the full flow safely
4. When ready, disconnect and reconnect with your production API key
## Disconnecting Paddle
Go to **Payment Methods → Paddle** and click **Disconnect**. This also removes the webhook endpoint from your Paddle account. Your existing orders are not affected - only new checkouts will no longer offer Paddle.
## Troubleshooting
| Problem | Fix |
| ----------------------------------- | ----------------------------------------------------------------------------------------- |
| Payments not completing | Check that your API key is valid and starts with `pdl_live_` (or `pdl_test_` for sandbox) |
| Inactive status | Double-check your API key in **Payment Methods → Paddle** |
| Not receiving dispute notifications | Contact support - the webhook may need to be recreated (disconnect and reconnect Paddle) |
# Supported Payment Methods
Source: https://docs.komerza.com/guides/payment-methods
A complete list of payment gateways and cryptocurrencies supported by Komerza
Komerza supports a wide range of payment methods, from traditional card processors to popular cryptocurrencies.
## Payment Gateways
} href="/guides/apple-pay-stripe">
Industry-standard payment processing for cards, wallets, and local payment methods.
}
href="/guides/paypal-ff-setup"
>
Support for standard PayPal checkout and Friends & Family manual processing.
}
href="/guides/cash-app-setup"
>
Direct integration with automated payment matching via email forwarding.
}
>
Point of sale and online payments.
}
>
European payment gateway for card processing.
}
>
Robust payment gateway for high volume.
}
>
Flexible gateway solution connecting to multiple processors.
}
>
Leading European payment platform.
}
href="/guides/shopify-setup"
>
Accept payments through Shopify's hosted checkout.
}
>
Card payment processing with VenPayr.
}
>
Card payment processing with moneymotion.
}
>
Card payment processing with Pandabase.
}>
Card payment processing with Storrik.
## Cryptocurrencies
Refunding automated payments for digital goods is simple with our extensive crypto support.
### How your crypto settles
Choose how crypto payments settle - hold your own keys with zero fees, or connect a processor.
Receive crypto directly into wallets you control. Absolutely zero fees.
Connect your own NOWPayments account. 0.5-1% service fee.
Connect your own Heleket account. 0.4% service fee.
### Supported coins
} />
}
/>
}
/>
}
>
ERC20, TRC20, SOL, BSC
}
>
ERC20, SOL, BSC
}
/>
}
/>
}
/>
}
/>
}
/>
}
/>
}
/>
}
/>
}
/>
}
/>
} />
# Setup PayPal F&F
Source: https://docs.komerza.com/guides/paypal-ff-setup
Configure PayPal Friends & Family payments with IPN or mail forwarding
Follow this guide to connect your PayPal account to Komerza. You can choose between IPN (Instant Payment Notification) or Email Forwarding integration.
Navigate to the [Komerza Dashboard Payments](https://dashboard.komerza.com/account?payment) page and click the **Connect** button next to **PayPal Friends & Family**.
In the dialog box, enter the following details:
* **PayPal Email**: Enter the email address associated with your PayPal account.
If you plan to use **Email Forwarding**, this **must** be a Gmail address. If using **IPN**, any email provider is supported.
### Risk Settings
PayPal sometimes holds payments for review. Enable this if you want to deliver orders immediately even if the payment is held.
**High Risk**: Held payments can be reversed by PayPal. Only enable this if you are willing to accept the risk of losing the funds.
Enable this if you want Komerza to automatically try to accept payment requests.
This feature carries some risk. Only enable it if PayPal frequently forces you to manually accept payments in the app.
Select the integration method that best fits your needs:
### Option A: IPN (Instant Payment Notification)
* **Pros**: Very stable, standard integration.
* **Cons**: Easily detectable by PayPal.
* **Recommendation**: Not recommended for high-volume merchants who want to stay under the radar.
**To setup IPN:**
1. Select **IPN** as the integration type.
2. Click **Next**.
3. The dialog will provide you with a Notification URL.
4. Log in to your PayPal account settings and paste this URL into the [IPN settings area](https://www.paypal.com/merchantnotification/ipn/preference/edit).
5. Paste the URL into the **Notification URL** field.
6. Ensure **Receive IPN messages (Enabled)** is selected.
7. Click **Save**.
### Option B: Email Forwarding
* **Pros**: Harder to detect, very reliable for privacy.
* **Cons**: Relies on email parsing, which can break if PayPal changes email formats.
* **Note**: "Notes Version" is forced to **Version 1** for stability when using this method.
**To setup Email Forwarding:**
1. Select **Email Forwarding** as the integration type.
2. Click **Next**.
3. You will be given a `@hermes.komerza.com` forwarding address.
If you chose **Email Forwarding**, follow these steps to forward payment receipts to Komerza.
### 1. Add Forwarding Address
1. Login to your Gmail account.
2. Go to **Settings** > **See all settings** > **Forwarding and POP/IMAP**.
3. Click **Add a forwarding address**.
4. Paste the `@hermes.komerza.com` address provided in the dashboard.
5. Complete the verification steps (check your inbox/spam for the code).
### 2. Configure Filter
**Important:** Ensure **"Disable forwarding"** is selected in the Forwarding tab. Do not enable global forwarding.
1. Click **"creating a filter"** in the Forwarding tab.
2. You need to set the **From** address to your specific PayPal service email.
#### Finding Your PayPal "From" Address
The "From" address varies by region. **To be certain, check a recent email from PayPal or send yourself a test payment (\$0.01).**
**Most users outside of major specific regions will use:** `service@intl.paypal.com`
**Common Addresses:**
| Region | From Address |
| --------------------------------------------- | ------------------------- |
| USA | `service@paypal.com` |
| UK | `service@paypal.co.uk` |
| Germany | `service@paypal.de` |
| France | `service@paypal.fr` |
| Spain | `service@paypal.es` |
| Italy | `service@paypal.it` |
| Australia | `service@paypal.com.au` |
| Brazil | `service@paypal.com.br` |
| Mexico | `service@paypal.com.mx` |
| Canada | `service@paypal.ca` |
| Netherlands | `service@paypal.nl` |
| Belgium | `service@paypal.be` |
| Poland | `service@paypal.pl` |
| Sweden | `service@paypal.se` |
| India | `service@paypal.co.in` |
| Singapore | `service@paypal.com.sg` |
| Hong Kong | `service@paypal.com.hk` |
| 🌍 International | `service@intl.paypal.com` |
3. Enter your region's address (e.g., `service@paypal.com`) in the **From** field.
4. Click **Create filter**.
5. Check **Forward it to:** and select the hermes email address.
6. Click **Create filter**.
Your PayPal integration is configured.
We strongly recommend making a **\$1 test payment** on your store to confirm that the system correctly detects the payment and marks the order as paid.
# Products & Variants
Source: https://docs.komerza.com/guides/products-and-variants
How products, variants, stock and visibility work on Komerza
Everything you sell on Komerza is a **product**, and every product contains one or more **variants** - the options a customer actually buys, such as editions, tiers or durations. A product with a single variant appears as a simple product on your storefront; a product with several variants shows a picker so the customer chooses one.
Products are managed in the dashboard under **Products → Products**.
Delivery, pricing and stock are configured **per variant**, not per product. A
product on its own cannot be purchased - it needs at least one variant with a
price and a delivery configuration.
## Product settings
| Setting | Rules |
| -------------------- | --------------------------------------------------------------- |
| **Name** | 3-64 characters |
| **Slug** | 1-72 characters, lowercase, unique within your store |
| **Description** | Markdown, up to 16,384 characters |
| **Meta description** | Separate short description used for SEO |
| **Images** | PNG, JPG, WebP or GIF, under 30 MB each |
| **Badges** | Optional labels shown on the storefront |
| **Bestseller** | Optional flag for highlighting a product |
| **Hide stock** | Hides stock counts and the out-of-stock badge on the storefront |
Products can be grouped into **Categories** (**Products → Categories**), each with its own slug, image and custom product ordering.
### The product editor
The editor is split into tabs:
}>
Name, slug, description, SEO, badges and visibility.
}>
Product imagery and ordering.
}>
Purchasable options, pricing, stock and quantity limits.
}>
What the customer receives - configured per variant.
}>
Block specific gateways for this product, or add per-gateway fees.
}>
Use the store default affiliate discount, override it, or disable it.
}>
Block VPN and proxy connections for this product.
Grant a Discord role on delivery.
If a product cannot currently be purchased - it is On Hold, has zero stock, or has no delivery items - the editor shows a banner explaining why, with a button that jumps to the tab that fixes it.
## Visibility
Each product is in one of four states:
Visible on the store page, searchable, and accessible to all visitors.
Hidden from the store page, but reachable and purchasable via its direct URL.
Hidden from the storefront entirely and not purchasable.
Visible but blocked from purchase. Customers see: "This product is marked as
on hold, try again later."
The Products list has quick-filter tabs for each state, plus search and a low-stock filter.
## Variants
Each variant carries its own configuration:
* **Name** - 3-128 characters, with an optional description up to 4,096 characters
* **Cost** - 0 to 100,000 in your store currency
* **Minimum and maximum quantity** per order (minimum at least 1; maximum can be unlimited)
* **Stock mode** - Calculated, Ignored or Fixed (see below)
* **Type** - a one-time purchase, or a subscription with a billing interval
* Its own images, custom fields, volume discounts and optional Discord role
A variant is rejected if its price multiplied by its minimum quantity falls
below one minor currency unit (for example, one cent). The error message tells
you the minimum quantity required - raise either the price or the minimum
quantity.
## Stock modes
Stock equals the number of delivery items (license keys) loaded on the
variant. The stock field is read-only - you change stock by adding or removing
keys on the **Delivery** tab. The displayed count re-syncs periodically, so it
can briefly lag reality.
Stock is not tracked or displayed, and the variant never goes out of stock.
The right choice for file downloads and dynamic delivery.
A number you set and maintain manually. Checkout rejects quantities above it:
"Sorry, `{name}` cannot be purchased due to a lack of stock."
**Hide stock** (product level) hides counts on the storefront but does not disable stock checks - customers still cannot buy more than exists.
### Stock reservations
While a payment is still being confirmed - most commonly cryptocurrency - the items for that order are reserved so another customer cannot take them. Storefront availability drops at that moment, before the order completes. If the payment fails or expires, the items return to inventory automatically.
When a Calculated variant drops to 5 or fewer remaining keys, you receive a
"Product nearly out of stock" email linking to the product's delivery page.
The Products list also has a low-stock filter.
### When stock runs out after payment
If a paid order needs more license keys than a Calculated variant has, the order is marked **Undeliverable** and the customer receives a "We couldn't deliver your order" email. Restock the variant on the **Delivery** tab, then use **Deliver** on the order page. See [Managing Orders](/guides/orders).
## Plan limits
| | Free | Premium | Business |
| -------- | ---- | ------- | --------- |
| Products | 15 | 1,000 | Unlimited |
| Stores | 1 | 5 | Unlimited |
| Coupons | 5 | 1,000 | Unlimited |
Creating a product past your plan's cap returns an upgrade-required error.
## Duplicating and deleting
**Duplicating** a product or variant creates a fully independent copy - its own images, delivery files, license keys, custom fields, volume discounts, fees and settings. The copy needs a unique name, and editing or deleting it never affects the original.
Deleting a product also deletes its variants, custom fields, volume discounts,
images and **reviews**. Files already delivered to customers are retained, so
past buyers keep access to their downloads.
## Bulk editing
From the Products list you can update many products at once: visibility, VPN blocking, gateway blacklist, description, delivery message (applied to all variants), bestseller flag, affiliate discount, custom fields, Discord settings and role, and randomised key delivery.
Bulk-applied custom fields **replace** the existing custom fields on all
variants of the selected products.
## Next steps
} href="/guides/delivery/introduction">
Configure what each variant delivers after payment.
} href="/guides/coupons-and-discounts">
Discount codes, volume pricing and pop-up offers.
} href="/guides/orders">
Statuses, manual delivery, refunds and exports.
} href="/api-reference/endpoint/products/create">
Create and manage products programmatically.
# Reselling
Source: https://docs.komerza.com/guides/reselling
Sell another store's products as your own, drawing their stock at the moment you make a sale.
## Overview
Reselling lets you list another Komerza store's products in your own store. When one of your customers buys, Komerza draws a unit from the supplier's stock, charges it to a balance you hold with them, and hands the delivery straight to your customer.
You never hold their stock, and you never front the money for units you have not sold.
If your own store is on Komerza, none of this page applies to you. Your draws
are served internally and there is nothing to configure - set it all up from
**Reselling** in your dashboard instead.
This guide is for **off-platform resellers**: you sell somewhere other than Komerza.
## Getting approved
Every supplier decides how people join, and you will meet one of three:
| Mode | What happens |
| --------------- | ------------------------------------------------ |
| **Open** | You are approved the moment you join. |
| **Application** | You fill in their form and wait for a decision. |
| **Invite only** | Nothing is shown until they invite you by email. |
An invite-only programme is not discoverable. Asking about one answers exactly as it would for a store with no programme at all, so there is nothing to find by guessing.
### Application forms
Suppliers taking applications can ask their own questions - short answers, long ones, dropdowns, checkboxes, numbers, links. You answer them once, when you apply.
What you answered is stored with the wording it was asked under, so a supplier who later rewords the form does not change what you appear to have been asked.
### Deposits
A supplier may ask for money before they will approve you. Two settings do quite different things, and the panel tells you which you are looking at:
* A **required deposit** blocks approval outright. Nothing happens until it is paid.
* An **automatic approval amount** is optional. Reach it and you are approved on the spot instead of waiting; ignore it and your application still sits in the queue like any other.
Money you pay in becomes your balance with that supplier, which is what stock is bought from. If they turn you down, it stays yours - it remains as store credit, and they can refund it on request.
## Your shop
Suppliers can publish a list of who resells for them. If you sell on Komerza you are named by your own store; if you sell anywhere else, there is nothing to name you by until you say so.
Set a shop name, a link and a logo under **Reselling** in the supplier's customer dashboard, and that is what appears wherever they list you. Leave it and you appear unnamed.
## Two ways to get stock
Both start the same way: get approved by the supplier's reseller programme, and fund a balance with them. Stock is paid for out of that balance either way.
Buy a quantity now and download it as a text file. Nothing to build - the
units are yours to sell wherever you like.
Your storefront calls Komerza at the moment it makes a sale, and gets a unit
back. Needs a store that can call out mid-checkout.
You can use both, and most people start with the first.
## Buying up front
From the supplier's customer dashboard, under **Reselling**: pick a product, choose a quantity, and confirm. The cost comes out of your balance and the units come straight back, downloadable as a text file with one unit per line.
Nothing about this needs an integration. If your own shop is a Discord bot, a spreadsheet, or a marketplace listing, this is the whole thing.
Suppliers can turn this off, in which case every sale has to draw at the time
through a delivery URL. It is on by default.
Two limits worth knowing:
* **Text-delivered products only.** A file-delivered product cannot be handed over as a downloadable unit, though it still sells fine drawn per sale.
* **The supplier's delivery has to answer per unit.** If it returns one response for the whole order, one unit arrives and the rest is refunded to your balance. Suppliers wanting to sell in bulk should return an `items` array - see [Dynamic Delivery](/guides/dynamic-delivery#honouring-quantity).
If fewer units arrive than you paid for, the difference goes back to your balance automatically. You are never charged for stock that did not turn up.
## Drawing per sale
The rest of this page covers the other route: your store calls a delivery URL when it makes a sale, and Komerza draws a unit from the supplier at that moment.
1. **You generate a delivery URL** and paste it into your own product's delivery configuration.
2. **A customer buys** from you. Your store calls the delivery URL, Komerza draws a unit from the supplier, and returns it.
## Your delivery URL
Generate it from the supplier's customer dashboard, under **Reselling**, then take the URL for the product you want from the catalogue on the same page - the **Copy ID** button beside each product gives you the last part.
```
https://m-api.komerza.com/reseller/delivery/{token}/{product-id}
```
Two parts, both from that page:
Identifies you, and authorises the draw. Shown once when you generate it.
Which of the supplier's products to draw. Copy it from the catalogue. A
product with several variants asks you which one - the price and stock differ
per variant, so the id has to name one.
**The URL is the credential.** Anyone who has it can draw stock and spend your
balance with that supplier. Treat it like a password: store it where you keep
secrets, never commit it, and never paste it into a support ticket or a
screenshot.
Komerza keeps only a hash of the token, so it is shown once and cannot be looked up again. If you lose it or it leaks, generate a new one - the previous URL stops working immediately.
### Why there is no signature
Delivery calls are not signed. Your store signs its outbound requests with its own secret, or does not sign at all, and Komerza has no way to verify a signature it did not issue the key for. Requiring one would mean nobody could integrate.
The unguessable token takes its place. That is a deliberate trade: it is simpler to integrate and weaker than a signature, which is why the token is 256 bits, is never returned twice, and can be replaced at any time.
## The request
A `POST` to that URL. **The body is optional** - the product is already in the URL, so there is nothing you have to look up or map.
```json theme={null}
{
"lineItemId": "ORD-1042-1",
"quantity": 2
}
```
Your own reference for this sale - an order number, a line id, a cart
reference. Any string; it does not have to be a Komerza id. This is the
idempotency key, so sending it is strongly recommended - see
[Retries](#retries). Also accepted as `orderReference`, `orderId` or
`reference`.
How many units were sold. The response contains one entry per unit.
Anything else you send is ignored, so if your platform already posts a dynamic-delivery payload you can point it at this URL unchanged - it will find the two fields it needs and skip the rest.
If your storefront can only call one URL for everything, there is an older
route that takes the product from the body instead. It requires a Komerza
variant of your own mapped to the supplier's, so it only suits resellers who
also sell on Komerza. Ask the supplier if you need it.
## The response
`200 OK` with one entry per unit:
```json theme={null}
{
"items": ["LICENSE-KEY-ABC123", "LICENSE-KEY-DEF456"],
"sourceOrderId": "5f3a8b2c-1d4e-5f6a-7b8c-9d0e1f2a3b4c",
"cost": 14.0,
"currency": "USD",
"sampled": 0
}
```
The delivered units, one entry per unit sold. Hand these to your customer.
The order raised on the supplier's store. Quote it if you need to chase them
about a delivery.
What was charged to your balance, in the supplier's currency.
Units the supplier gave you free as a sample. These cost nothing.
The `items` shape is the same one Komerza's dynamic delivery accepts, so a Komerza store consuming this needs no translation.
**`cost` is worked out at the moment of the draw, not when you set your
prices.** If the supplier raises theirs, the next sale costs you more and
nothing stops it - check `cost` against what you charged, or buy up front,
where you agree the price before you pay it.
## Retries
`lineItemId` is the idempotency key. Calling again with the same one returns the units already drawn for it rather than drawing more - so a retry after a timeout is safe and cannot double-charge your balance.
Use a stable reference from your own order. A fresh one on each attempt reads as a separate sale and draws again, and **omitting it entirely means every call is a new purchase** - which is why it is the one field worth sending.
Your references are yours alone. Two resellers both sending `1` are two different draws; nobody ever receives someone else's stock by picking the same reference.
## Failures
The draw was refused. The body carries a short reason.
Responses are deliberately terse: the caller is a storefront, and the detail of *why* a draw failed is not something to expose there. The full reason is in your reseller panel on the supplier's store.
Common causes:
Top it up from the supplier's customer dashboard. Draws are charged at the
moment of sale, so a balance that runs out mid-day stops deliveries.
Nothing you can fix. Consider buying stock up front if they allow it, which
also protects you from them running out.
The supplier withdrew it. Stock you already bought outright is still yours.
They removed it from their reseller catalogue, or paused it. The same answer
covers a URL naming a product this supplier does not offer you at all -
which the catalogue is deliberately not going to confirm either way.
## Chains
A product can be resold through more than one store, but not indefinitely - a draw is refused once it has passed through **two** stores.
Between Komerza stores that is counted for you: Komerza walks the chain of listings above the product and refuses one that runs deeper than two stores, or that loops back on itself.
Off-platform there is no chain to walk - Komerza cannot see who called you - so the count has to arrive with the request:
```
X-Komerza-Resell-Depth: 1
```
Send it only when you are forwarding a draw you received: take the number you were sent and add one. A request without the header counts as the first hop, which is what an ordinary sale of your own is.
**Komerza does not add this header for you**, including on a draw it forwards
to another store, so a chain of off-platform shops is capped only if each one
counts. Passing a header through unchanged is not counting - the number has to
go up at every hop, or the ceiling is never reached.
## Volume pricing
Suppliers can set price breaks for buying in quantity. They apply to both routes, but they are far easier to reach by buying up front, since a per-sale draw is usually for one unit.
The highest threshold your quantity clears wins, and terms set on a specific product beat catalogue-wide ones. A price break worse than the everyday reseller price is ignored rather than charged, so a supplier's typo can never make buying in bulk cost more than buying one at a time.
Volume pricing asks *how many of this product*. Two other things ask *how much altogether*, and they are what a spread across a catalogue actually earns.
## Basket discounts
A supplier can take a percentage off any basket that reaches a size, whatever it is made of. A hundred weekly keys and a hundred yearly ones are the same number and nothing like the same order, so these are measured in **value**, not units.
The size is measured at list price, before anything else comes off - qualifying cannot lower a basket's value and disqualify it again. Where several thresholds apply, the largest wins.
## Bundles
A bundle is a basket the supplier composed and priced as one thing: so many of this, so many of that, one figure at the end.
```
100 × 7-Day Key $499.00
30 × 30-Day Key $509.70
25 × 90-Day Key $999.75
─────────────────────────────
Retail value $2,008.45
45% off
You pay $1,104.65
```
Buying one runs the same purchase as buying its lines yourself, so stock, delivery and refunds behave exactly as they always do. Three things are worth knowing:
* **It can never cost more than buying the lines separately.** If your own terms already beat the bundle, you pay yours.
* **A bundle is bought as composed.** Quantities are the supplier's, not yours - assembling your own basket is what basket discounts are for.
* **If a product in it is no longer offered, the whole bundle stops being buyable** rather than quietly shrinking. You are told which line is short.
A supplier can aim a bundle at particular resellers, cap how many times you may take it, or run it between two dates. One aimed at somebody else is not listed to you, and cannot be bought by guessing its id.
# Self-Custody Setup
Source: https://docs.komerza.com/guides/self-custody-setup
Receive crypto payments directly into wallets you control - zero fees, no third party ever holds your funds
**Komerza Self-Custody** settles crypto payments straight into wallets you own. Komerza only watches your addresses to confirm payments and mark orders as paid - it never holds, moves, or has access to your funds.
It sits alongside the processor options (NOWPayments, Heleket) on the Cryptocurrency page, but works differently:
* **Zero fees** - keep 100% of every crypto payment. Processors take a cut (NOWPayments 0.5-1%, Heleket 0.4%); self-custody takes nothing.
* **You hold the keys** - payments settle to a wallet you control; funds never touch Komerza.
* **No extra accounts** - just link a wallet address or `xpub`/`zpub`. No third-party signup or API keys.
You link a wallet **per chain**, in one of two ways depending on the chain:
| Chain | Link method |
| --------------- | ------------- |
| Solana (SOL) | WalletConnect |
| Ethereum (ETH) | WalletConnect |
| BNB Chain (BNB) | WalletConnect |
| Polygon (POL) | WalletConnect |
| Bitcoin (BTC) | Account xpub |
| Litecoin (LTC) | Account xpub |
| Dogecoin (DOGE) | Account xpub |
* **WalletConnect chains** - connect a wallet and Komerza reads a **deposit address** to watch.
* **xpub chains** - paste an **account xpub** and Komerza derives a fresh receiving address for every payment, so your private keys never leave your wallet.
## Prerequisites
* A store on the **Business plan** - Self-Custody is a Business-tier feature.
* A wallet for each chain you want to accept. WalletConnect chains work with almost any wallet; xpub chains need a wallet that can **export an account xpub** (see [Which wallets can export an xpub](#which-wallets-can-export-an-xpub)).
## Open Komerza Self-Custody
In your dashboard, open **Settings** under **Online Store**, then go to **Cryptocurrency**. Under **Processors**, find the **Komerza Self-Custody** card.
If you don't see Self-Custody, confirm your store is on the **Business plan**.
On the Komerza Self-Custody card, click **Link Wallet**. The **Link a receiving wallet** dialog opens.
Pick the **Chain** you want to set up. The dialog then shows either a **Connect Wallet** button or a **Link via account xpub** option, depending on the chain. Follow the matching section below, and repeat the whole process for each currency you want to accept.
## Path A - WalletConnect chains (SOL, ETH, BNB, Polygon)
These chains are the quick path. You click **Connect Wallet** to link a wallet with WalletConnect, and Komerza reads the deposit address to watch.
In **Link a receiving wallet**, choose the chain (e.g. Ethereum) and click **Connect Wallet**. A WalletConnect QR code and connection link appear.
Open your wallet and connect:
* **On mobile** - tap the connection link (or scan the QR from another device) to open your wallet app, then approve.
* **On desktop** - scan the QR code with your mobile wallet, or use a browser-extension wallet.
Approve the connection request. Komerza records the wallet's address for that chain and starts watching it.
Wallets are linked **per chain**, so connect once for every currency you want to accept.
**EVM chains (ETH, BNB, Polygon)** can all use the same wallet (MetaMask, Trust, Rabby, etc.) - you just run the connect step once per chain. **Solana (SOL)** needs a Solana wallet such as Phantom or Solflare.
## Path B - xpub chains (BTC, LTC, DOGE)
For these chains you paste an **account xpub** from your wallet. Komerza derives a fresh receiving address from it for each payment - your private keys never leave your wallet, and Komerza can only *watch*, never spend.
**Any format works.** Komerza auto-detects the derivation, so paste whatever
extended public key your wallet gives you - `xpub`, `ypub`, or `zpub` for
Bitcoin; `Ltub`/`Mtub` for Litecoin; `dgub` for Dogecoin. You don't need to
convert it.
Find and copy your wallet's account xpub. Steps vary by wallet - see [Export your xpub by wallet](#export-your-xpub-by-wallet) below.
Only ever paste an **account xpub** (`xpub`/`ypub`/`zpub`/…). **Never** paste your seed phrase or private key - Komerza never needs them, and no legitimate service ever will.
In **Link a receiving wallet**, select the chain, choose **Link via account xpub**, and paste your key into the **Account xpub** field. Komerza confirms it will derive a fresh receiving address per payment.
Double-check the chain and key before saving. Payments are sent on-chain and **cannot be reversed** - an xpub pasted under the wrong chain sends funds somewhere you may not control.
We strongly recommend a small test order (for example a **\$1** purchase) to confirm the payment is detected and the order is marked as paid before you go live.
### Export your xpub by wallet
1. Open Electrum with the wallet loaded.
2. Go to **Wallet → Information**.
3. Copy the **Master Public Key** shown - for a default Electrum wallet this starts with `zpub`. That's your account xpub; paste it into Komerza as-is.
Electrum is the most reliable option for Litecoin. For Dogecoin, use **Trezor Suite** below - the Dogecoin Electrum fork is poorly maintained.
1. Open your wallet in Sparrow. 2. In the left sidebar, click **Settings**. 3.
In the **Keystore** section, the extended public key is shown - you can toggle
the format (e.g. **XPUB / ZPUB**). Copy it. 4. Paste it into Komerza.
Trezor supports all three UTXO chains, which makes it a good single option.
1. Connect your Trezor and open **Trezor Suite**.
2. Select the coin account (**Bitcoin**, **Litecoin**, or **Dogecoin**) in the left sidebar.
3. Open the **Details** tab and click **Show public key**.
4. Verify the key **on your Trezor device**, then copy it and paste it into Komerza.
1. Open the wallet and tap the **••• (top right)** to open **Wallet Settings**.
2. Tap **Show Wallet XPUB**.
3. The xpub appears as a QR code and text - tap the text to copy it.
4. Paste it into Komerza.
BlueWallet is Bitcoin-only - use another wallet from this list for Litecoin or Dogecoin.
Exodus can export an xpub from its **desktop** app only - the mobile app doesn't expose it.
1. Open the **Exodus desktop** app and select the asset (e.g. Bitcoin).
2. Click the **••• (three dots)** at the top right.
3. Click **Export XPub**. Exodus saves a `.txt` file in an **`exodus-exports`** folder and shows you where - for example `…/exodus-exports/exodus_0-bitcoin-xpub.txt`.
4. Open that file. It lists several keys - a legacy `xpub` (starts with `xpub…`) followed by one or more native-segwit keys (start with `zpub…`).
5. Copy the **`zpub`** line - it's the newer native-segwit format and matches the addresses Exodus actually receives to. Paste it into Komerza.
On mobile, use the desktop app instead, or another wallet from this list.
Ledger Live shows the xpub for UTXO coins - **Bitcoin, Litecoin, Dogecoin**.
1. Open **Ledger Live** and connect your device.
2. Go to **Accounts** and select the coin account (e.g. Bitcoin).
3. Click the **tool/wrench icon** at the top right of the account.
4. Open **Advanced Logs** and copy the value that starts with `xpub`.
Ledger Live shows one **generic `xpub`** for the account regardless of address type (Legacy / SegWit / Native SegWit) - Komerza accepts it as-is.
### Which wallets can export an xpub
These popular wallets **cannot** export an xpub, so they can't be used for the
xpub chains: **Trust Wallet**, **Coinbase Wallet**, and **MetaMask** (MetaMask
is EVM-only and doesn't support these chains anyway). **Exodus** works too,
but from its **desktop app only** - the mobile app can't export an xpub. If
your wallet can't, move the relevant coins to a wallet from the list above -
such as **Electrum**, **Sparrow**, **Trezor Suite**, or **BlueWallet**.
## Route your coins to self-custody
Linking a wallet tells Komerza *where* to settle - but each coin also needs to be **routed** to the Komerza Self-Custody processor. On the Cryptocurrency page, open **Coin routing** and set each coin you want to self-custody to **Komerza Self-Custody**, or confirm default routing already points there.
If a coin is routed to a processor (NOWPayments or Heleket) instead of Komerza
Self-Custody, payments for that coin settle to the processor and incur its fee
* even if you've linked a self-custody wallet for the same chain.
## How payments settle
1. A customer chooses one of your self-custody currencies at checkout.
2. Komerza shows them a receiving address:
* For **WalletConnect chains**, your linked deposit address.
* For **xpub chains**, a fresh address derived from your xpub for that order.
3. The customer pays. Funds land **directly in your wallet** - Komerza never touches them.
4. Komerza detects the on-chain payment and marks the order as paid, triggering delivery.
## Troubleshooting
| Problem | Fix |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Self-Custody option not visible | Confirm your store is on the **Business plan**. |
| Coin still charging a fee | Check **Coin routing** - the coin is likely routed to NOWPayments or Heleket. Set it to **Komerza Self-Custody**. |
| xpub rejected when pasting | Make sure you copied the full **account xpub** (not a single address, seed phrase, or private key). Any format - `xpub`/`ypub`/`zpub`/`Ltub`/`dgub` - is accepted; copy the whole string with no spaces. |
| Test payment not detected | Give the network a few confirmations, and double-check you paid the correct chain. For xpub chains, confirm the xpub belongs to the wallet you're watching for the payment. |
| Wallet can't export an xpub | Your wallet isn't supported for that chain - move the coins to a wallet from [the supported list](#export-your-xpub-by-wallet). |
| WalletConnect won't connect | Refresh the dialog to generate a new QR/link, make sure your wallet is on the same chain you're linking, and update your wallet app if the session times out. |
# Setup Shopify
Source: https://docs.komerza.com/guides/shopify-setup
Configure Shopify payments on your Komerza store
Accept payments through Shopify on your Komerza store. Komerza creates draft orders on your Shopify store and redirects customers to Shopify's hosted checkout to complete payment.
## Prerequisites
* A [Shopify store](https://www.shopify.com/) with an active plan
* Access to the [Shopify Dev Dashboard](https://dev.shopify.com/dashboard/)
1. Go to the [Shopify Dev Dashboard](https://dev.shopify.com/dashboard/)
2. Click **Create an app** and give it a name (e.g. "Komerza Integration")
3. Set the **App URL** to `https://dashboard.komerza.com`
4. Add this **Allowed redirection URL** - exactly as written, with no trailing slash:
```
https://dashboard.komerza.com/shopify/callback
```
5. In **Access scopes**, paste:
```
write_draft_orders,read_draft_orders,read_orders,write_orders
```
6. Set the **Webhook version** to `2026-04`
7. Click **Create app**
The allowed redirection URL is the step people miss. Shopify requires the
exact callback URL to be registered on the app before it will show the
install screen, so without it authorization fails no matter how correct
your credentials are.
All four scopes are required. Missing any of them will cause connection
errors later.
Open the app's **Settings** and copy the **Client ID** and **Client secret**.
Keep these secure - they grant access to your Shopify store's order data.
1. In your Komerza dashboard, go to **Settings** → **Payment Methods** → **Shopify**
2. Enter your **Shop URL**: your `myshopify.com` domain only, e.g. `mystore.myshopify.com`
3. Paste the **Client ID** and **Secret** from the previous step
4. Click **Connect Shopify**
The Shop URL must be the bare `mystore.myshopify.com` domain. Do not
include `https://`, do not add a trailing slash, and do not use your
custom storefront domain or the `admin.shopify.com/store/...` address from
your browser's address bar - none of those work for authorization or for
creating orders.
1. You'll be redirected to Shopify to approve the app's permissions
2. Click **Install app** on the Shopify authorization screen
3. You'll be redirected back to your Komerza dashboard - the connection is now active
Finish this step. Saving your credentials makes Shopify available at your
checkout, but orders cannot be created until authorization completes - so
an abandoned connection leaves customers unable to pay.
The status will show:
* **Active** - Everything is connected and working
* **Inactive** - Authorization has not completed, or the credentials are wrong
## How It Works
1. A customer selects Shopify at checkout
2. Komerza creates a draft order on your Shopify store
3. The customer is redirected to Shopify's hosted checkout to complete payment
4. Once payment is confirmed, Komerza automatically marks the order as delivered
## Managing Payments
* **Refunds** - You can issue refunds through Komerza. The refund is processed via the Shopify Orders API and reflected in both platforms.
* **Webhooks** - Komerza listens for order and refund events from Shopify to keep everything in sync.
## Disconnecting Shopify
Go to **Settings** → **Payment Methods** → **Shopify** and click **Disconnect**. Your existing orders are not affected - only new checkouts will no longer offer Shopify.
## Troubleshooting
| Problem | Fix |
| ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization fails, or Shopify rejects the redirect | Add `https://dashboard.komerza.com/shopify/callback` to your app's **Allowed redirection URL(s)**. It must match exactly, with no trailing slash |
| "Missing required permissions" error | Make sure all four access scopes are set on the app, then reconnect |
| Customers see "This store's payment method is misconfigured" | Your Shop URL is not a bare `mystore.myshopify.com` domain. Re-enter it without `https://`, without a trailing slash, and without a custom domain |
| Shopify appears at checkout but orders fail | Authorization was never completed. Reconnect and finish the **Install app** step on Shopify |
| Webhooks not firing | Verify your app has the `read_orders` scope - Shopify requires it for order and refund webhook subscriptions. Webhooks are only registered once authorization completes |
| "Please configure your Shopify app credentials first" | Save your Client ID and Secret before clicking Connect |
# Selling Subscriptions
Source: https://docs.komerza.com/guides/subscriptions
Sell recurring products, control renewals, and manage subscriber access
Any product variant can be sold as a subscription. Turn on recurring billing for the variant and every purchase of it creates a subscription for that customer. Individual subscriptions live under **Products → Subscriptions**; store-wide behaviour is configured in **Settings → Subscriptions**.
If **Products → Subscriptions** isn't in your dashboard, contact support to
confirm whether subscriptions are enabled for your account. Viewing the page
also requires the View Orders team permission.
## Making a variant recurring
On the variant, expand **Recurring Billing**, switch on **Enable recurring billing**, and pick a **Billing interval**: Weekly, Monthly, Quarterly, Yearly, or Custom with a day count.
Two rules decide the price a subscriber pays:
* The **price and currency are locked in at signup** from the variant price and your store currency.
* **Renewals are charged at the variant's current price.** If you raise the price later, existing subscribers renew at the new price. Renewal reminder emails go out before each charge, so it isn't silent, but it is a real change to your subscribers' bills.
Subscription and one-time products cannot be combined in a single order, and a
100%-off order cannot create a subscription.
## Automatic vs manual renewal
This is the single most important thing to understand before you launch, because it is decided by the payment method, not by you.
| Payment method | Renewal behaviour |
| ------------------------------------------------------- | --------------------------------------------------------------------- |
| **Stripe or Mollie** | Automatic. The gateway charges the stored payment method each period. |
| **Everything else** (crypto, PayPal, Square, and so on) | Manual. Nothing is charged automatically. |
Manual-renewal subscribers get reminder emails before expiry containing a checkout link for the next period. That renewal order is reused while it stays unpaid, so a customer clicking an older reminder link will not be charged twice.
The store setting **Force manual renewal** turns even Stripe and Mollie subscriptions into manual renewals, if you would rather every period be an explicit decision by the customer.
## Store-wide settings
**Settings → Subscriptions** has three sections.
* **Allow customers to pause** - off by default, with an optional maximum pause duration in days and a maximum pause count.
* **Allow customers to cancel** - on by default, with a **Cancel mode**: *End of period* (access continues until the period ends, and the customer can reactivate to undo it) or *Immediate* (access is revoked right away).
* **Allow customers to reactivate** - on by default.
* **Allow payment method updates** - on by default. The customer is redirected to the gateway's own secure form.
Immediate cancellation applies **no proration and no automatic refund**. If a refund is appropriate, you issue it yourself.
**Grace period days** (default 3) and **Max retry attempts** (default 3),
used when an automatic charge fails.
* **Send expiry & renewal notifications** - on by default, with reminders at **7, 3 and 1 days** before expiry.
* **Apply grace period on manual renewal** - off by default. Manual subscriptions otherwise expire at the end of the period.
* **Revoke access on expiry** - on by default. See the warning below.
## Statuses
Paid and current.
An automatic charge failed. The payment is retried on a backoff schedule, and
the subscription's detail page shows Failed Payment Count, Last Failure and
Next Retry.
Resumes automatically at its resume date. Note that the billing period
restarts from the resume date.
Ended by you or the customer, according to your cancel mode.
Reached after the configured maximum retry attempts. You and the customer are
both notified.
If a customer was charged but the subscription still shows Past Due, wait
before intervening. Status is reconciled with Stripe and Mollie automatically,
and a paid invoice flips it back to Active.
## Access revocation
With **Revoke access on expiry** enabled, delivered items are revoked when a
subscription expires or is cancelled immediately. **License keys are redacted
permanently and cannot be restored automatically** - if the customer
resubscribes, you must re-issue keys manually. File downloads re-enable on
their own while the subscription is active.
This is worth deciding deliberately before you have subscribers. Revoking access protects a licensed product; it also means a lapsed-then-returning customer needs manual work from you.
## What your customers can do
Customers currently have **no self-service billing area**. There is nowhere for them to cancel, pause or change a subscription themselves, so subscribers contact you and you make the change from **Products → Subscriptions**.
A message like "Cancellation is not allowed for this subscription" means your store or product configuration forbids that action - change it in **Settings → Subscriptions**.
Subscription questions from your customers come to you, not to Komerza.
## Next steps
} href="/guides/products-and-variants">
Where recurring billing is configured.
} href="/guides/orders">
Renewal orders behave like any other order.
} href="/guides/payment-methods">
Only Stripe and Mollie support automatic renewal.
} href="/guides/notifications">
Alerts for renewals, failures and expiries.
# Teams & Permissions
Source: https://docs.komerza.com/guides/teams-and-permissions
Give other people access to your stores without sharing your login
Teams let you bring in staff, a VA, a developer or a business partner without handing over your account. You create a team, attach your stores to it, and invite members by email with their own granular permissions. Teams are included on **every plan, including Free**, at no extra cost.
Teams are managed under **Account Settings → Teams**.
## How teams work
A team is created and owned by one merchant account - yours. The owner
attaches their **own stores** to it, and members then work on those shared
stores.
The invitee **must already have a Komerza account** with that exact email
address. If not, the invite fails with: "No user with that email address is
registered on Komerza. Please ensure an account is created with that email
before sending an invitation." Ask them to register first - a Free account
is enough - then send the invite again.
The invitation email contains an accept link valid for **72 hours**. Members
show as inactive until they accept. If the link expired, just send a new
invite.
## Permissions
Each member gets their own permission set, chosen by the owner. Permissions are granular - view, create, update and delete - across the areas of the attached stores. You will see friendly names such as "Update Products", "View Orders", "Deliver Orders" and "Refund Orders".
Permissions cover products, orders, coupons, customers, categories, tickets, blacklist, discount dialogs, email marketing, blog, tax, subscriptions, buyer protection, webhooks, analytics and the audit log. "View Stores" is always included for members.
Be deliberate with the money-adjacent and destructive ones. **Refund Orders**
moves real money, **Deliver Orders** consumes stock and issues license keys
without verifying payment, and delete permissions are irreversible. Grant the
minimum each person actually needs.
The same permission model backs API access, so the [scopes reference](/api-reference/scopes) is a useful companion when you are deciding what a member or an integration should be able to do.
## Who can do what
| Action | Owner | Member |
| ------------------------ | ----- | ------ |
| Invite or remove members | Yes | No |
| Edit member permissions | Yes | No |
| Add or remove stores | Yes | No |
| Rename the team | Yes | No |
| Leave the team | n/a | Yes |
There is **no ownership transfer**. A team, or its stores, cannot be handed to
another account through the dashboard. If a business changes hands, contact
support before making any other changes.
## The audit log
The owner, and any member granted "View Audit Log", can review what members have done on shared stores. The log filters by member email, store, method, date range, and a destructive-action flag, and lives in the dashboard's Audit Log tab.
If a member changed something they shouldn't have: check the audit log to confirm who did what, tighten that member's permissions or remove them from the team, then undo the change in the store.
## Common questions
The invitee must register a Komerza account with that exact email address
first, then you resend the invite.
Links last 72 hours. Send a fresh invite.
No. Inviting, removing and permission changes are owner-only.
No. Teams are included on every plan, Free included.
They are missing the permission for that action. Some features also require
a related permission to be visible at all - for example the Buyer Protection
page requires the store analytics view permission.
## Next steps
} href="/api-reference/scopes">
The same permission model, applied to API access.
} href="/guides/notifications">
Store alerts reach members holding the relevant permission.
# Store Webhooks
Source: https://docs.komerza.com/guides/webhooks
Receive order events on your own server, with signed payloads, delivery logs and retries
Store webhooks push order events from Komerza to your server as they happen - so you can sync orders into your own database, trigger fulfilment in an external system, or notify your team. They are managed in the dashboard under **Developers → Webhooks**.
Webhooks are **not** the same thing as [Dynamic
Delivery](/guides/dynamic-delivery). A webhook notifies you that something
happened; dynamic delivery asks your server for the content to deliver, and
its response is sent to the customer. Use dynamic delivery to generate goods,
and webhooks to observe orders.
## Events
A new order was created.
Payment was received or detected and is settling.
The order was delivered. Also fires for manual and re-deliveries.
The order was cancelled or expired.
The order was refunded. Also emitted when an order enters the Disputed state.
## Payload
Each delivery is a JSON envelope containing the event name, the order data, a `messageId` and a timestamp:
```json theme={null}
{
"event": "order.completed",
"messageId": "0f2b0b2c-1c2f-4b7f-9e2a-8f2b1c9d4e77",
"timestamp": "2026-07-31T14:02:11Z",
"data": {
"id": "...",
"status": "Delivered"
}
}
```
Use `messageId` for idempotency. Retries re-send the original payload, so a
handler that stores processed message IDs will never double-process an order.
## Verifying the signature
Every request is signed with an HMAC signature in the **`X-Signature`** header. Verify it against your webhook's signing secret before trusting the payload - an unverified endpoint will accept anything anyone posts to it.
The verification approach is the same as for dynamic delivery: compute an HMAC-SHA256 of the raw request body using your secret, hex-encode it, and compare against the header. Full code samples for Node.js, Python and PHP are in the [Dynamic Delivery guide](/guides/dynamic-delivery#signature-verification).
Compare the **raw** request body, exactly as received. Parsing and
re-serialising the JSON before hashing changes the bytes and the signature
will never match.
## Creating a webhook
Each webhook is scoped to one or more of your stores. You can have up to
**25 webhooks per user**.
The URL must be HTTP or HTTPS. URLs that resolve to private or reserved
addresses are blocked - the log entry reads "Blocked: webhook URL resolved
to a private or reserved address."
The signing secret is shown **exactly once**, at creation: "Copy your
signing secret below - it won't be shown again." There is no way to view it
later.
If you lose the signing secret, you cannot recover or rotate it - create a new
webhook to get a fresh secret. Deleting a webhook also **deletes all of its
logs**.
## Delivery logs
Each webhook has its own execution log. You can filter by status (Success 2xx / Failed 4xx-5xx) and date range, and search by log ID, order ID, or a fragment of the URL.
Failed deliveries record a readable reason - TLS or certificate failure, DNS failure, connection refused, or timeout. Each entry also records its attempt/retry number, so you can watch a failed delivery being retried.
**Resend** on any log entry re-sends the original payload using the webhook's current URL and secret.
### Paused delivery
If your endpoint fails repeatedly, delivery is temporarily paused and entries appear with response code **-1**:
> Delivery is temporarily paused because this endpoint has failed repeatedly. Delivery will resume automatically once it starts responding.
Fix the endpoint and delivery resumes on its own - there is nothing to re-enable in the dashboard.
### Per-order logs
On any order's detail page, the **Webhook Execution Logs** card shows the most recent 100 deliveries for that specific order (date, URL, status code, attempt/retry number, response) - useful when debugging one order rather than a whole endpoint.
## Endpoint best practices
Acknowledge the delivery immediately and process asynchronously. Slow
handlers time out and get retried, which looks like duplicate events.
Retries, manual **Resend**, and re-deliveries all mean the same order can
arrive more than once. Key your processing on `messageId` or the order ID.
Check the `X-Signature` header on every request, and return 401 when it does
not match.
Endpoints go down. Reconcile periodically against the [Orders
API](/api-reference/endpoint/orders/get-list) rather than assuming every
event arrived.
Discord notifications are configured in **Store Settings → Notifications**,
not on the Webhooks page.
## Next steps
} href="/api-reference/endpoint/webhooks/create">
Create, update and inspect webhooks programmatically.
} href="/api-reference/endpoint/webhooks/get-logs">
Read delivery logs and resend deliveries.
} href="/guides/dynamic-delivery">
Generate delivery content on demand from your own server.
} href="/guides/orders">
What each order event means operationally.
# Guides, API Reference and SDKs
Source: https://docs.komerza.com/index
Guides for running your store, plus the API, SDKs and integrations for building on Komerza
# How can we help?
Guides for setting up and running your store, plus the API, SDKs and
integrations for building on Komerza.
Search the documentation
⌘K
Popular:
How delivery works
Payment methods
Managing orders
Embed not loading
# Blacklist Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/blacklist
POST /stores/{storeId}/affiliates/{customerId}/blacklist
Disables the affiliate, blocks the customer on the store, and adds their email to the store's blacklist.
Requires the `stores.affiliates.update` permission.
Blacklisting stops the affiliate earning on new referrals. It does not claw
back commission already accrued, and it does not refund orders they referred.
# Configure Affiliate Program
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/configure
PUT /stores/{storeId}/affiliates/configure
Updates the configuration options for the affiliate program of the specified store.
Requires the `stores.affiliates.update` permission.
# Create Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/create
POST /stores/{storeId}/affiliates/{customerId}
Creates a new affiliate record for the given customer in this store.
Requires the `stores.affiliates.update` permission.
# Delete Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/delete
DELETE /stores/{storeId}/affiliates/{customerId}
Permanently removes an affiliate and all their payout records from the store.
Requires the `stores.affiliates.update` permission.
# Get Affiliates
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/get-list
GET /stores/{storeId}/affiliates
Retrieves affiliates for the specified store using query-based filter options.
Supports filtering, sorting, and pagination via query parameters.
Requires the `stores.affiliates.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ---------------------- | ------- | ----------------------------------------------------------- |
| `isEnabled` | boolean | Filter by affiliate enabled status |
| `email` | string | Filter by email address (contains) |
| `link` | string | Filter by affiliate link/code (contains) |
| `balanceFrom` | number | Minimum affiliate balance (inclusive) |
| `balanceTo` | number | Maximum affiliate balance (inclusive) |
| `returnPercentageFrom` | integer | Minimum return percentage (inclusive) |
| `returnPercentageTo` | integer | Maximum return percentage (inclusive) |
| `percentageOffFrom` | integer | Minimum discount percentage (inclusive) |
| `percentageOffTo` | integer | Maximum discount percentage (inclusive) |
| `referredOrdersFrom` | integer | Minimum referred delivered orders (inclusive) |
| `referredOrdersTo` | integer | Maximum referred delivered orders (inclusive) |
| `allTimeEarnedFrom` | number | Minimum all-time earned amount (inclusive) |
| `allTimeEarnedTo` | number | Maximum all-time earned amount (inclusive) |
| `payoutCountFrom` | integer | Minimum payout count (inclusive) |
| `payoutCountTo` | integer | Maximum payout count (inclusive) |
| `dateFrom` | string | Filter affiliates created from this date/time (inclusive) |
| `dateTo` | string | Filter affiliates created before this date/time (inclusive) |
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | -------- | ----------------------------------------------------- |
| `isEnabled` | boolean | Filter by whether the affiliate profile is enabled |
| `email` | string | Filter by affiliate email (case-insensitive contains) |
| `link` | string | Filter by referral link or code |
| `balance` | number | Current unpaid balance |
| `allTimeEarned` | number | Total commission earned across all time |
| `referredOrders` | integer | Number of orders attributed to this affiliate |
| `payoutCount` | integer | Number of payouts recorded against this affiliate |
| `returnPercentage` | number | The affiliate's commission rate |
| `percentageOff` | number | The customer discount applied to their referrals |
| `dateCreated` | datetime | When the affiliate profile was created |
Numeric and date fields take ranges through the comparison operators rather than separate from/to parameters.
**Sortable:** `isEnabled`, `email`, `link`, `referredOrders`, `allTimeEarned`, `payoutCount`, `dateCreated`.
**Example:**
```bash theme={null}
# Enabled affiliates who have earned over 100, biggest earners first
GET /stores/{storeId}/affiliates?filters=isEnabled==true,allTimeEarned>=100&sorts=-allTimeEarned
# Affiliates with an outstanding balance and at least one referred order
GET /stores/{storeId}/affiliates?filters=balance>0,referredOrders>=1
```
# List Affiliate Orders
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/get-orders
GET /stores/{storeId}/affiliates/{customerId}/orders/{page}
Retrieves a paginated list of orders attributed to a specific affiliate (by customerId) in the store.
Requires the `stores.affiliates.view` permission.
# List Affiliate Payouts
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/get-payouts
GET /stores/{storeId}/affiliates/{customerId}/payouts/{page}
Requires the `stores.affiliates.view` permission.
# Invite Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/invite
POST /stores/{storeId}/affiliates/invite
Invites an affiliate, this will create a customer profile if one does not exist and takes in a customer email
address instead.
Requires the `stores.affiliates.update` permission.
# Record Affiliate Payout
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/record-payout
POST /stores/{storeId}/affiliates/{customerId}/payouts
Requires the `stores.affiliates.update` permission.
**This records a payout, it does not send money.** Komerza reduces the
affiliate's balance by the amount; the actual transfer happens between you and
your affiliate outside Komerza.
Record it only once you have actually paid, or your balances will disagree with
reality.
# Search Affiliates
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/search
GET /stores/{storeId}/affiliates/search
Searches affiliates by the `query` parameter and applies filtering/sorting/pagination.
The query is matched against affiliate email, affiliate link/code, affiliate ID, and customer ID.
Requires the `stores.affiliates.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | -------- | ----------------------------------------------------- |
| `isEnabled` | boolean | Filter by whether the affiliate profile is enabled |
| `email` | string | Filter by affiliate email (case-insensitive contains) |
| `link` | string | Filter by referral link or code |
| `balance` | number | Current unpaid balance |
| `allTimeEarned` | number | Total commission earned across all time |
| `referredOrders` | integer | Number of orders attributed to this affiliate |
| `payoutCount` | integer | Number of payouts recorded against this affiliate |
| `returnPercentage` | number | The affiliate's commission rate |
| `percentageOff` | number | The customer discount applied to their referrals |
| `dateCreated` | datetime | When the affiliate profile was created |
Numeric and date fields take ranges through the comparison operators rather than separate from/to parameters.
**Sortable:** `isEnabled`, `email`, `link`, `referredOrders`, `allTimeEarned`, `payoutCount`, `dateCreated`.
**Example:**
```bash theme={null}
# Enabled affiliates who have earned over 100, biggest earners first
GET /stores/{storeId}/affiliates?filters=isEnabled==true,allTimeEarned>=100&sorts=-allTimeEarned
# Affiliates with an outstanding balance and at least one referred order
GET /stores/{storeId}/affiliates?filters=balance>0,referredOrders>=1
```
# Get Affiliate Statistics
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/stats
GET /stores/{storeId}/affiliates/stats
Retrieves the affiliate statistics associated with a specific store.
Requires the `stores.affiliates.view` permission.
# Update Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/update
PUT /stores/{storeId}/affiliates/{customerId}
Updates affiliate settings for the given customer in this store.
Requires the `stores.affiliates.update` permission.
# Void Affiliate Payout
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/void-payout
PATCH /stores/{storeId}/affiliates/{customerId}/payouts/{payoutId}
Marks the payout as voided, restores balances, and records the admin and reason.
Requires the `stores.affiliates.update` permission.
Voiding a recorded payout **restores the amount to the affiliate's balance**.
Use it when a payout was recorded in error or the transfer failed - not to
reverse money you actually sent.
# Create Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/create
POST /stores/{storeId}/coupons
Creates a new coupon in the store.
Requires the `stores.coupons.create` permission.
# Delete Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/delete
DELETE /stores/{storeId}/coupons/{couponId}
Deletes an existing coupon from the store.
Requires the `stores.coupons.delete` permission.
# Get Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/get
GET /stores/{storeId}/coupons/{couponId}
Gets a coupon by ID.
Requires the `stores.coupons.view` permission.
# Get All Coupons
Source: https://docs.komerza.com/api-reference/endpoint/coupons/get-all
GET /stores/{storeId}/coupons/all
Gets the coupons in the store as references.
Requires the `stores.coupons.view` permission.
# Get Coupons
Source: https://docs.komerza.com/api-reference/endpoint/coupons/get-list
GET /stores/{storeId}/coupons
Gets the coupons in the store.
Requires the `stores.coupons.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------ |
| `usesFrom` | integer | Filter by minimum number of times coupon has been used |
| `usesTo` | integer | Filter by maximum number of times coupon has been used |
| `maximumUsesFrom` | integer | Filter by minimum maximum uses allowed |
| `maximumUsesTo` | integer | Filter by maximum maximum uses allowed |
| `percentageFrom` | decimal | Filter by minimum percentage discount |
| `percentageTo` | decimal | Filter by maximum percentage discount |
| `amountFrom` | decimal | Filter by minimum amount discount |
| `amountTo` | decimal | Filter by maximum amount discount |
| `isEnabled` | boolean | Filter by whether coupon is enabled |
**Example:**
```bash theme={null}
# Active coupons with 20-50% discount
GET /stores/{storeId}/coupons?filters=isEnabled==true,percentageFrom>=20,percentageTo<=50
# Heavily used coupons
GET /stores/{storeId}/coupons?filters=usesFrom>=100&sorts=-uses
```
# Search Coupons
Source: https://docs.komerza.com/api-reference/endpoint/coupons/search
GET /stores/{storeId}/coupons/search/{query}
Searches the coupons in the store by the given query.
Requires the `stores.coupons.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------ |
| `usesFrom` | integer | Filter by minimum number of times coupon has been used |
| `usesTo` | integer | Filter by maximum number of times coupon has been used |
| `maximumUsesFrom` | integer | Filter by minimum maximum uses allowed |
| `maximumUsesTo` | integer | Filter by maximum maximum uses allowed |
| `percentageFrom` | decimal | Filter by minimum percentage discount |
| `percentageTo` | decimal | Filter by maximum percentage discount |
| `amountFrom` | decimal | Filter by minimum amount discount |
| `amountTo` | decimal | Filter by maximum amount discount |
| `isEnabled` | boolean | Filter by whether coupon is enabled |
**Example:**
```bash theme={null}
# Active coupons with 20-50% discount
GET /stores/{storeId}/coupons?filters=isEnabled==true,percentageFrom>=20,percentageTo<=50
# Heavily used coupons
GET /stores/{storeId}/coupons?filters=usesFrom>=100&sorts=-uses
```
# Update Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/update
PUT /stores/{storeId}/coupons/{couponId}
Requires the `stores.coupons.update` permission.
# Create Customer
Source: https://docs.komerza.com/api-reference/endpoint/customers/create
POST /stores/{storeId}/customers
Registers a customer for the store using their email.
If the customer already exists and is linked to the store, a 409 conflict is returned.
Requires the `stores.customers.create` permission.
# Get Customer
Source: https://docs.komerza.com/api-reference/endpoint/customers/get
GET /stores/{storeId}/customers/{customerId}
Retrieves detailed information for a single customer in the specified store.
Requires the `stores.customers.view` permission.
# Get Customer by Email
Source: https://docs.komerza.com/api-reference/endpoint/customers/get-by-email
GET /stores/{storeId}/customers/{customerEmail}/email
Retrieves detailed information for a single customer in the specified store.
Requires the `stores.customers.view` permission.
# Get Customers
Source: https://docs.komerza.com/api-reference/endpoint/customers/get-list
GET /stores/{storeId}/customers
Retrieves a paginated list of customers for the specified store.
Requires the `stores.customers.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | ------- | ---------------------------------------------------- |
| `email` | string | Filter by email address (case-insensitive contains) |
| `ipAddress` | string | Filter by IP address (case-insensitive contains) |
| `balanceFrom` | decimal | Filter by minimum balance (inclusive) |
| `balanceTo` | decimal | Filter by maximum balance (inclusive) |
| `stripeCustomerId` | string | Filter by Stripe customer ID (exact match) |
| `affiliateCode` | string | Filter by affiliate code (case-insensitive contains) |
**Example:**
```bash theme={null}
# Customers with balance between $10 and $100
GET /stores/{storeId}/customers?filters=balanceFrom>=10,balanceTo<=100
# Customers by affiliate code
GET /stores/{storeId}/customers?filters=affiliateCode@=*PROMO
```
# Search Customers
Source: https://docs.komerza.com/api-reference/endpoint/customers/search
GET /stores/{storeId}/customers/search/{query}
Retrieves a paginated list of customers for the specified store, matching the provided query.
Requires the `stores.customers.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | ------- | ---------------------------------------------------- |
| `email` | string | Filter by email address (case-insensitive contains) |
| `ipAddress` | string | Filter by IP address (case-insensitive contains) |
| `balanceFrom` | decimal | Filter by minimum balance (inclusive) |
| `balanceTo` | decimal | Filter by maximum balance (inclusive) |
| `stripeCustomerId` | string | Filter by Stripe customer ID (exact match) |
| `affiliateCode` | string | Filter by affiliate code (case-insensitive contains) |
**Example:**
```bash theme={null}
# Customers with balance between $10 and $100
GET /stores/{storeId}/customers?filters=balanceFrom>=10,balanceTo<=100
# Customers by affiliate code
GET /stores/{storeId}/customers?filters=affiliateCode@=*PROMO
```
# Update Customer Balance
Source: https://docs.komerza.com/api-reference/endpoint/customers/update-balance
PATCH /stores/{storeId}/customers/{customerId}/balance
Updates the store-specific balance for a customer.
Requires the `stores.customers.update` permission.
**Store credit is real spendable value.** The balance applies immediately at
your checkout and never expires, so treat this like issuing money.
It is not cashable - customers cannot withdraw it - and a top-up order can
never be refunded to balance, since that would credit them twice.
# Approve Custom Payment
Source: https://docs.komerza.com/api-reference/endpoint/orders/approve-custom-payment
POST /stores/{storeId}/paymentMethods/custom/orders/{orderId}/approve
Approves a pending custom payment order, marking it as delivered.
Requires the `stores.orders.deliver` permission.
# Create Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/create
POST /stores/{storeId}/orders
Creates a new order for a customer in the admin dashboard.
Requires the `stores.orders.create` permission.
## Payment Methods
When creating an order, specify the `paymentMethod` field using one of the supported payment method codes. See the [Payment Methods reference](/api-reference/payment-methods) for the complete list of available options.
## Redirecting to Checkout
After successfully creating an order, redirect the customer to the hosted checkout page to complete payment:
```
https://checkout.komerza.com/checkout/{orderId}
```
### Example
```javascript theme={null}
const response = await fetch(`/stores/${storeId}/orders`, {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}` },
body: JSON.stringify(orderData),
});
const result = await response.json();
if (result.success) {
const orderId = result.data.id;
window.location.href = `https://checkout.komerza.com/checkout/${orderId}`;
}
```
The checkout page handles payment processing, order confirmation, and receipt
generation automatically.
# Deliver Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/deliver
PUT /stores/{storeId}/orders/{orderId}/deliver
Forces delivery of an existing order as if purchased normally.
No action is taken if the order is already delivered.
Requires the `stores.orders.deliver` permission.
**This does not verify that you were paid.** It runs the full delivery flow as
though payment had arrived: stock is consumed, license keys leave your
inventory, the receipt email is sent, `order.completed` fires and Discord roles
are granted.
Confirm the payment first. For custom payment methods, use [Approve Custom
Payment](/api-reference/endpoint/orders/approve-custom-payment) instead, which
is the reviewed path.
Manually delivered orders can only ever be refunded to **customer balance**,
never back to the gateway.
# Get Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/get
GET /stores/{storeId}/orders/{orderId}
Retrieve order details by ID in the given store.
Requires the `stores.orders.view` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
# Get Orders
Source: https://docs.komerza.com/api-reference/endpoint/orders/get-list
GET /stores/{storeId}/orders
Gets orders in the specified store and optionally filters by the given criteria.
Requires the `stores.orders.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ---------- | -------- | -------------------------------------------------------------------------------- |
| `dateFrom` | datetime | Filter orders created on or after this date |
| `dateTo` | datetime | Filter orders created on or before this date |
| `status` | string | Filter by order status. See [Order Statuses](/api-reference/payment-status) |
| `gateway` | string | Filter by payment gateway. See [Payment Methods](/api-reference/payment-methods) |
| `email` | string | Filter by customer email address |
**Example:**
```bash theme={null}
# Orders from last 30 days with "pending" status
GET /stores/{storeId}/orders?filters=dateFrom>=2025-10-28,status==pending
# Orders paid via Stripe, sorted by newest first
GET /stores/{storeId}/orders?filters=gateway==stripe&sorts=-dateFrom
```
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
# Get Proof of Payment
Source: https://docs.komerza.com/api-reference/endpoint/orders/get-payment-proof
GET /stores/{storeId}/paymentMethods/custom/orders/{orderId}/proof
Gets the proof of payment submitted by a customer for an order.
Requires the `stores.orders.view` permission.
# Re-deliver Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/redeliver
PUT /stores/{storeId}/orders/{orderId}/redeliver
Forces redelivery of an already delivered order as if purchased normally.
Requires the `stores.orders.deliver` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
**This reruns the entire delivery, exactly like a fresh purchase.** It takes
**new license keys** from your inventory, re-fires dynamic delivery webhooks,
and emails the customer again.
Call it once, deliberately. Repeated calls consume stock every time, and there
is no way to return issued keys to inventory.
# Refund Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/refund
DELETE /stores/{storeId}/orders/{orderId}/refund
Refunds a delivered order for a customer in the admin dashboard.
Requires the `stores.orders.refund` permission.
**Refunds move real money and cannot be undone.** Only **Delivered** orders can
be refunded, always for the **full amount** - there are no partial refunds.
A successful refund also reverses any affiliate commission and the Buyer
Protection accrual on that order.
For cryptocurrency, custom payment methods, Cash App and PayPal Friends &
Family, marking an order refunded **moves no money** - you must return the
funds yourself. Those methods can only refund to customer balance.
# Reject Custom Payment
Source: https://docs.komerza.com/api-reference/endpoint/orders/reject-custom-payment
POST /stores/{storeId}/paymentMethods/custom/orders/{orderId}/reject
Rejects a pending custom payment order, cancelling it.
Requires the `stores.orders.deliver` permission.
# Resend Receipt
Source: https://docs.komerza.com/api-reference/endpoint/orders/resend-receipt
PUT /stores/{storeId}/orders/{orderId}/resend
Resends the delivery receipt to the customer for a previously delivered order.
Rate limited due to sensitivity.
Requires the `stores.orders.deliver` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
# Search Orders
Source: https://docs.komerza.com/api-reference/endpoint/orders/search
GET /stores/{storeId}/orders/search/{query}
Search orders by ID or customer email in the specified store.
Requires the `stores.orders.view` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ---------- | -------- | -------------------------------------------------------------------------------- |
| `dateFrom` | datetime | Filter orders created on or after this date |
| `dateTo` | datetime | Filter orders created on or before this date |
| `status` | string | Filter by order status. See [Order Statuses](/api-reference/payment-status) |
| `gateway` | string | Filter by payment gateway. See [Payment Methods](/api-reference/payment-methods) |
| `email` | string | Filter by customer email address |
**Example:**
```bash theme={null}
# Orders from last 30 days with "pending" status
GET /stores/{storeId}/orders?filters=dateFrom>=2025-10-28,status==pending
# Orders paid via Stripe, sorted by newest first
GET /stores/{storeId}/orders?filters=gateway==stripe&sorts=-dateFrom
```
# Add to Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/add-to-catalogue
PUT /stores/{storeId}/reseller/catalogue/{variantId}
Adds a variant to the catalogue or updates its terms. The variant does not have to be visible on
the storefront — offering resellers something ordinary customers never see is a supported use.
Requires the `stores.resellers.update` permission.
# Configure Programme
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/configure
PUT /stores/{storeId}/reseller
Sets whether the store supplies resellers, how they enrol, the discount they pay, and the
optional exposure and top-up limits.
Requires the `stores.resellers.update` permission.
# Create Application
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-application
POST /stores/{storeId}/reseller/applications
For a merchant hosting their own application form. Collect whatever fields suit your site, then
post them here with the applicant's email — answers may reference the questions configured on
this store, carry a `label` of your own, or mix the two.
The email is taken as given and not verified: the form is yours, on your domain, and you
review what it produces. The applicant proves the address the first time they sign in to
deposit or reach their reseller panel.
The application lands in the same review queue as every other, at
`GET /stores/{storeId}/reseller/application/submissions`.
Requires the `stores.resellers.update` permission.
# Create Basket Discount
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-basket-tier
POST /stores/{storeId}/reseller/basket-tiers
Measured at list price before anything comes off, so qualifying for a discount cannot lower a
basket's value and disqualify it again. Where several apply, the largest wins.
Requires the `stores.resellers.update` permission.
# Create Bundle
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-bundle
POST /stores/{storeId}/reseller/bundles
A bundle is a basket you have composed and priced as one thing. Set a discount to advertise a
figure, or leave it off and let your basket-size discounts decide.
Every product in it has to be in your reseller catalogue already, and it can never cost a
reseller more than buying the same lines separately would.
Requires the `stores.resellers.update` permission.
# Create Invitation
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-invite
POST /stores/{storeId}/reseller/invites
Returns the invite token once and only once — only its hash is stored, because the token grants
an already-approved reseller relationship and a leaked invite list must not be usable. Send it on
immediately; it cannot be read back.
Requires the `stores.resellers.update` permission.
# Create Question
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-question
POST /stores/{storeId}/reseller/application/questions
Questions can be added, reworded and reordered at any time, including while applications are
waiting: an answer records the wording it was given against, so editing the form never changes
what someone already in the queue appears to have been asked.
Requires the `stores.resellers.update` permission.
# Delete Basket Discount
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-basket-tier
DELETE /stores/{storeId}/reseller/basket-tiers/{basketTierId}
Requires the `stores.resellers.update` permission.
# Delete Bundle
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-bundle
DELETE /stores/{storeId}/reseller/bundles/{bundleId}
Takes it off sale. What resellers already bought through it, and what they paid, stays on record.
Requires the `stores.resellers.update` permission.
# Delete Invitation
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-invite
DELETE /stores/{storeId}/reseller/invites/{inviteId}
Requires the `stores.resellers.update` permission.
# Delete Question
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-question
DELETE /stores/{storeId}/reseller/application/questions/{questionId}
Archived rather than deleted. Every answer ever given to it still points at it, and tidying the
form should not orphan the applications that answered it.
Requires the `stores.resellers.update` permission.
# Get Programme
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get
GET /stores/{storeId}/reseller
Returns the store's reseller settings plus headline counts. A store that has never configured a
programme comes back disabled with defaults rather than a 404.
Requires the `stores.resellers.view` permission.
# Get Applications
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-applications
GET /stores/{storeId}/reseller/application/submissions
The review queue: who has applied, what they answered, and what they have deposited since. The
deposit is summed from the balance ledger rather than stored, so it always matches the money
that actually arrived.
Requires the `stores.resellers.view` permission.
# Get Basket Discounts
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-basket-tiers
GET /stores/{storeId}/reseller/basket-tiers
Volume terms measured on what a whole basket comes to rather than on how many of one product it
holds, so a reseller taking a spread of your catalogue is credited for the whole commitment.
Requires the `stores.resellers.view` permission.
# Get Bundles
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-bundles
GET /stores/{storeId}/reseller/bundles
Each comes back priced the way your resellers will see it: what it contains, what that comes to
at list price, the discount, and what they would pay. A bundle whose products have left your
reseller catalogue comes back unavailable, with the reason, rather than disappearing.
Requires the `stores.resellers.view` permission.
# Get Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-catalogue
GET /stores/{storeId}/reseller/catalogue
The variants resellers can draw, each with its list price and the price a reseller pays, so the
merchant can see the margin they are giving away.
Requires the `stores.resellers.view` permission.
# Get Invitations
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-invites
GET /stores/{storeId}/reseller/invites
Tokens are never included — they exist only in the response that created them.
Requires the `stores.resellers.view` permission.
# Get Leaderboard
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-leaderboard
GET /stores/{storeId}/reseller/leaderboard
Your resellers ranked by what they spent with you over a period, biggest first. Built for
publishing: every reseller is identified by the store they sell from — its name, description,
storefront and branding, all of which is public already — so nothing here has to be masked
before it goes on a page.
`from` is inclusive and `to` exclusive. Omit them for the last thirty days.
Spend is net of refunds, so a reseller who was refunded more than they spent in the period
does not appear.
A reseller who sells outside Komerza still ranks, with no store attached — leaving them out
would produce a leaderboard whose total disagreed with your own revenue.
Requires the `stores.resellers.view` permission.
# Get Questions
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-questions
GET /stores/{storeId}/reseller/application/questions
The store's reseller application form, in the order applicants are asked. Retired questions are
left out — they exist only so answers already given to them stay readable.
Requires the `stores.resellers.view` permission.
# Get Resellers
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-resellers
GET /stores/{storeId}/reseller/resellers
Who resells for this store, what they have spent, and what they can currently draw against.
Does not include the resellers' own customers — who they sell to is their business.
Requires the `stores.resellers.view` permission.
# Get Variant Stats
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-variant-stats
GET /stores/{storeId}/reseller/catalogue/{variantId}/stats
How many resellers list this variant and what they have drawn, so the reselling side of a product
is visible on the product page rather than only in the reseller section.
Requires the `stores.resellers.view` permission.
# Grant Samples
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/grant-samples
POST /stores/{storeId}/reseller/resellers/{resellerId}/samples
Gives a reseller free units so they can prove delivery works end to end before funding a
balance. Samples are spent before money on a matching draw.
Requires the `stores.resellers.update` permission.
# Remove from Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/remove-from-catalogue
DELETE /stores/{storeId}/reseller/catalogue/{variantId}
Removes it from the catalogue entirely, discarding its price overrides. Resellers already
listing it keep the listing, but their next draw fails. To pause it and keep the terms, set
`isEnabled` to false instead.
Requires the `stores.resellers.update` permission.
# Set Volume Tiers
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/set-volume-tiers
PUT /stores/{storeId}/reseller/catalogue/{variantId}/tiers
Applies only when a reseller buys stock up front, not to ordinary draws — drawing one unit at a
time commits them to nothing, so there is nothing for a discount to buy. Replaces the whole set,
so what you send is exactly what applies.
Requires the `stores.resellers.update` permission.
# Update Basket Discount
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-basket-tier
PUT /stores/{storeId}/reseller/basket-tiers/{basketTierId}
Requires the `stores.resellers.update` permission.
# Update Bundle
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-bundle
PUT /stores/{storeId}/reseller/bundles/{bundleId}
Replaces the bundle wholesale, lines and all. Anyone who already bought it keeps what they
bought at the price they paid.
Requires the `stores.resellers.update` permission.
# Update Question
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-question
PUT /stores/{storeId}/reseller/application/questions/{questionId}
Requires the `stores.resellers.update` permission.
# Update Status
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-status
PUT /stores/{storeId}/reseller/resellers/{resellerId}/status
Revoking stops further draws but leaves the reseller's balance alone — they funded it and are
still owed it.
Requires the `stores.resellers.update` permission.
# Accept Invitation
Source: https://docs.komerza.com/api-reference/endpoint/reselling/accept-invite
POST /stores/{storeId}/reselling/invites/accept
Redeems an invite on behalf of the store in the route, which `PlatformAuthorize` has already
proven you own. The relationship is approved outright — the supplier decided that when they sent
it — and a draft product is created for everything the invite named, so you land on a dashboard
with stock to sell rather than a setup guide.
Only redeemable by an account on the address the invite was sent to.
Requires the `stores.reselling.update` permission.
# Add Funds
Source: https://docs.komerza.com/api-reference/endpoint/reselling/add-funds
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/topup
Creates an order on the supplier's store that credits your balance with them once paid. Drawing
stock and buying it in bulk both spend that balance, so this is what makes either possible.
The account credited is the one this store's relationship is held under, not whoever is
signed in — a staff member topping up must not end up crediting themselves.
Requires the `stores.reselling.update` permission.
# Apply to Supplier
Source: https://docs.komerza.com/api-reference/endpoint/reselling/apply
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}
Asks to resell a store's stock. A customer account on that store is created from the caller's
platform email, so there is nothing to sign up for separately. Stores that enrol openly approve
immediately; stores that take applications leave this pending.
Requires the `stores.reselling.update` permission.
# Buy Basket
Source: https://docs.komerza.com/api-reference/endpoint/reselling/buy-basket
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/basket
The basket is priced once and each line is then bought at its share of that price, landing on
your own products as ordinary stock exactly as a single-variant purchase would. Short deliveries
are refunded the same way.
Supply an idempotency key. Lines carry it with their own index appended, so a retried basket
replays line for line rather than buying twice.
Requires the `stores.reselling.update` permission.
# Buy Stock
Source: https://docs.komerza.com/api-reference/endpoint/reselling/buy-stock
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/stock
Buys units up front, at whatever volume price the supplier offers for that quantity, and puts
them on your own product as ordinary stock. Nothing is held or pending: the units are yours, they
sell through your normal delivery, and nothing the supplier does afterwards takes them away.
If the supplier delivers fewer than were bought, the difference is refunded to your balance
rather than quietly kept.
Requires the `stores.reselling.update` permission.
# Create Listing
Source: https://docs.komerza.com/api-reference/endpoint/reselling/create-listing
PUT /stores/{storeId}/reselling/suppliers/{supplierStoreId}/listings/{sourceVariantId}
Points one of this store's variants at a supplier's variant. Selling it then draws a unit from
the supplier automatically — no delivery configuration of any kind is needed on this side.
Requires the `stores.reselling.update` permission.
# Delete Listing
Source: https://docs.komerza.com/api-reference/endpoint/reselling/delete-listing
DELETE /stores/{storeId}/reselling/suppliers/{supplierStoreId}/listings/{sourceVariantId}
The store's own product keeps existing and keeps selling; it just no longer draws from the
supplier, which for a purely resold product means it can no longer be delivered. Worth
unpublishing it at the same time.
Requires the `stores.reselling.update` permission.
# Find Supplier
Source: https://docs.komerza.com/api-reference/endpoint/reselling/find-supplier
GET /stores/{storeId}/reselling/suppliers/lookup
There is no supplier directory, so this is how you reach a store you already know of: paste its
address.
Only stores currently taking applications are found. One running an invite-only programme, or
none at all, comes back as not found — which store has a reseller programme is not something
this should let anyone map out.
Requires the `stores.reselling.view` permission.
# Get Bundles
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-bundles
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/bundles
Baskets the supplier composed and priced as one thing, each quoted for this store: what is in
it, what that comes to at their list prices, the discount, and what you would pay. Bundles aimed
at other resellers are not listed, and cannot be bought by guessing an id.
Requires the `stores.reselling.view` permission.
# Get Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-catalogue
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/catalogue
Priced as it will actually cost this store, with whichever of its own products is already
sourced from each.
Requires the `stores.reselling.view` permission.
# Get Draws
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-draws
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/draws
One entry per draw, newest first, each quoting the supplier's own order id so it can be chased
upstream.
Requires the `stores.reselling.view` permission.
# Get Linkable Accounts
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-linkable
GET /stores/{storeId}/reselling/linkable
Relationships created by signing up on a supplier's own storefront, before this store existed or
before you thought to use it. They have no store attached, so none of the reselling here applies
to them and the supplier's panel keeps pointing you back at a dashboard that cannot see them.
Matched on your confirmed email address.
Requires the `stores.reselling.view` permission.
# Get Payment Methods
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-payment-methods
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/payment-methods
The supplier's own payment methods, since topping up is paid to them rather than to Komerza.
Only readable by a store that actually holds a relationship with them — a merchant's enabled
gateways are not public information.
Requires the `stores.reselling.view` permission.
# Get Resold Products
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-products
GET /stores/{storeId}/reselling/products
Every product of this store drawn from a supplier, with its cost, margin, and whether it would
currently sell. A resold product can stop being deliverable without its owner touching anything —
the supplier withdraws it, revokes them, raises the price past their ceiling, or runs out — so
each carries the reason it is blocked, if it is.
Requires the `stores.reselling.view` permission.
# Get Suppliers
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-suppliers
GET /stores/{storeId}/reselling/suppliers
Every store this one draws stock from, with what it can currently spend at each.
Requires the `stores.reselling.view` permission.
# Link Account
Source: https://docs.komerza.com/api-reference/endpoint/reselling/link-account
POST /stores/{storeId}/reselling/linkable/{resellerId}
Attaches an off-platform reseller relationship to this store. From then on its draws are served
in-process — stock lands on your own variants, there is no delivery URL to hold, and the balance
already held with that supplier comes across untouched.
Requires the `stores.reselling.update` permission.
# Quote Basket
Source: https://docs.komerza.com/api-reference/endpoint/reselling/quote-basket
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/basket/quote
Give it a bundle id, or lines of your own, and it comes back with the same arithmetic the
purchase will use — including what those lines would have cost bought separately, so a discount
you already have is not counted twice in what you tell yourself you saved.
Requires the `stores.reselling.view` permission.
# Get Review Analytics
Source: https://docs.komerza.com/api-reference/endpoint/reviews/analytics
GET /stores/{storeId}/reviews/analytics
Gets the review analytics for a store.
Requires the `stores.view` permission.
# Get Reviews
Source: https://docs.komerza.com/api-reference/endpoint/reviews/get-list
GET /stores/{storeId}/reviews
Gets the current and previous reviews for a store.
Requires the `stores.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| --------------- | -------- | ---------------------------------------------------- |
| `rating` | integer | Filter by star rating (1-5) |
| `emailAddress` | string | Filter by customer email (case-insensitive contains) |
| `hasReply` | boolean | Filter reviews that have a merchant reply |
| `dateRangeFrom` | datetime | Filter reviews created on or after this date |
| `dateRangeTo` | datetime | Filter reviews created on or before this date |
**Example:**
```bash theme={null}
# 5-star reviews from last 30 days
GET /stores/{storeId}/reviews?filters=rating==5,dateRangeFrom>=2025-10-28
# Reviews without replies, sorted by newest first
GET /stores/{storeId}/reviews?filters=hasReply==false&sorts=-dateRangeFrom
```
# Reply to Review
Source: https://docs.komerza.com/api-reference/endpoint/reviews/reply
PUT /stores/{storeId}/reviews/{reviewId}/reply
Requires the `stores.update` permission.
Replies are **public**. Whatever you send here appears under the review on
your storefront, next to the customer's original words.
## How it works
A review holds **one** reply, stored on the review itself rather than as a thread. That shapes the whole endpoint:
* **`PUT` overwrites.** Call it again on the same review and you replace the previous reply - this is also how you edit one. There is no separate update endpoint.
* **There is no delete.** `reply` is required and must be at least 8 characters, so you cannot blank it out to remove a reply. Replace it with better wording instead.
## Request
```json theme={null}
{
"reply": "Sorry this landed late - your key was reissued the same day and we've since fixed the delay."
}
```
| Field | Rules |
| ------- | ------------------------------------------- |
| `reply` | Required. Between **8 and 4096** characters |
## Permission
Requires **`stores.update`**, not a review-specific scope. A key that can reply to reviews can also change other store settings, so scope keys with that in mind.
## Finding what to reply to
Use [Get Reviews](/api-reference/endpoint/reviews/get-list) with `hasReply==false` to fetch the ones still waiting, worst first:
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/reviews" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=hasReply==false,rating<3" \
--data-urlencode "sorts=-dateRangeFrom"
```
A reply is read by far more future buyers than by the reviewer. Answer the
question the next shopper is silently asking - what went wrong, and what you
did about it.
# Add Ticket Response
Source: https://docs.komerza.com/api-reference/endpoint/tickets/add
PUT /stores/{storeId}/tickets/{ticketId}
Adds a response/reply to the given ticket.
Requires the `stores.tickets.update` permission.
# Close Ticket
Source: https://docs.komerza.com/api-reference/endpoint/tickets/delete
DELETE /stores/{storeId}/tickets/{ticketId}
Closes the given ticket.
Requires the `stores.tickets.delete` permission.
**Closed and Resolved are final states.** Neither you nor the customer can
reopen a closed ticket - they have to open a new one, losing the thread's
context. Reply instead if the conversation is still live.
# Get Ticket
Source: https://docs.komerza.com/api-reference/endpoint/tickets/get
GET /stores/{storeId}/tickets/{ticketId}
Gets a ticket by the ID in the given store and returns it.
Requires the `stores.tickets.view` permission.
# Get Tickets
Source: https://docs.komerza.com/api-reference/endpoint/tickets/get-list
GET /stores/{storeId}/tickets
Gets the tickets in the store.
Requires the `stores.tickets.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| --------------- | -------- | -------------------------------------------------------------------------------- |
| `subject` | string | Search ticket subject (case-insensitive contains) |
| `status` | integer | Filter by ticket status. See [Ticket Status](/api-reference/enums#ticket-status) |
| `customerEmail` | string | Filter by customer email (case-insensitive contains) |
| `dateFrom` | datetime | Filter tickets created on or after this date |
| `dateTo` | datetime | Filter tickets created on or before this date |
**Example:**
```bash theme={null}
# Open tickets from last 7 days
GET /stores/{storeId}/tickets?filters=status==0,dateFrom>=2025-11-20
# Tickets by customer email
GET /stores/{storeId}/tickets?filters=customerEmail@=*user@example.com
```
# Remove File from Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/delete-file
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/file/{fileId}
Deletes the delivery file and its link.
Requires the `stores.products.update` permission.
# Create Volume Discount
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/create
POST /stores/{storeId}/products/{productId}/variants/{variantId}/discounts
Adds a new volume discount tier to the specified variant. Each tier defines a minimum quantity
and the discount to apply when that quantity is reached.
Requires the `stores.products.update` permission.
# Delete Volume Discount
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/delete
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/discounts/{discountId}
Removes the specified volume discount tier from the variant.
Requires the `stores.products.update` permission.
# Get Volume Discounts
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/get
GET /stores/{storeId}/products/{productId}/variants/{variantId}/discounts
Retrieves all volume discount tiers configured for the specified variant, ordered by quantity threshold.
Requires the `stores.products.update` permission.
# Replace Volume Discounts
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/replace
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/discounts
Replaces all existing volume discount tiers for the variant with a new set.
This is useful for bulk updates where you want to set all tiers at once.
Requires the `stores.products.update` permission.
# Update Volume Discount
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/update
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/discounts/{discountId}
Updates the quantity threshold, discount type, or discount value of an existing volume discount tier.
Requires the `stores.products.update` permission.
# Search License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/search-items
GET /stores/{storeId}/products/{productId}/variants/{variantId}/items/search/{query}
Searches for license keys in the specified store using fuzzy matching.
Results are ordered by relevance score, with exact matches appearing first,
followed by prefix matches, substring matches, and then similar keys.
Requires the `stores.products.view` permission.
# Create Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/create
POST /webhooks
Creates a webhook that POSTs the events you subscribe to, for the stores you choose, as signed
JSON. The signing secret is returned once here, so save it to verify the X-Signature header.
Requires the `user.webhooks.create` permission.
**The signing secret is returned once, here, and never again.** There is no
endpoint or dashboard screen that will show it later. Store it when you
receive it; if you lose it, your only option is to delete the webhook and
create a new one.
You can hold up to **25 webhooks per user**, each scoped to one or more of
your stores. URLs must be HTTP or HTTPS, and any that resolve to private or
reserved addresses are rejected.
# Get Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/get
GET /webhooks/{webhookId}
Retrieves the webhook if it belongs to the currently authenticated user.
Requires the `user.webhooks.view` permission.
# Get Webhooks
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/get-list
GET /webhooks
Returns a list of all account-wide webhooks configured by the user.
Requires the `user.webhooks.view` permission.
# Update Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/update
PUT /webhooks/{webhookId}
Overwrites the URL, events, and store IDs.
Requires the `user.webhooks.update` permission.
# Authentication
Source: https://docs.komerza.com/api-reference/authentication
API keys, required headers and scopes
Every endpoint requires an API key, sent as a bearer token.
```bash theme={null}
curl "https://api.komerza.com/stores/{storeId}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0"
```
## Creating a key
1. Open **[Account Settings → API Keys](https://dashboard.komerza.com/account?api)** in the dashboard
2. Click **Create New API Key** and name it after the integration that will use it
3. Select its scopes
4. Copy the key
The key is shown once and can't be retrieved later. Keep it in an environment
variable or a secrets manager, never in source control.
## Required headers
| Header | Required | Value |
| --------------- | --------- | ------------------------------------------------ |
| `Authorization` | Yes | `Bearer YOUR_API_KEY` |
| `User-Agent` | Yes | Something identifying your app, e.g. `MyApp/1.0` |
| `Content-Type` | On writes | `application/json` |
## Scopes
Each key carries a fixed set of scopes, chosen when you create it. They decide which endpoints it can reach: `stores.orders.view` to read orders, `stores.orders.refund` to refund them, and so on.
Scopes can't be edited afterwards. To change what a key can do, create a new one and retire the old.
Two habits worth adopting from the start:
* **One key per integration.** You can then revoke a single integration without breaking everything else.
* **Only the scopes it needs.** A reporting script has no business holding `stores.orders.refund`.
} href="/api-reference/scopes">
Every permission a key can carry, and what each one unlocks.
## When authentication fails
| Status | Meaning | Fix |
| ------ | --------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `401` | Key missing, malformed or revoked | Check the `Bearer ` prefix is present and the key still exists |
| `403` | Key is valid but lacks the scope | Scopes follow the resource, e.g. orders need `stores.orders.*`. Create a new key with the right ones |
Both return the standard error envelope, with `code` set to `AccessDenied`. See [Responses & Errors](/api-reference/responses).
# Conventions
Source: https://docs.komerza.com/api-reference/conventions
IDs, dates, enums, money, store scoping and paging
These hold across every endpoint. Knowing them up front saves a lot of trial and error.
## Base URL
```
https://api.komerza.com
```
There is **no `/api` prefix**. Paths start straight at the resource:
```bash theme={null}
https://api.komerza.com/stores/{storeId}/orders # correct
https://api.komerza.com/api/stores/{storeId}/orders # 404
```
## Store scoping
Nearly every path begins `/stores/{storeId}/…`, because almost everything belongs to a store rather than to your account. `GET /user` returns your account with its stores, and each store's `id` is the `{storeId}` used everywhere else.
An account can hold several stores, so treat the store ID as a required piece of configuration in your integration, not something to hardcode once and forget.
## IDs
UUIDs, in the standard hyphenated form:
```
550e8400-e29b-41d4-a716-446655440000
```
Stores, products, variants, orders, customers and coupons all use them. They're opaque - don't parse or generate them.
## Dates
ISO 8601, in UTC:
```
2026-01-15T10:30:00Z
```
Filters accept either a full timestamp or just the date, e.g. `dateFrom>=2026-01-15`.
## Enums
Sent and returned as **integers**, not names. A product's visibility is `0`, not `"Public"`.
```json theme={null}
{ "privacy": 0, "stockCalculationMode": 2 }
```
} href="/api-reference/enums">
Every enum value the API accepts and returns.
Order status and payment gateway are the exceptions - both are strings, documented on [Order Status](/api-reference/payment-status) and [Payment Methods](/api-reference/payment-methods).
## Money
Decimal values in the store's own currency, with `currencyCode` on the store telling you which. There's no minor-unit convention to decode: `29.99` means 29.99.
Amounts a buyer paid can differ from the order total on split payments, and crypto orders carry the amount actually received - check both rather than assuming.
## Paging
| Parameter | Default | Notes |
| ---------- | ------- | ---------------------------------------------------- |
| `page` | `1` | 1-indexed |
| `pageSize` | `20` | Maximum 100; larger values are clamped, not rejected |
List endpoints also take `filters` and `sorts`.
} href="/api-reference/filtering">
Operators, combining conditions, sorting and paging.
## Fair use
Requests are rate limited. On a `429`, back off and retry with increasing delays, and cache anything that doesn't change often.
If you're polling for order updates, use [webhooks](/guides/webhooks) instead. They're signed, logged and retried, and they'll reach you sooner than any poll interval you'd be comfortable running.
# Create Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/create
POST /stores/{storeId}/categories
Creates a new category in the store.
Requires the `stores.categories.create` permission.
# Delete Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/delete
DELETE /stores/{storeId}/categories/{categoryId}
Deletes an existing category from the store.
Requires the `stores.categories.delete` permission.
# Get Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/get
GET /stores/{storeId}/categories/{categoryId}
Retrieves a category by its ID with full product details.
Requires the `stores.categories.view` permission.
# Get All Categories
Source: https://docs.komerza.com/api-reference/endpoint/categories/get-all
GET /stores/{storeId}/categories/all
Retrieves all categories in the store as references (no pagination).
Requires the `stores.categories.view` permission.
# Get Categories
Source: https://docs.komerza.com/api-reference/endpoint/categories/get-list
GET /stores/{storeId}/categories
Gets the categories in the store with pagination and filtering.
Requires the `stores.categories.view` permission.
# Order Categories
Source: https://docs.komerza.com/api-reference/endpoint/categories/order
PATCH /stores/{storeId}/categories/order
Updates the display order of categories.
Requires the `stores.categories.update` permission.
# Order Products in Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/order-products
PATCH /stores/{storeId}/categories/{categoryId}/products/order
Updates the display order of products within a category.
Requires the `stores.categories.update` permission.
# Update Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/update
PUT /stores/{storeId}/categories/{categoryId}
Updates an existing category in the store.
Requires the `stores.categories.update` permission.
# Create Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/create
POST /stores/{storeId}/coupons
Creates a new coupon in the store.
Requires the `stores.coupons.create` permission.
# Delete Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/delete
DELETE /stores/{storeId}/coupons/{couponId}
Deletes an existing coupon from the store.
Requires the `stores.coupons.delete` permission.
# Get Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/get
GET /stores/{storeId}/coupons/{couponId}
Gets a coupon by ID.
Requires the `stores.coupons.view` permission.
# Get All Coupons
Source: https://docs.komerza.com/api-reference/endpoint/coupons/get-all
GET /stores/{storeId}/coupons/all
Gets the coupons in the store as references.
Requires the `stores.coupons.view` permission.
# Get Coupons
Source: https://docs.komerza.com/api-reference/endpoint/coupons/get-list
GET /stores/{storeId}/coupons
Gets the coupons in the store.
Requires the `stores.coupons.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------ |
| `usesFrom` | integer | Filter by minimum number of times coupon has been used |
| `usesTo` | integer | Filter by maximum number of times coupon has been used |
| `maximumUsesFrom` | integer | Filter by minimum maximum uses allowed |
| `maximumUsesTo` | integer | Filter by maximum maximum uses allowed |
| `percentageFrom` | decimal | Filter by minimum percentage discount |
| `percentageTo` | decimal | Filter by maximum percentage discount |
| `amountFrom` | decimal | Filter by minimum amount discount |
| `amountTo` | decimal | Filter by maximum amount discount |
| `isEnabled` | boolean | Filter by whether coupon is enabled |
**Example:**
```bash theme={null}
# Active coupons with 20-50% discount
GET /stores/{storeId}/coupons?filters=isEnabled==true,percentageFrom>=20,percentageTo<=50
# Heavily used coupons
GET /stores/{storeId}/coupons?filters=usesFrom>=100&sorts=-uses
```
# Search Coupons
Source: https://docs.komerza.com/api-reference/endpoint/coupons/search
GET /stores/{storeId}/coupons/search/{query}
Searches the coupons in the store by the given query.
Requires the `stores.coupons.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------ |
| `usesFrom` | integer | Filter by minimum number of times coupon has been used |
| `usesTo` | integer | Filter by maximum number of times coupon has been used |
| `maximumUsesFrom` | integer | Filter by minimum maximum uses allowed |
| `maximumUsesTo` | integer | Filter by maximum maximum uses allowed |
| `percentageFrom` | decimal | Filter by minimum percentage discount |
| `percentageTo` | decimal | Filter by maximum percentage discount |
| `amountFrom` | decimal | Filter by minimum amount discount |
| `amountTo` | decimal | Filter by maximum amount discount |
| `isEnabled` | boolean | Filter by whether coupon is enabled |
**Example:**
```bash theme={null}
# Active coupons with 20-50% discount
GET /stores/{storeId}/coupons?filters=isEnabled==true,percentageFrom>=20,percentageTo<=50
# Heavily used coupons
GET /stores/{storeId}/coupons?filters=usesFrom>=100&sorts=-uses
```
# Update Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/update
PUT /stores/{storeId}/coupons/{couponId}
Requires the `stores.coupons.update` permission.
# Create Product
Source: https://docs.komerza.com/api-reference/endpoint/products/create
POST /stores/{storeId}/products
Creates a new product in the store.
Requires the `stores.products.create` permission.
# Delete Product
Source: https://docs.komerza.com/api-reference/endpoint/products/delete
DELETE /stores/{storeId}/products/{productId}
Deletes a product from the store.
Requires the `stores.products.delete` permission.
**Deleting a product takes its reviews with it.** Variants, custom fields,
volume discounts and images go too, and none of it can be restored.
Files already delivered to customers are kept, so past buyers keep their
downloads. If you only want a product off the storefront, set its privacy to
Private or On Hold instead of deleting it.
# Bulk Delete Products
Source: https://docs.komerza.com/api-reference/endpoint/products/delete-bulk
DELETE /stores/{storeId}/products
Deletes multiple products in one request. This will delete all variants, files, images, and reviews
associated with each product. Products that don't exist are silently skipped.
Requires the `stores.products.delete` permission.
**Irreversible, across every product in the request.** Each one loses its
variants, custom fields, volume discounts, images and reviews.
Files already delivered to customers are kept. To hide products rather than
destroy them, bulk-update their privacy instead.
# Duplicate Product
Source: https://docs.komerza.com/api-reference/endpoint/products/duplicate
POST /stores/{storeId}/products/{productId}/duplicate
Creates a copy of an existing product.
Requires the `stores.products.create` permission.
# Get Product
Source: https://docs.komerza.com/api-reference/endpoint/products/get
GET /stores/{storeId}/products/{productId}
Retrieves a product by its ID.
Requires the `stores.products.view` permission.
# Get All Products
Source: https://docs.komerza.com/api-reference/endpoint/products/get-all
GET /stores/{storeId}/products/all
Retrieves all products in the store as redacted references (no pagination).
Requires the `stores.products.view` permission.
# Get Products
Source: https://docs.komerza.com/api-reference/endpoint/products/get-list
GET /stores/{storeId}/products
Retrieves products in pages.
Requires the `stores.products.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------------------------------------ |
| `visibility` | integer | Filter by privacy level. See [Product Privacy](/api-reference/enums#product-privacy) |
| `isBestSeller` | boolean | Filter best-seller products |
| `shouldBlockVpns` | boolean | Filter products that block VPNs |
| `lowStock` | boolean | Filter products with low stock availability |
**Example:**
```bash theme={null}
# Public best-sellers with low stock
GET /stores/{storeId}/products?filters=visibility==0,isBestSeller==true,lowStock==true
# Products that don't block VPNs
GET /stores/{storeId}/products?filters=shouldBlockVpns==false
```
# Get Product by Slug
Source: https://docs.komerza.com/api-reference/endpoint/products/get-slug
GET /stores/{storeId}/products/slug/{slug}
Retrieves a product by its slug.
Requires the `stores.products.view` permission.
# Order Products
Source: https://docs.komerza.com/api-reference/endpoint/products/order
PATCH /stores/{storeId}/products/order
Updates the display order of products.
Requires the `stores.products.update` permission.
# Order Product Images
Source: https://docs.komerza.com/api-reference/endpoint/products/order-images
PATCH /stores/{storeId}/products/{productId}/images/order
Updates the display order of images for a product.
Requires the `stores.products.update` permission.
# Search Products
Source: https://docs.komerza.com/api-reference/endpoint/products/search
GET /stores/{storeId}/products/search/{query}
Searches products by query within the store.
Requires the `stores.products.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------------------------------------ |
| `visibility` | integer | Filter by privacy level. See [Product Privacy](/api-reference/enums#product-privacy) |
| `isBestSeller` | boolean | Filter best-seller products |
| `shouldBlockVpns` | boolean | Filter products that block VPNs |
| `lowStock` | boolean | Filter products with low stock availability |
**Example:**
```bash theme={null}
# Public best-sellers with low stock
GET /stores/{storeId}/products?filters=visibility==0,isBestSeller==true,lowStock==true
# Products that don't block VPNs
GET /stores/{storeId}/products?filters=shouldBlockVpns==false
```
# Update Product
Source: https://docs.komerza.com/api-reference/endpoint/products/update
PUT /stores/{storeId}/products/{productId}
Updates basic information of a product.
Requires the `stores.products.update` permission.
# Update Affiliate Discount
Source: https://docs.komerza.com/api-reference/endpoint/products/update-affiliate-discount
PUT /stores/{storeId}/products/{productId}/affiliateDiscount
Updates affiliate discount configuration of a product.
Requires the `stores.products.update` permission.
# Update Product Badges
Source: https://docs.komerza.com/api-reference/endpoint/products/update-badges
PUT /stores/{storeId}/products/{productId}/badges
Replaces every badge on the product at once (bulk set). An empty list clears all badges.
Requires the `stores.products.update` permission.
# Bulk Update Products
Source: https://docs.komerza.com/api-reference/endpoint/products/update-bulk
PUT /stores/{storeId}/products
Updates multiple products in one request.
Requires the `stores.products.update` permission.
# Update Payment Settings
Source: https://docs.komerza.com/api-reference/endpoint/products/update-payment
PUT /stores/{storeId}/products/{productId}/payment
Sets a custom fee structure for a product.
Requires the `stores.products.update` permission.
# Update Product Security
Source: https://docs.komerza.com/api-reference/endpoint/products/update-security
PUT /stores/{storeId}/products/{productId}/security
Updates security information of a product.
Requires the `stores.products.update` permission.
# Create Store
Source: https://docs.komerza.com/api-reference/endpoint/stores/create
POST /stores
Creates a new store for the current user.
Requires the `stores.create` permission.
# Get Store
Source: https://docs.komerza.com/api-reference/endpoint/stores/get
GET /stores/{storeId}
Retrieves the specified store's reference information.
Requires the `stores.view` permission.
# Get Permissions
Source: https://docs.komerza.com/api-reference/endpoint/stores/get-permissions
GET /stores/{storeId}/permissions
Gets the permissions that the currently authenticated user has against the store, along with the payment methods
enabled by the store owner
Requires the `stores.view` permission.
This method can return permissions (otherwise known as scopes), you can learn
more about scopes by clicking here.
# Update Store
Source: https://docs.komerza.com/api-reference/endpoint/stores/update
PUT /stores/{storeId}
Replaces the specified store's data with the information in the form.
Requires the `stores.update` permission.
# Maintenance Mode
Source: https://docs.komerza.com/api-reference/endpoint/stores/update-maintenance
PUT /stores/{storeId}/maintenance
Enables or disables maintenance mode for the specified store.
Requires the `stores.update` permission.
**This takes your storefront offline for shoppers.** They see your message
instead of the store and cannot buy anything until it is switched back off.
## The reason is the switch
There is no `enabled` flag - maintenance is on or off depending on whether `reason` has a value.
| Send | Result |
| --------------------------------- | ---------------------------------------- |
| `{ "reason": "Back at 3pm UTC" }` | Maintenance **on**, showing that message |
| `{ "reason": null }` | Maintenance **off**, store live again |
```bash theme={null}
curl -X PUT "https://api.komerza.com/stores/{storeId}/maintenance" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
-H "Content-Type: application/json" \
-d '{"reason": "Restocking, back at 3pm UTC"}'
```
## Message rules
Up to **1024 characters**, and only letters, digits, spaces and `. , ! ? ' -`.
Colons and slashes are rejected, so **a URL will fail validation** - as will
emoji, `@`, `#`, `&` and brackets.
The message is public, so write it for shoppers.
## Reading the current state
The store object returns `maintenanceReason`; `null` means live.
If the storefront is dark while `maintenanceReason` is `null`, this endpoint
won't fix it - that's an expired plan, an unpaid Buyer Protection invoice, or
a suspended account.
# Get Ticket
Source: https://docs.komerza.com/api-reference/endpoint/tickets/get
GET /stores/{storeId}/tickets/{ticketId}
Gets a ticket by the ID in the given store and returns it.
Requires the `stores.tickets.view` permission.
# Get User
Source: https://docs.komerza.com/api-reference/endpoint/users/get
GET /user
Returns the currently authenticated user’s profile data.
Requires the `user.view` permission.
# Add License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/add-items
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/items
Appends delivery items to a variant.
Requires the `stores.products.update` permission.
On a variant using **Calculated** stock, the number of keys loaded here *is*
the stock level - adding keys is how you restock, and an Undeliverable order
can be retried with [Deliver](/api-reference/endpoint/orders/deliver) once
stock is back.
# Clear License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/clear-items
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/items/clear
Removes all items from a variant.
Requires the `stores.products.update` permission.
**This wipes every license key on the variant, permanently.** There is no undo
and no way to recover the keys afterwards - keep your own copy before calling
it.
On a variant using Calculated stock, this also drops its stock to zero, so the
product stops selling immediately.
# Create Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/create
POST /stores/{storeId}/products/{productId}/variants
Adds a new variant to the specified product.
Requires the `stores.products.update` permission.
# Delete Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/delete
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}
Removes the specified variant.
Requires the `stores.products.update` permission.
**Deletes the variant's license keys, custom fields and volume discounts with
it,** and cannot be undone. Any unsold keys loaded on it are gone.
# Remove File from Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/delete-file
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/file/{fileId}
Deletes the delivery file and its link.
Requires the `stores.products.update` permission.
# Delete License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/delete-items
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/items
Deletes specified items from a variant.
Requires the `stores.products.update` permission.
**Removed keys are gone for good** - they leave your inventory and cannot be
restored. On Calculated stock, removing keys lowers the sellable stock
straight away.
# Create Volume Discount
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/create
POST /stores/{storeId}/products/{productId}/variants/{variantId}/discounts
Adds a new volume discount tier to the specified variant. Each tier defines a minimum quantity
and the discount to apply when that quantity is reached.
Requires the `stores.products.update` permission.
# Delete Volume Discount
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/delete
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/discounts/{discountId}
Removes the specified volume discount tier from the variant.
Requires the `stores.products.update` permission.
# Get Volume Discounts
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/get
GET /stores/{storeId}/products/{productId}/variants/{variantId}/discounts
Retrieves all volume discount tiers configured for the specified variant, ordered by quantity threshold.
Requires the `stores.products.update` permission.
# Replace Volume Discounts
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/replace
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/discounts
Replaces all existing volume discount tiers for the variant with a new set.
This is useful for bulk updates where you want to set all tiers at once.
Requires the `stores.products.update` permission.
# Update Volume Discount
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/update
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/discounts/{discountId}
Updates the quantity threshold, discount type, or discount value of an existing volume discount tier.
Requires the `stores.products.update` permission.
# Duplicate Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/duplicate
POST /stores/{storeId}/products/{productId}/variants/{variantId}/duplicate
Creates a copy of an existing variant in the same product.
Requires the `stores.products.update` permission.
# Create Field
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/create
POST /stores/{storeId}/products/{productId}/variants/{variantId}/fields
Adds a new custom field to the specified variant. Options for select/radio fields are managed separately.
Requires the `stores.products.update` permission.
# Delete Field
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/delete
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}
Removes the specified custom field and all its options from the variant.
Requires the `stores.products.update` permission.
# Create Field Option
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/options/create
POST /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}/options
Adds a selectable option under the given custom field.
Requires the `stores.products.update` permission.
# Delete Field Option
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/options/delete
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}/options/{optionId}
Removes the specified option from its parent custom field.
Requires the `stores.products.update` permission.
# Update Field Option
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/options/update
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}/options/{optionId}
Updates the label, value, or display order of an existing option.
Requires the `stores.products.update` permission.
# Update Field
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/update
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}
Updates the label, placeholder, type, requirement, or hint of an existing custom field.
Requires the `stores.products.update` permission.
# Get Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/get
GET /stores/{storeId}/products/{productId}/variants/{variantId}
Retrieves a single variant by ID.
Requires the `stores.products.view` permission.
# Get License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/get-items
GET /stores/{storeId}/products/{productId}/variants/{variantId}/items/{page}
Retrieves files/items delivered by a variant in pages.
Requires the `stores.products.view` permission.
# Order Variants
Source: https://docs.komerza.com/api-reference/endpoint/variants/order
PATCH /stores/{storeId}/products/{productId}/variants/order
Updates the display order of variants.
Requires the `stores.products.update` permission.
# Order Variant Images
Source: https://docs.komerza.com/api-reference/endpoint/variants/order-images
PATCH /stores/{storeId}/products/{productId}/variants/{variantId}/images/order
Sets the display order of images.
Requires the `stores.products.update` permission.
# Search License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/search-items
GET /stores/{storeId}/products/{productId}/variants/{variantId}/items/search/{query}
Searches for license keys in the specified store using fuzzy matching.
Results are ordered by relevance score, with exact matches appearing first,
followed by prefix matches, substring matches, and then similar keys.
Requires the `stores.products.view` permission.
# Update Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/update
PUT /stores/{storeId}/products/{productId}/variants/{variantId}
Modifies a variant’s label, SKU, price, etc.
Requires the `stores.products.update` permission.
# Update Delivery Settings
Source: https://docs.komerza.com/api-reference/endpoint/variants/update-delivery
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/delivery
Configures how the variant is delivered (e.g., file, custom script).
Requires the `stores.products.update` permission.
# Delete Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/delete
DELETE /webhooks/{webhookId}
Removes the webhook and all associated logs from the user account.
Requires the `user.webhooks.delete` permission.
**Deleting a webhook deletes all of its execution logs**, so any delivery
history you would need for debugging goes with it. Export anything you want to
keep first.
# Get Webhook Logs
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/get-logs
GET /webhooks/{webhookId}/logs
Returns a webhook's delivery history: one entry per attempt, with the response code and, on
failures, the reason.
Supports filtering by response code and success/failure status via query parameters.
Requires the `user.webhooks.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| -------------- | ------- | --------------------------------------------------------------------------------- |
| `success` | boolean | Whether the delivery succeeded (a 2xx response) |
| `responseCode` | integer | The HTTP status your endpoint returned. `-1` means paused after repeated failures |
**Example:**
```bash theme={null}
# Failed deliveries only
GET /webhooks/{webhookId}/logs?filters=success==false
# Deliveries your endpoint rejected as unauthorised
GET /webhooks/{webhookId}/logs?filters=responseCode==401
```
# Resend Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/resend
POST /webhooks/{webhookId}/logs/{executionLogId}/resend
Re-queues the Hangfire job for a particular webhook execution log. If the job no longer exists or the user
does not own the log, it fails. This is a single execution log re-send.
Requires the `user.webhooks.update` permission.
# Search Webhook Logs
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/search-logs
GET /webhooks/{webhookId}/logs/search/{query}
Searches logs by log ID, order ID (if the query is a valid GUID), or by URL fragment.
All pagination and date/status filters from the listing endpoint are also supported.
Requires the `user.webhooks.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| -------------- | ------- | --------------------------------------------------------------------------------- |
| `success` | boolean | Whether the delivery succeeded (a 2xx response) |
| `responseCode` | integer | The HTTP status your endpoint returned. `-1` means paused after repeated failures |
**Example:**
```bash theme={null}
# Failed deliveries only
GET /webhooks/{webhookId}/logs?filters=success==false
# Deliveries your endpoint rejected as unauthorised
GET /webhooks/{webhookId}/logs?filters=responseCode==401
```
# Enums & Constants
Source: https://docs.komerza.com/api-reference/enums
Every enum value the API accepts and returns
Most enums are **integers** - send and expect the numeric value, not the name. The names below are for readability only; they never appear in a payload. String enums are marked as such.
Order statuses have their own page: [Order Status](/api-reference/payment-status).
## Products
### Privacy
Controls where a product appears and whether it can be bought.
| Value | Name | Meaning |
| ----- | -------- | ------------------------------------------------------ |
| `0` | Public | Visible on the store page and purchasable |
| `1` | Unlisted | Hidden from the store page, purchasable via direct URL |
| `2` | Private | Hidden entirely, not purchasable |
| `3` | OnHold | Visible but blocked from purchase |
### Product type
| Value | Name | Meaning |
| ----- | ------------ | --------------------------------------------- |
| `0` | OneTime | A single purchase |
| `1` | Subscription | Creates or extends a subscription on delivery |
### Billing interval
Applies to subscription variants.
| Value | Name |
| ----- | ----------------------------- |
| `0` | Weekly |
| `1` | Monthly |
| `2` | Quarterly |
| `3` | Yearly |
| `4` | Custom (interval set in days) |
### Stock calculation mode
| Value | Name | Meaning |
| ----- | ---------- | --------------------------------------------------------------- |
| `0` | Calculated | Stock equals the number of delivery items loaded on the variant |
| `1` | Ignored | Stock is not tracked; never out of stock |
| `2` | Fixed | A number you set and maintain yourself |
### Volume discount type
| Value | Name | Meaning |
| ----- | ----------- | ------------------------ |
| `0` | Percentage | A percentage off |
| `1` | FixedAmount | An amount off per unit |
| `2` | FixedPrice | A replacement unit price |
### Affiliate discount override
Set per product to override the store's default affiliate discount.
| Value | Name | Meaning |
| ----- | ---------- | ---------------------------------------------------------- |
| `0` | UseDefault | Use the store's default discount |
| `1` | Disabled | No customer discount; the affiliate still earns commission |
| `2` | Custom | Use the percentage set on the product |
### Delivery types
**String values**, supplied as an array on a variant. A variant can combine several.
| Value | Delivers |
| --------------- | ---------------------------------------------------------------------- |
| `license_keys` | Keys drawn from the variant's inventory |
| `files` | The uploaded file, via a time-limited download link |
| `dynamic` | Whatever your endpoint returns at delivery time |
| `instructions` | A static message included in the delivery email |
| `custom_fields` | Not a delivery mechanism - collects answers from the buyer at checkout |
`keyauth` still appears in some request descriptions. It was removed from the
product and no longer works - do not send it.
## Orders
### Refund reason
| Value | Name |
| ----- | ------------------- |
| `0` | Fraudulent |
| `1` | RequestedByCustomer |
| `2` | Duplicate |
| `3` | Other |
### Refund destination
| Value | Name | Meaning |
| ----- | --------------- | ----------------------------------- |
| `0` | OriginalMethod | Reverse through the payment gateway |
| `1` | CustomerBalance | Issue store credit instead |
Several payment methods only support `1` - see [Managing Orders](/guides/orders#refunds).
### Custom payment method type
| Value | Name | Meaning |
| ----- | ------------ | -------------------------------------------------- |
| `0` | Instructions | Show the buyer instructions to pay outside Komerza |
| `1` | Redirect | Send the buyer to a URL you specify |
## Subscriptions
### Subscription status
| Value | Name | Meaning |
| ----- | -------- | ------------------------------------------------------ |
| `0` | Active | Paid and current |
| `1` | PastDue | An automatic charge failed; retries are scheduled |
| `2` | Canceled | Ended by the merchant or the customer |
| `3` | Expired | Retries exhausted, or the period ended without renewal |
| `4` | Paused | Resumes automatically at its resume date |
### Cancel mode
| Value | Name | Meaning |
| ----- | ----------- | -------------------------------------------- |
| `0` | AtPeriodEnd | Access continues until the period ends |
| `1` | Immediate | Access is revoked at once, with no proration |
## Discord
### Join mode
| Value | Name | Meaning |
| ----- | -------------------------- | ------------------------------------------------------ |
| `0` | AddAndGrantAfterPayment | Added to the server and granted the role after payment |
| `1` | AddOnLinkGrantAfterPayment | Added when they link Discord; role waits for payment |
### Requirement
| Value | Name | Meaning |
| ----- | -------- | ---------------------------------------- |
| `0` | Required | Cannot check out without linking Discord |
| `1` | Optional | Offered but not enforced |
| `2` | Disabled | Not shown at checkout |
### Revocation behavior
Applied when an order is refunded or charged back.
| Value | Name | Meaning |
| ----- | --------------- | -------------------------------- |
| `0` | RevokeAndRemove | Remove the role and the member |
| `1` | RevokeRoleOnly | Remove the role, keep the member |
| `2` | DoNothing | Leave access in place |
## Customers & support
### Ticket status
| Value | Name | Meaning |
| ----- | ----------------- | ------------------------------------- |
| `0` | Opened | Awaiting the seller |
| `1` | Resolved | Closed, issue sorted. Final |
| `2` | Closed | Closed without resolution. Final |
| `3` | Responded | Seller replied; awaiting the customer |
| `4` | CustomerResponded | Customer replied; awaiting the seller |
The values are not in flow order: a ticket moves `0` → `3` ⇄ `4` → `1` or `2`.
### Customer segment
Used to target email marketing recipients.
| Value | Name |
| ----- | ---------- |
| `0` | All |
| `1` | New |
| `2` | Active |
| `3` | Regulars |
| `4` | Vip |
| `5` | Affiliates |
### Blacklist type
The kind of value a blacklist rule matches on.
| Value | Name |
| ----- | ----------------------- |
| `0` | EmailAddress |
| `1` | EmailDomain |
| `2` | IpAddress |
| `3` | IpCidr |
| `4` | AutonomousSystemNumber |
| `5` | InternetServiceProvider |
| `6` | CountryCode |
| `7` | City |
## Email marketing
### Campaign status
| Value | Name | Meaning |
| ----- | ------------ | --------------------------------------------------- |
| `0` | Draft | Editable, not queued |
| `1` | Scheduled | Queued for a future time; still cancellable |
| `2` | Sending | In progress; cannot be cancelled from the dashboard |
| `3` | Sent | Finished |
| `4` | Cancelled | Cancelled before sending |
| `5` | Failed | The reason is not exposed; contact support |
| `6` | Under review | Held by automated screening before sending |
### Recipient send status
Per-recipient outcome within a campaign.
| Value | Name |
| ----- | ---------- |
| `0` | Queued |
| `1` | Sent |
| `2` | Delivered |
| `3` | Bounced |
| `4` | Complained |
| `5` | Failed |
### Suppression type
| Value | Name | Meaning |
| ----- | ----------- | ------------------------- |
| `0` | Unsubscribe | Removable by the merchant |
| `1` | HardBounce | Permanent |
| `2` | Complaint | Permanent |
## Storefront
### Discount dialog trigger
| Value | Name | Meaning |
| ----- | ---------- | ------------------------------------- |
| `0` | Entrance | Shown when the visitor arrives |
| `1` | ExitIntent | Shown when the visitor moves to leave |
Enums used only by dashboard-internal endpoints - platform billing, passkey
registration, account standing - are deliberately not listed. They are not
part of the public API and can change without notice.
# Error Codes
Source: https://docs.komerza.com/api-reference/error-codes
Complete reference for all API error codes and how to handle them
Every failed request carries a `code`: a stable, machine-readable identifier for what went wrong. This page lists them all.
```json theme={null}
{
"success": false,
"message": "The request contains invalid parameters",
"code": "ValidationError",
"data": null
}
```
For the response envelope itself, validation errors and HTTP status codes, see
[Responses & Errors](/api-reference/responses).
## Error Codes Reference
### Authentication & Authorization
**HTTP Status:** 403 Forbidden
**Description:** You don't have permission to access this resource or perform this action.
**Common causes:**
* API key lacks required scope
* Trying to access another user's resources
* Account restrictions or suspensions
* Store-level permissions insufficient
**How to fix:**
* Check your API key has the necessary [scopes](/api-reference/scopes)
* Verify you're accessing resources you own
* Check account status in dashboard
* Request additional permissions if needed
**Example:**
```json theme={null}
{
"success": false,
"message": "Your API key does not have permission to update products",
"code": "AccessDenied",
"data": null
}
```
**HTTP Status:** 401 Unauthorized
**Description:** The provided authentication token is invalid, expired, or malformed.
**Common causes:**
* Expired API key
* Revoked API key
* Malformed Authorization header
* API key not found
**How to fix:**
* Generate a new API key from dashboard
* Verify Authorization header format: `Bearer YOUR_API_KEY`
* Check for whitespace or special characters in token
* Ensure API key hasn't been revoked
**Example:**
```json theme={null}
{
"success": false,
"message": "The provided API key is invalid or has expired",
"code": "BadToken",
"data": null
}
```
**HTTP Status:** 403 Forbidden
**Description:** Two-factor authentication is required for this action.
**Common causes:**
* Sensitive operation requires 2FA
* Account security policy enforced
* Administrative action attempted
**How to fix:**
* Complete 2FA challenge
* Enable 2FA on your account
* Use session-based authentication for sensitive operations
**Example:**
```json theme={null}
{
"success": false,
"message": "This operation requires two-factor authentication",
"code": "TwoFactorRequired",
"data": null
}
```
**HTTP Status:** 401 Unauthorized
**Description:** OAuth authentication is required for this endpoint.
**Common causes:**
* Endpoint requires user session, not API key
* OAuth flow not completed
* Session expired
**How to fix:**
* Complete OAuth authentication flow
* Use correct authentication method for endpoint
* Check if endpoint supports API key authentication
**Example:**
```json theme={null}
{
"success": false,
"message": "This endpoint requires OAuth authentication",
"code": "OAuthLoginRequired",
"data": null
}
```
### Validation & Input Errors
**HTTP Status:** 400 Bad Request
**Description:** The request contains invalid or missing parameters.
**Common causes:**
* Missing required fields
* Invalid data types
* Values outside allowed ranges
* Invalid format (email, URL, UUID, etc.)
* Business rule violations
**How to fix:**
* Check request body against API documentation
* Validate data types and formats
* Ensure all required fields are present
* Review error details for specific field errors
**Example:**
```json theme={null}
{
"success": false,
"message": "Price must be greater than 0",
"code": "ValidationError",
"data": null
}
```
**HTTP Status:** 400 Bad Request
**Description:** CAPTCHA verification failed.
**Common causes:**
* Invalid CAPTCHA response
* Expired CAPTCHA token
* CAPTCHA not solved
* Bot detection triggered
**How to fix:**
* Request new CAPTCHA challenge
* Ensure user completes CAPTCHA
* Check CAPTCHA token hasn't expired
* Verify CAPTCHA integration is correct
**Example:**
```json theme={null}
{
"success": false,
"message": "CAPTCHA verification failed. Please try again.",
"code": "BadCaptcha",
"data": null
}
```
### Resource Errors
**HTTP Status:** 404 Not Found
**Description:** The requested resource does not exist.
**Common causes:**
* Invalid resource ID
* Resource was deleted
* Typo in endpoint URL
* Resource belongs to different store
**How to fix:**
* Verify resource ID is correct
* Check resource hasn't been deleted
* Ensure you're querying the right store
* Validate endpoint URL
**Example:**
```json theme={null}
{
"success": false,
"message": "Product not found",
"code": "NotFound",
"data": null
}
```
**HTTP Status:** 409 Conflict
**Description:** The operation conflicts with an existing resource.
**Common causes:**
* Duplicate unique field (email, slug, etc.)
* Resource already exists
* Concurrent modification conflict
* Business rule prevents operation
**How to fix:**
* Use unique values for unique fields
* Check if resource already exists
* Implement optimistic locking for concurrent updates
* Review business rules
**Example:**
```json theme={null}
{
"success": false,
"message": "A product with this slug already exists",
"code": "ObjectConflict",
"data": null
}
```
### Rate Limiting
**HTTP Status:** 429 Too Many Requests
**Description:** You have exceeded the API rate limit.
**Common causes:**
* Too many requests in short time period
* Burst limit exceeded
* Account-level rate limit reached
**How to fix:**
* Implement exponential backoff
* Respect `Retry-After` header
* Cache frequently accessed data
* Optimize API calls to reduce frequency
* Contact support for higher limits
**Response headers:**
```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1638360000
Retry-After: 60
```
**Example:**
```json theme={null}
{
"success": false,
"message": "Rate limit exceeded. Please retry after 60 seconds.",
"code": "RateLimited",
"data": null
}
```
### Business Logic Errors
**HTTP Status:** 400 Bad Request
**Description:** The requested product is not available for purchase.
**Common causes:**
* Product out of stock
* Product is private or unlisted
* Product deleted or disabled
* Product not available in customer's region
* Purchase limits exceeded
**How to fix:**
* Check product availability
* Verify product stock levels
* Ensure product is public
* Check regional restrictions
**Example:**
```json theme={null}
{
"success": false,
"message": "This product is currently out of stock",
"code": "ProductUnavailable",
"data": null
}
```
**HTTP Status:** 400 Bad Request
**Description:** Insufficient funds in customer balance.
**Common causes:**
* Customer balance too low
* Attempting to use balance payment method
* Withdrawal amount exceeds available balance
**How to fix:**
* Check customer balance
* Request customer add funds
* Use alternative payment method
* Reduce order amount
**Example:**
```json theme={null}
{
"success": false,
"message": "Insufficient customer balance",
"code": "NotEnoughFunds",
"data": null
}
```
**HTTP Status:** 403 Forbidden
**Description:** Account verification is required before proceeding.
**Common causes:**
* Email not verified
* Identity verification pending
* Payment method requires verification
* Account limits require verification
**How to fix:**
* Complete email verification
* Submit required verification documents
* Verify payment methods
* Contact support for verification status
**Example:**
```json theme={null}
{
"success": false,
"message": "Email verification required",
"code": "VerificationRequired",
"data": null
}
```
### Feature & Upgrade Errors
**HTTP Status:** 403 Forbidden
**Description:** This feature is not available for your account.
**Common causes:**
* Feature disabled for your plan
* Beta feature not enabled
* Regional restrictions
* Feature requires specific integration
**How to fix:**
* Check feature availability for your plan
* Contact support to enable beta features
* Verify regional availability
* Review feature requirements
**Example:**
```json theme={null}
{
"success": false,
"message": "Advanced analytics is not available on your current plan",
"code": "FeatureUnavailable",
"data": null
}
```
**HTTP Status:** 402 Payment Required
**Description:** Your current plan does not support this operation.
**Common causes:**
* Plan limits exceeded
* Feature requires higher plan tier
* Usage quota exhausted
**How to fix:**
* Upgrade to higher plan tier
* Review current plan limits
* Reduce usage to stay within limits
* Contact sales for enterprise options
**Example:**
```json theme={null}
{
"success": false,
"message": "You have reached the maximum number of products for your plan",
"code": "UpgradeRequired",
"data": null
}
```
**HTTP Status:** 400 Bad Request
**Description:** The requested operation is not supported.
**Common causes:**
* Deprecated API version
* Invalid operation combination
* Unsupported payment method
* Unsupported currency
**How to fix:**
* Check API documentation for supported operations
* Use current API version
* Verify operation compatibility
* Check supported payment methods/currencies
**Example:**
```json theme={null}
{
"success": false,
"message": "This payment method is not supported for subscriptions",
"code": "UnsupportedRequest",
"data": null
}
```
### File & Upload Errors
**HTTP Status:** 400 Bad Request
**Description:** File upload failed.
**Common causes:**
* File too large
* Invalid file type
* Corrupted file
* Network interruption
* Storage quota exceeded
**How to fix:**
* Check file size limits
* Verify file type is supported
* Ensure file is not corrupted
* Retry upload with stable connection
* Check storage quota
**Example:**
```json theme={null}
{
"success": false,
"message": "File size exceeds maximum allowed size",
"code": "UploadFailed",
"data": null
}
```
### Server Errors
**HTTP Status:** 500 Internal Server Error
**Description:** An unexpected error occurred on the server.
**Common causes:**
* Server-side bug
* Database connection issue
* External service failure
* Unexpected condition
**How to fix:**
* Retry the request after a short delay
* If problem persists, contact support
* Check [status page](https://status.komerza.com) for incidents
* Include error ID when contacting support
**Example:**
```json theme={null}
{
"success": false,
"message": "An unexpected error occurred",
"code": "InternalServerError",
"data": null
}
```
Always include the `errorId` when reporting issues to support for faster resolution.
## HTTP Status Code Summary
| Status Code | Error Codes | Description |
| ----------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| 400 | ValidationError, BadCaptcha, ProductUnavailable, NotEnoughFunds, UnsupportedRequest, UploadFailed | Bad Request - Invalid input |
| 401 | BadToken, OAuthLoginRequired | Unauthorized - Invalid or missing authentication |
| 402 | UpgradeRequired | Payment Required - Plan upgrade needed |
| 403 | AccessDenied, TwoFactorRequired, VerificationRequired, FeatureUnavailable | Forbidden - Insufficient permissions |
| 404 | NotFound | Not Found - Resource doesn't exist |
| 409 | ObjectConflict | Conflict - Resource conflict |
| 429 | RateLimited | Too Many Requests - Rate limit exceeded |
| 500 | InternalServerError | Internal Server Error - Server-side issue |
## Support Resources
Check API and service status
Browse error troubleshooting guides
Get help from the community
Report issues with error IDs
# Filtering & Sorting
Source: https://docs.komerza.com/api-reference/filtering
Narrow down, order and page through list endpoints
List endpoints return everything by default. Four query parameters let you narrow that down:
| Parameter | Does |
| ---------- | ---------------------------- |
| `filters` | Which items come back |
| `sorts` | What order they come back in |
| `page` | Which page you want |
| `pageSize` | How many per page |
You can use any of them on their own. Endpoints that support them show a **Filterable Fields** table on their reference page - that table is the list of field names you're allowed to use.
## Start with one filter
A filter is three parts stuck together: **field**, **operator**, **value**.
```bash theme={null}
GET /stores/{storeId}/orders?filters=status==pending
```
That's `status` (field) `==` (equals) `pending` (value). Add more by separating with commas - every condition has to match:
```bash theme={null}
# Pending orders, paid by Stripe
GET /stores/{storeId}/orders?filters=status==pending,gateway==stripe
```
Then order them, newest first, with `-` meaning descending:
```bash theme={null}
GET /stores/{storeId}/orders?filters=status==pending&sorts=-dateFrom
```
And take them 50 at a time:
```bash theme={null}
GET /stores/{storeId}/orders?filters=status==pending&sorts=-dateFrom&page=1&pageSize=50
```
That's the whole idea. The rest of this page is the detail.
**A field name that isn't on the endpoint's list is ignored, not rejected.**
You get a `200` with unfiltered results rather than an error. If a filter
seems to do nothing, check the spelling against that endpoint's Filterable
Fields table before looking anywhere else.
## Operators
**Comparing numbers and dates:**
| Operator | Meaning | Example |
| -------- | ------------ | ---------------------- |
| `==` | Equals | `status==pending` |
| `!=` | Not equals | `gateway!=stripe` |
| `>` | Greater than | `rating>3` |
| `<` | Less than | `rating<3` |
| `>=` | At least | `dateFrom>=2026-01-01` |
| `<=` | At most | `dateTo<=2026-06-30` |
**Matching text:**
| Operator | Meaning | Example |
| -------- | ------------------- | ------------------ |
| `@=` | Contains | `email@=gmail` |
| `_=` | Starts with | `subject_=Refund` |
| `_-=` | Ends with | `email_-=.com` |
| `!@=` | Does not contain | `email!@=test` |
| `!_=` | Does not start with | `subject!_=[spam]` |
| `!_-=` | Does not end with | `email!_-=.ru` |
Text matching is case-sensitive. Add a `*` to the end of any operator to ignore case:
```bash theme={null}
?filters=subject@=Premium # matches "Premium", not "premium"
?filters=subject@=*premium # matches both
```
Field names are always case-insensitive, so `dateFrom` and `datefrom` both work.
## Matching several values
Separate values with `|` to match any of them:
```bash theme={null}
# Orders paid by Stripe or PayPal
?filters=gateway==stripe|paypal
```
Put several **fields** in brackets to run the same test against each, matching if any one hits:
```bash theme={null}
# "urgent" in either the subject or the customer's email
?filters=(subject|customerEmail)@=urgent
```
## Values that need care
Enum fields take their numeric value: `visibility==0`, **not**
`visibility==Public`. Passing the name matches nothing and returns an empty
list. Every value is in [Enums & Constants](/api-reference/enums).
Order status and gateway are the exceptions - they take the string name, as
their field tables show.
`dateFrom>=2026-01-01`, or with a time `dateFrom>=2026-01-01T09:30:00Z`.
`isBestSeller==true`. Anything that isn't recognisably a boolean counts as
`false`.
These mean something to the parser, so escape them with a backslash:
| To match | Write |
| --------------- | -------------------- |
| `some,value` | `field@=some\,value` |
| `some\|value` | `field@=some\|value` |
| the text "null" | `field@=\null` |
Unescaped, `field@=some,value` is read as two separate filters.
`!=` leaves out rows where the field is empty. `gateway!=stripe` returns
orders paid another way, but not orders with no gateway at all.
## Two shortcut filters
These aren't real fields, they're prepared questions. Pass `true`; passing `false` is the same as leaving them out.
| Field | On | Matches |
| ---------- | -------- | ----------------------------------------------------------- |
| `lowStock` | Products | Has a license-key variant and fewer than 5 in stock overall |
| `hasReply` | Reviews | Already has a merchant reply |
## Sorting
One field, or several separated by commas. The first is the main order, the rest break ties. `-` means descending.
```bash theme={null}
?sorts=-dateFrom # newest first
?sorts=status,-dateFrom # by status, newest first inside each status
```
When paging through results, sort by something nearly unique. If lots of rows
share the same value, their order can shift between requests, so you may see
the same row twice or miss one entirely.
## Paging
`page` starts at 1. `pageSize` defaults to **20** and is capped at **100** - ask for 500 and you get 100, with no error.
Responses tell you how many pages there are:
```json theme={null}
{
"success": true,
"pages": 5,
"data": [
// items
]
}
```
There's no total item count, so keep requesting pages until you reach `pages`.
A few endpoints use an older style, with the page in the path and a `limit` or
`count` parameter:
```
GET /stores/{storeId}/products/{productId}/variants/{variantId}/items/2?count=50
```
Their reference pages show what they accept.
## Recipes
Real requests you can paste and edit. `curl -G` with `--data-urlencode` matters here: filter values are full of `=`, `>`, `|` and `*`, and letting curl encode them avoids the request being mangled on the way out.
**Last 30 days of pending orders, newest first**
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/orders" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=status==pending,dateFrom>=2026-01-02" \
--data-urlencode "sorts=-dateFrom"
```
**Unanswered 1-2 star reviews**
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/reviews" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=rating<3,hasReply==false" \
--data-urlencode "sorts=-dateRangeFrom"
```
**Open tickets from one customer, oldest first**
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/tickets" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=status==0,customerEmail@=*buyer@example.com" \
--data-urlencode "sorts=dateCreated" \
--data-urlencode "pageSize=50"
```
**Public best-sellers running low on stock**
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/products" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=visibility==0,isBestSeller==true,lowStock==true"
```
## When it doesn't work
| What you see | What's wrong |
| --------------------------------- | ---------------------------------------------------------------------------------------------- |
| The filter changes nothing | The field isn't on that endpoint's list, or is misspelled. Unknown fields are ignored silently |
| Empty list when filtering an enum | You passed the name instead of the number - use `visibility==0` |
| One value became two filters | It contains a comma; escape it as `\,` |
| `pageSize=500` returned 100 items | 100 is the maximum, and larger values are quietly clamped |
| Results shuffle between pages | Equal sort values have no guaranteed order - add a tie-breaker field |
# Introduction
Source: https://docs.komerza.com/api-reference/introduction
The Komerza REST API: what it covers and where to start
The Komerza API is a REST interface to everything in your store. Anything you can do in the dashboard, you can automate: manage your catalogue, read and fulfil orders, issue refunds, look after customers, run coupons and affiliates, and receive events on your own server.
```
https://api.komerza.com
```
} href="/api-reference/quickstart">
Create a key, find your store ID and get a real response back, in three steps.
## What you can build
}>
Sync products, variants, pricing and stock from wherever you already manage
them.
}>
Read orders, deliver them, issue refunds, and push status into your own
systems.
}>
Drive checkout from a site you build yourself, with your own design.
}>
Feed orders and customers into accounting, CRM, fulfilment or analytics.
## Start here
} href="/api-reference/authentication">
API keys, required headers and scopes.
} href="/api-reference/responses">
The response envelope, list responses and how failures look.
} href="/api-reference/conventions">
IDs, dates, enums, money and store scoping.
} href="/api-reference/filtering">
Narrow, order and page through list endpoints.
## Related
} href="/api-reference/sdks">
Official libraries for .NET, JavaScript, Python, Go and PHP.
} href="/guides/webhooks">
Order events pushed to your server, instead of polling for them.
} href="/storefront/introduction">
Cart and checkout on a site you host yourself.
} href="https://status.komerza.com">
Live API availability.
# Exchange Code
Source: https://docs.komerza.com/api-reference/oauth2/exchange
GET /oauth2/{appId}/exchange/{code}
## Overview
Exchange an authorization code for a JWT API key. This is the final step in the OAuth2 flow, converting the temporary code into a permanent access token.
This endpoint requires the `X-OAuth2-Client-Secret` header for authentication instead
of the standard `Authorization` header.
## Path Parameters
| Parameter | Type | Description |
| --------- | ------ | ------------------------------------------------- |
| `appId` | string | Your application's Client ID |
| `code` | string | The authorization code received from the callback |
## Headers
| Header | Required | Description |
| ------------------------ | -------- | --------------------------------------------- |
| `X-OAuth2-Client-Secret` | Yes | Your application's 72-character client secret |
| `User-Agent` | Yes | Your application identifier |
**Server-Side Only**: This request must be made from your backend server,
never from client-side code. The client secret must remain confidential.
## Response
```json theme={null}
{
"success": true,
"data": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```
The `data` field contains a JWT API key that you can use to make authenticated API requests.
## Using the API Key
Include the JWT as a Bearer token in subsequent API requests:
```bash theme={null}
curl -X GET https://api.komerza.com/stores/STORE_ID \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "User-Agent: MyApp/1.0"
```
## Token Properties
The JWT API key:
* **Grants access** only to the stores the user authorized
* **Has permissions** limited to the scopes you requested and the user approved
* **Does not expire** until the user revokes authorization
* **Is tied** to the specific user and application
## Error Responses
| Error | Description |
| ----------------------- | ----------------------------------------------------------------- |
| `invalid_code` | The authorization code is invalid or has already been used |
| `code_expired` | The authorization code has expired (codes are valid for 1 minute) |
| `invalid_client_secret` | The client secret is incorrect |
| `app_not_found` | The application ID is invalid |
## Complete Flow Example
```javascript theme={null}
// 1. User clicks "Connect with Komerza" in your app
const authUrl = `https://dashboard.komerza.com/auth/authorize?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&state=${randomState}`;
window.location.href = authUrl;
// 2. User authorizes on Komerza, redirected back to your app
// URL: https://myapp.com/callback?code=ENCRYPTED_CODE&state=randomState
// 3. Your backend exchanges the code for an API key
const response = await fetch(
`https://api.komerza.com/oauth2/${CLIENT_ID}/exchange/${code}`,
{
headers: {
"X-OAuth2-Client-Secret": process.env.CLIENT_SECRET,
"User-Agent": "MyApp/1.0",
},
}
);
const { data: apiKey } = await response.json();
// 4. Store the API key securely and use it for API calls
const stores = await fetch("https://api.komerza.com/stores", {
headers: {
Authorization: `Bearer ${apiKey}`,
"User-Agent": "MyApp/1.0",
},
});
```
# OAuth2 Integration
Source: https://docs.komerza.com/api-reference/oauth2/introduction
Allow third-party applications to access user accounts with their consent
## Overview
Komerza's OAuth2 system enables third-party developers to create applications that can securely access user accounts with explicit user consent. This allows you to build integrations, plugins, and services that interact with Komerza stores on behalf of users.
Register your OAuth2 application in the Komerza Dashboard
## How It Works
Register your application with Komerza, specifying the permissions you need
and your redirect URLs.
Redirect users to Komerza's authorization page where they can review and
approve your app's access.
After approval, users are redirected back to your app with a temporary
authorization code.
Exchange the authorization code for a JWT API key that grants access to the
authorized stores.
## Authorization URL
Send users to the following URL to initiate the OAuth2 flow:
```
https://dashboard.komerza.com/auth/authorize
```
### Query Parameters
| Parameter | Required | Description |
| -------------- | -------- | ------------------------------------------------------ |
| `client_id` | Yes | Your application's Client ID |
| `redirect_uri` | Yes | One of your registered redirect URLs |
| `state` | No | Optional value passed back to your app in the callback |
### Example Authorization URL
```
https://dashboard.komerza.com/auth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=https://myapp.com/callback&state=random123
```
## OAuth2 Flow Diagram
```
┌─────────────┐ ┌─────────────┐
│ Your App │ │ Komerza │
└──────┬──────┘ └──────┬──────┘
│ │
│ 1. Redirect user to authorization URL │
│ ─────────────────────────────────────────> │
│ │
│ 2. User reviews & approves │
│ │
│ 3. Redirect back with authorization code │
│ <───────────────────────────────────────── │
│ │
│ 4. Exchange code for API key │
│ ─────────────────────────────────────────> │
│ │
│ 5. Return JWT API key │
│ <───────────────────────────────────────── │
│ │
│ 6. Make API calls with Bearer token │
│ ─────────────────────────────────────────> │
│ │
```
## Permissions & Scopes
When creating your application, you must specify which permissions (scopes) your app requires. Users will see these permissions during the authorization flow and can choose which stores to grant access to.
Complete reference for all available permission scopes
## Security Best Practices
**Client Secret Security**: Your client secret is like a password. Never
expose it in client-side code, public repositories, or logs.
All redirect URLs should use HTTPS in production environments.
Always exchange authorization codes from your backend server, never from
client-side code.
Store client secrets and API keys securely using environment variables or
secret management systems.
Only request the permissions your application actually needs.
## Token Characteristics
| Property | Value |
| ------------------------------- | --------------------- |
| **Token Type** | JWT (JSON Web Token) |
| **Authorization Code Validity** | 1 minute (single-use) |
| **API Key Validity** | Until revoked |
| **Max Redirect URLs** | 10 per application |
## Quick Links
Exchange authorization code for API key
# Payment Methods
Source: https://docs.komerza.com/api-reference/payment-methods
Complete reference for all supported payment methods and cryptocurrencies
## Overview
Komerza supports a wide range of payment methods including traditional payment gateways and cryptocurrencies. Each payment method has a unique identifier used throughout the API.
## Payment Gateways
### Traditional Processors
**Stripe** - Industry-leading payment processor supporting cards, wallets, and
local payment methods
**SumUp** - European payment gateway for card processing
**Square** - Payment processor with integrated point-of-sale solutions
**Cash App** - Mobile payment service for instant transfers
**Authorize.Net** - Long-established payment gateway for online transactions
**NMI** - Payment gateway similar to Authorize.Net
**Mollie** - European payment service provider supporting cards, iDEAL,
Bancontact, and 20+ local payment methods
**VenPayr** - Card payment processor
**moneymotion** - Card payment processor
**Pandabase** - Card payment processor
**Storrik** - Card payment processor
**Shopify** - Card payments through Shopify's hosted checkout
### PayPal
**PayPal Business** - PayPal business accounts with Instant Payment
Notifications (IPNs)
**PayPal Friends & Family** - PayPal personal payment method
### Customer Balance
**Customer Balance** - Payment using customer's account balance
## Cryptocurrencies
### Major Cryptocurrencies
**Bitcoin** - The original and most widely recognized cryptocurrency
* Network: Bitcoin
* Symbol: BTC
* Type: Native coin
**Ethereum** - Leading smart contract platform - Network: Ethereum - Symbol:
ETH - Type: Native coin
**Litecoin** - Faster alternative to Bitcoin - Network: Litecoin - Symbol: LTC
* Type: Native coin
**Bitcoin Cash** - Bitcoin fork with larger block sizes - Network: Bitcoin
Cash - Symbol: BCH - Type: Native coin
**Monero** - Privacy-focused cryptocurrency - Network: Monero - Symbol: XMR -
Type: Native coin
**Dash** - Digital cash with instant transactions - Network: Dash - Symbol:
DASH - Type: Native coin
**Dogecoin** - Popular meme-based cryptocurrency
* Network: Dogecoin
* Symbol: DOGE
* Type: Native coin
### Smart Contract Platforms
**Solana** - High-performance blockchain platform
* Network: Solana
* Symbol: SOL
* Type: Native coin
**Cardano** - Proof-of-stake blockchain platform - Network: Cardano - Symbol:
ADA - Type: Native coin
**TRON** - Decentralized entertainment ecosystem - Network: TRON - Symbol: TRX
* Type: Native coin
**Nano** - Zero-fee instant transactions
* Network: Nano
* Symbol: NANO
* Type: Native coin
### Stablecoins
Stablecoins are cryptocurrencies designed to maintain a stable value, typically pegged to fiat currencies like USD.
**USDT** - Most widely used stablecoin (1 USDT ≈ 1 USD)
Available on multiple networks:
* `usdt` - USD Tether (primary)
* `usdterc20` - USD Tether on Ethereum (ERC-20)
* `usdtbsc` - USD Tether on Binance Smart Chain (BEP-20)
* `usdtsol` - USD Tether on Solana (SPL)
**USDC** - Regulated stablecoin (1 USDC ≈ 1 USD) Available on multiple
networks: - `usdc` - USD Coin (primary) - `usdcbsc` - USD Coin on Binance
Smart Chain (BEP-20) - `usdcsol` - USD Coin on Solana (SPL)
**BUSD** - Binance's regulated stablecoin
* Network: Multiple
* Symbol: BUSD
* Peg: 1 BUSD ≈ 1 USD
### Exchange Tokens
**BNB** - Binance Smart Chain native token - Network: Binance Smart Chain -
Symbol: BNB - Type: Exchange utility token
### Meme Tokens
**SHIB** - Popular meme token - Network: Ethereum - Symbol: SHIB - Type:
ERC-20 token
## Related Resources
Process payments and create orders
Track payment statuses
# Order Status
Source: https://docs.komerza.com/api-reference/payment-status
Reference for all order statuses and their meanings
The order has not received payment and is considered incomplete. This is the
initial status for all new orders.
The order has received payment and the funds are pending or being verified by
the payment gateway.
The funds have been received but are withheld for verification. Commonly used
for cryptocurrency payments awaiting blockchain confirmations or fraud checks.
The order is being delivered. This is a guard status to prevent multiple
deliveries from happening simultaneously.
The order has been verified, paid for, and the product has been delivered to
the customer.
The order has been verified and paid for, but issues were encountered during
delivery that prevented fulfillment. This is typically due to missing stock
(e.g., no license keys available).
The order has been partially paid. The customer sent less than the required
amount.
The order was cancelled by the customer or the store before completion.
The store refunded the amount or the customer disputed the payment and won.
The order has been disputed by the customer, usually through a chargeback with
their payment gateway.
# Quick Start
Source: https://docs.komerza.com/api-reference/quickstart
Make your first Komerza API call
Three steps: create a key, call `/user` to find your store, then call something with that store ID. You need a Komerza account and dashboard access.
## 1. Create an API key
1. Open **[Account Settings → API Keys](https://dashboard.komerza.com/account?api)** in the dashboard
2. Click **Create New API Key** and give it a name you'll recognise later
3. Select the scopes it needs - `user.view` and `stores.view` are enough for this guide
4. Copy the key
The key is shown once. Store it in an environment variable or a secrets
manager, never in source control, and give each integration its own key so you
can revoke one without breaking the rest.
Every request needs two headers:
| Header | Value |
| --------------- | ---------------------------------------------------- |
| `Authorization` | `Bearer YOUR_API_KEY` |
| `User-Agent` | Something that identifies your app, e.g. `MyApp/1.0` |
## 2. Find your store ID
Almost every endpoint is scoped to a store. `GET /user` returns your account, with the stores on it:
```bash theme={null}
curl "https://api.komerza.com/user" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0"
```
```json theme={null}
{
"success": true,
"message": null,
"code": null,
"data": {
"id": "…",
"email": "you@example.com",
"plan": 1,
"stores": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "My Store",
"url": "mystore",
"currencyCode": "USD"
}
]
}
}
```
The `id` of the store you want is what every `{storeId}` in these docs refers to.
You can also copy it from the dashboard: **Online Store → Settings →
General**, where the **Store ID** field has a copy button next to it.
## 3. Call an endpoint
Fetch the store you just found:
```bash theme={null}
curl "https://api.komerza.com/stores/{storeId}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0"
```
```json theme={null}
{
"success": true,
"message": null,
"code": null,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"dateCreated": "2026-01-15T10:30:00Z",
"name": "My Store",
"description": "Digital goods, delivered instantly",
"url": "mystore",
"customDomain": "shop.example.com",
"currencyCode": "USD",
"rating": 4.8,
"isTicketingEnabled": true,
"isCustomerBalanceEnabled": false
}
}
```
That's it - you're authenticated and reading real data.
## The response envelope
Every response has the same four fields:
| Field | What it is |
| --------- | ------------------------------------------------------ |
| `success` | Whether the request worked |
| `message` | Human-readable detail, often `null` on success |
| `code` | Error code when something went wrong, otherwise `null` |
| `data` | The payload - an object, an array, or `null` |
Errors keep the same shape:
```json theme={null}
{
"success": false,
"message": "Access denied."
}
```
Check `success` rather than assuming a `200` means you got what you asked for.
## When it doesn't work
The key is missing, malformed or revoked. Check you sent `Authorization:
Bearer YOUR_KEY` including the `Bearer ` prefix, and that the key still
exists in the dashboard.
The key is valid but lacks the scope for that endpoint. Scopes follow the
resource: reading orders needs `stores.orders.view`, refunding them needs
`stores.orders.refund`. The full list is in [Scopes](/api-reference/scopes).
Scopes are fixed when the key is created, so create a new key with the ones
you need.
Usually a store ID that doesn't exist, belongs to another account, or picked
up a stray character. Confirm it against `GET /user`. Check the path too:
the base URL is `https://api.komerza.com` with **no** `/api` prefix.
You're sending requests faster than allowed. Back off and retry with
increasing delays rather than immediately.
## Next steps
} href="/api-reference/filtering">
Narrow, order and page through list endpoints.
} href="/api-reference/scopes">
Every permission a key can carry.
} href="/api-reference/endpoint/products/get-list">
Read and manage your catalogue.
} href="/api-reference/endpoint/orders/get-list">
Query orders, deliver and refund them.
} href="/guides/webhooks">
Get order events pushed to your server instead of polling.
} href="/api-reference/sdks">
Official libraries for .NET, JavaScript, Python, Go and PHP.
# Responses & Errors
Source: https://docs.komerza.com/api-reference/responses
The response envelope, list responses, and how failures are reported
Every response uses the same envelope, whether it succeeded or not.
| Field | Type | Description |
| --------- | ----------------------- | -------------------------------------------------- |
| `success` | boolean | Whether the request did what you asked |
| `message` | string \| null | Human-readable detail, usually `null` on success |
| `code` | string \| null | Error code when something failed, otherwise `null` |
| `data` | object \| array \| null | The payload |
```json theme={null}
{
"success": true,
"message": null,
"code": null,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "My Store"
}
}
```
Branch on `success`, not on the status code alone, and read `code` rather than
`message` when handling failures. Messages are written for humans and can be
reworded; codes are stable.
## List responses
Endpoints returning a list add `pages`: how many pages exist at the current page size. There's no total item count, so page until you reach it.
```json theme={null}
{
"success": true,
"pages": 5,
"data": [
// items
]
}
```
See [Filtering & Sorting](/api-reference/filtering) for narrowing, ordering and paging those results.
## Errors
```json theme={null}
{
"success": false,
"message": "Access denied.",
"code": "AccessDenied",
"data": null
}
```
`code` is a stable PascalCase identifier: `AccessDenied`, `NotFound`, `ObjectConflict`, `ValidationError`, `UpgradeRequired` and so on.
### Validation errors
Validation failures add `invalidFields`, one entry per problem, each formatted `Field: what's wrong`:
```json theme={null}
{
"success": false,
"message": "Validation failed",
"code": "ValidationError",
"data": null,
"invalidFields": ["Name: Must be between 3 and 64 characters"]
}
```
Surface these to whoever submitted the data - they name the field and the rule it broke.
### HTTP status codes
| Status | Usually means |
| ------ | ----------------------------------------------------- |
| `400` | Malformed request, or validation failed |
| `401` | Missing, malformed or revoked API key |
| `403` | Valid key, but it lacks the scope for this endpoint |
| `404` | Wrong ID, or a resource on an account you can't reach |
| `409` | Conflicts with something that already exists |
| `429` | Too many requests |
| `5xx` | Our side |
## Retrying
Retry `429` and `5xx` with increasing delays rather than immediately. Don't retry `4xx` responses other than `429` - the request will fail the same way until you change it.
If a `5xx` persists, check [status.komerza.com](https://status.komerza.com) before digging into your own code.
} href="/api-reference/error-codes">
The full list of `code` values, what triggers each and how to recover.
# Scopes
Source: https://docs.komerza.com/api-reference/scopes
Complete reference for all available API key permission scopes
## Overview
API keys use a permission-based scope system that controls access to specific endpoints and operations. Each API key can be configured with one or more scopes to limit access according to your integration needs.
API keys can be generated and scopes can be configured from your [Komerza
Dashboard](https://dashboard.komerza.com/account?api).
## Scope Categories
### Store Management
**Permission to view stores** Read access to store information, settings,
and configuration. Required for most store-related read operations.
**Permission to update stores** Modify store configuration, settings,
branding, and operational parameters.
**Permission to create new stores** Create new store instances. Typically used
for multi-store applications or store management platforms.
**Permission to view store analytics** Access store performance metrics,
sales data, traffic statistics, and analytical insights.
### Categories
**Permission to view categories in stores** Read access to product
categories, category hierarchy, and organization structure.
**Permission to create new categories in stores** Add new product categories
to organize your store catalog.
**Permission to update categories in stores** Modify category information,
reorganize hierarchy, and update category settings.
**Permission to delete categories in stores** Remove categories from the
store. Ensure products are reassigned before deleting categories.
### Products & Inventory
**Permission to view products in stores** Read access to product catalog,
variants, pricing, and inventory information.
**Permission to create new products in stores** Add new products to the store
catalog, including variants and pricing.
**Permission to update products in stores** Modify existing products, update
pricing, inventory levels, and product details.
**Permission to delete products in stores** Remove products from the store
catalog. Use with caution as this action may affect order history.
### Customer Management
**Permission to view customers in stores** Access customer profiles, contact
information, and purchase history.
**Permission to create new customers in stores** Add new customer profiles to
the store database.
**Permission to update customers in stores** Modify customer information,
update balances, and manage customer accounts.
### Order Processing
**Permission to view orders in stores** Access order information, payment
status, and order history.
**Permission to create orders** Process new orders, including payment
processing and order creation.
**Permission to deliver orders in stores** Mark orders as delivered, update
tracking information, and manage fulfillment.
**Permission to refund orders** Process refunds, partial refunds, and handle
order cancellations.
### Marketing & Promotions
**Permission to view coupons in stores** Access coupon information, usage
statistics, and discount configurations.
**Permission to create new coupons in stores** Create discount codes,
promotional campaigns, and coupon configurations.
**Permission to update coupons in stores** Modify existing coupons, update
discount amounts, and change validity periods.
**Permission to delete coupons in stores** Remove coupons from the system.
Active coupons should be disabled rather than deleted.
### Affiliate Program
**Permission to view affiliates in stores** Access affiliate information,
commission data, and performance statistics.
**Permission to update affiliates in stores** Modify affiliate settings,
commission rates, and payout configurations.
### Support & Communication
**Permission to view tickets in stores** Access support tickets, customer
inquiries, and communication history.
**Permission to update tickets in stores** Respond to tickets, update ticket
status, and manage customer support workflows.
**Permission to delete tickets in stores** Remove tickets from the system.
Consider archiving instead of deleting for audit purposes.
**Permission to flag reviews in stores** Mark reviews as inappropriate,
spam, or requiring moderation.
### User Account & Platform
**Permission to view user profile information** Access user account details,
preferences, and profile information.
**Permission to view enabled cryptocurrencies for a user** Access
cryptocurrency payment settings and enabled payment methods.
This scope is deprecated and will be removed in future versions. Please avoid
using it.
### Webhooks & Integrations
**Permission to view webhooks** Access webhook configurations, event
subscriptions, and delivery logs.
**Permission to manage webhooks: create** Set up new webhook endpoints and
event subscriptions.
**Permission to manage webhooks: update** Modify existing webhook
configurations and event subscriptions.
**Permission to manage webhooks: delete** Remove webhook endpoints and
cancel event subscriptions.
## Best Practices
### Principle of Least Privilege
Only request the minimum scopes necessary for your application. This reduces security risks and makes it easier to audit API access.
### Scope Planning
Plan your required scopes during the design phase of your integration. Consider:
* Which resources you need to read vs. modify
* Whether you need create/delete permissions
* If your application handles sensitive customer data
# SDKs
Source: https://docs.komerza.com/api-reference/sdks
Official SDKs for the Komerza API - coming soon
## Overview
We're building official SDKs to make integrating with the Komerza API even easier. These SDKs will provide idiomatic interfaces, automatic error handling, and built-in best practices for each programming language.
All SDKs are currently in development. While you wait, you can use the API
directly via HTTP requests as shown in our [Quick Start
Guide](/api-reference/quickstart).
## Planned SDKs
**Coming Soon** Full-featured SDK for .NET applications with strong typing
and async support.
**Coming Soon** Modern SDK with TypeScript definitions for Node.js and browser
environments.
**Coming Soon** Pythonic interface with support for async/await and popular
frameworks.
**Coming Soon** Modern PHP SDK with Composer support and PSR-compliant design.
**Coming Soon** Idiomatic Go SDK with proper error handling and context
support.
## SDK Previews
Here's what the SDK interfaces will look like:
```csharp .NET theme={null}
using Komerza.Api;
var client = new KomerzaClient("your-api-key");
// Get store information
var store = await client.Stores.GetAsync(storeId);
// List products with pagination
var products = await client.Products.ListAsync(storeId, new ProductListOptions
{
Page = 1,
PageSize = 20
});
// Create a new product
var newProduct = await client.Products.CreateAsync(storeId, new CreateProductRequest
{
Name = "New Product",
Price = 29.99m,
Currency = "USD"
});
```
```javascript JavaScript/TypeScript theme={null}
import { KomerzaClient } from "@komerza/api";
const client = new KomerzaClient("your-api-key");
// Get store information
const store = await client.stores.get(storeId);
// List products with pagination
const products = await client.products.list(storeId, {
page: 1,
pageSize: 20,
});
// Create a new product
const newProduct = await client.products.create(storeId, {
name: "New Product",
price: 29.99,
currency: "USD",
});
```
```python Python theme={null}
from komerza import KomerzaClient
client = KomerzaClient('your-api-key')
# Get store information
store = await client.stores.get(store_id)
# List products with pagination
products = await client.products.list(store_id, page=1, page_size=20)
# Create a new product
new_product = await client.products.create(store_id, {
'name': 'New Product',
'price': 29.99,
'currency': 'USD'
})
```
```php PHP theme={null}
stores->get($storeId);
// List products with pagination
$products = $client->products->list($storeId, [
'page' => 1,
'pageSize' => 20
]);
// Create a new product
$newProduct = $client->products->create($storeId, [
'name' => 'New Product',
'price' => 29.99,
'currency' => 'USD'
]);
```
```go Go theme={null}
package main
import (
"context"
"github.com/komerza/go-sdk"
)
func main() {
client := komerza.NewClient("your-api-key")
ctx := context.Background()
// Get store information
store, err := client.Stores.Get(ctx, storeID)
if err != nil {
// Handle error
}
// List products with pagination
products, err := client.Products.List(ctx, storeID, &komerza.ProductListOptions{
Page: 1,
PageSize: 20,
})
// Create a new product
newProduct, err := client.Products.Create(ctx, storeID, &komerza.CreateProductRequest{
Name: "New Product",
Price: 29.99,
Currency: "USD",
})
}
```
## Get Notified
Want to be the first to know when an SDK is released?
Get updates in our Telegram developer community
Watch our GitHub organization for new SDK releases
## In the Meantime
While waiting for the official SDKs, you can:
### Use the REST API Directly
Follow our [Quick Start Guide](/api-reference/quickstart) to start building with HTTP requests right away.
### Build Your Own Wrapper
Create a simple wrapper around the API for your specific use case. Our [API Reference](/api-reference/introduction) provides all the details you need.
### Community SDKs
Check if there are any community-built SDKs available. We'll showcase quality community contributions here.
## Contributing
Interested in contributing to an SDK or building one for another language?
Get in touch about SDK development opportunities
We welcome contributions and are happy to work with the community to build SDKs for additional programming languages and frameworks.
## Stay Updated
Check our help center for SDK documentation as it becomes available
Learn the API while waiting for your preferred SDK
# Basket
Source: https://docs.komerza.com/storefront/basket
Manage shopping cart items with the Komerza client library
## Overview
The basket (shopping cart) is stored in the browser's localStorage and persists across page reloads. All basket operations are synchronous and update the local state immediately.
The `komerza` object is available globally after including the script. Call
`komerza.init('your-store-id')` before using any other methods.
## Adding Items
Add a product variant to the basket with a specified quantity.
```javascript theme={null}
komerza.addToBasket(productId, variantId, quantity);
```
### Parameters
The unique identifier of the product
The unique identifier of the product variant
How many items to add (defaults to 1 if not provided)
### Example
```javascript theme={null}
// Add 2 items of a specific product variant
komerza.addToBasket("prod_abc123", "var_xyz789", 2);
// Add to cart button handler
document.getElementById("add-to-cart").addEventListener("click", () => {
const productId = "prod_abc123";
const variantId = document.getElementById("variant-select").value;
const quantity = parseInt(document.getElementById("quantity").value) || 1;
komerza.addToBasket(productId, variantId, quantity);
alert("Added to cart!");
});
```
## Viewing Basket
Get all items currently in the basket.
```javascript theme={null}
const items = komerza.getBasket();
```
### Returns
Returns an array of `BasketItem` objects:
```typescript theme={null}
interface BasketItem {
productId: string; // Product identifier
variantId: string; // Variant identifier
quantity: number; // Number of items
}
```
### Example
```javascript theme={null}
// Display cart items
const basket = komerza.getBasket();
if (basket.length === 0) {
console.log("Cart is empty");
} else {
basket.forEach((item) => {
console.log(
`Product: ${item.productId}, Variant: ${item.variantId}, Qty: ${item.quantity}`
);
});
}
```
## Getting Item Count
Get the total number of items in the basket (sum of all quantities).
```javascript theme={null}
const count = komerza.getBasketItemCount();
```
### Returns
Returns a number representing the total quantity of all items.
### Example
```javascript theme={null}
// Update cart badge
function updateCartBadge() {
const count = komerza.getBasketItemCount();
document.getElementById("cart-count").textContent = count;
if (count > 0) {
document.getElementById("cart-badge").style.display = "block";
}
}
// Call on page load and after cart changes
updateCartBadge();
```
## Removing Items
Remove a specific product variant from the basket entirely.
```javascript theme={null}
komerza.removeFromBasket(productId, variantId);
```
### Parameters
The product identifier to remove
The variant identifier to remove
### Example
```javascript theme={null}
// Remove button handler
function removeItem(productId, variantId) {
if (confirm("Remove this item from cart?")) {
komerza.removeFromBasket(productId, variantId);
updateCartDisplay();
}
}
```
## Clearing Basket
Remove all items from the basket at once.
```javascript theme={null}
komerza.clearBasket();
```
### Example
```javascript theme={null}
// Clear cart button
document.getElementById("clear-cart").addEventListener("click", () => {
if (confirm("Remove all items from cart?")) {
komerza.clearBasket();
updateCartDisplay();
}
});
```
## Backup & Restore
Create backups of the basket state and restore them later. Useful for handling checkout errors or implementing "save for later" features.
### Create Backup
```javascript theme={null}
const success = komerza.createBasketBackup();
```
Returns `true` if backup was created successfully.
### Restore Backup
```javascript theme={null}
const success = komerza.restoreBasketFromBackup();
```
Returns `true` if backup was restored successfully.
### Example
```javascript theme={null}
// Save basket before checkout
async function proceedToCheckout() {
// Create backup in case checkout fails
komerza.createBasketBackup();
try {
const order = await komerza.checkout(email, couponCode);
// Success - redirect to payment
window.location.href = order.data.gatewayMetadata.clientSecret;
} catch (error) {
// Restore basket on error
komerza.restoreBasketFromBackup();
alert("Checkout failed. Your cart has been restored.");
}
}
```
## Validation
Validate that the basket data is intact and properly formatted.
```javascript theme={null}
const isValid = komerza.validateBasketIntegrity();
```
Returns `true` if basket data is valid, `false` if corrupted.
### Example
```javascript theme={null}
// Validate on page load
window.addEventListener("load", () => {
if (!komerza.validateBasketIntegrity()) {
console.error("Cart data corrupted, clearing basket");
komerza.clearBasket();
}
});
```
## Advanced Operations
### Merge Baskets
Merge items from another basket into the current basket. Useful for syncing carts across devices or sessions.
```javascript theme={null}
const mergedBasket = komerza.mergeBaskets(otherBasket);
```
**Parameters:**
* `otherBasket` - Array of `BasketItem` objects to merge
**Returns:** The merged basket as an array of `BasketItem` objects.
```javascript theme={null}
// Example: Load saved cart from server and merge
async function syncCart() {
const savedCart = await fetch("/api/saved-cart").then((r) => r.json());
const merged = komerza.mergeBaskets(savedCart);
console.log(`Merged ${merged.length} items`);
}
```
### Move Basket Item
Move a specific item from one product/variant to another.
```javascript theme={null}
const success = komerza.moveBasketItem(
fromProductId,
fromVariantId,
toProductId,
toVariantId
);
```
**Parameters:**
* `fromProductId` - Source product ID
* `fromVariantId` - Source variant ID
* `toProductId` - Destination product ID
* `toVariantId` - Destination variant ID
**Returns:** `true` if the item was moved successfully.
```javascript theme={null}
// Example: User switches variant selection
function changeVariant(productId, oldVariantId, newVariantId) {
komerza.moveBasketItem(productId, oldVariantId, productId, newVariantId);
updateCartDisplay();
}
```
### Get Basket Statistics
Get detailed statistics about the current basket.
```javascript theme={null}
const stats = komerza.getBasketStats();
```
**Returns:** An object containing basket statistics (structure varies).
```javascript theme={null}
// Example: Display cart summary
const stats = komerza.getBasketStats();
console.log("Cart stats:", stats);
```
## Complete Example
Here's a complete shopping cart implementation:
```html theme={null}
Shopping Cart
Shopping Cart (0)
```
Basket data is stored in localStorage - it's not synced across devices or
browsers
## Next Steps
Learn how to fetch and display products
Process orders and handle payments
# Checkout
Source: https://docs.komerza.com/storefront/checkout
Process orders and redirect customers to hosted checkout
## Overview
The `checkout()` function creates an order from the current basket and automatically redirects the customer to Komerza's hosted checkout page to complete payment.
The checkout function validates the basket, creates an order, and
**automatically redirects** to the payment page. No manual redirect handling
needed.
## Basic Usage
```javascript theme={null}
const response = await komerza.checkout(emailAddress, couponCode);
```
### Parameters
Customer's email address for the order. Must be a valid email format.
Optional coupon/discount code to apply to the order. Must be 3-32 characters
if provided.
### Returns
Returns a Promise that resolves to `ApiResponse`:
```typescript theme={null}
interface ApiResponse {
success: boolean; // Whether the order was created
message?: string; // Error message if failed
invalidFields?: Array<{
// Validation errors if any
fieldName: string;
reason: string;
}>;
data?: Order; // Order details if successful
}
```
If `success` is `true`, the browser will automatically redirect to the
checkout page. The response is mainly useful for error handling.
## Validation
The checkout function performs validation before creating the order:
**Basket Validation:**
* Basket cannot be empty
* All items must have valid product and variant IDs
**Email Validation:**
* Must be a valid email address format
**Coupon Validation (if provided):**
* Must be at least 3 characters
* Cannot exceed 32 characters
## Simple Example
```javascript theme={null}
// Checkout button handler
async function handleCheckout() {
const email = document.getElementById("email").value;
const coupon = document.getElementById("coupon").value;
try {
const response = await komerza.checkout(email, coupon);
// Only executes if checkout failed (no redirect occurred)
if (!response.success) {
alert(response.message || "Checkout failed");
}
} catch (error) {
console.error("Checkout error:", error);
alert("An error occurred during checkout");
}
}
```
## Complete Example with Validation
```html theme={null}
Checkout
Checkout
```
## Error Handling
Common error scenarios and how to handle them:
### Empty Basket
```javascript theme={null}
const response = await komerza.checkout(email);
if (!response.success && response.message === "Basket is empty") {
alert("Please add items to your cart before checking out");
}
```
### Validation Errors
```javascript theme={null}
const response = await komerza.checkout(email, coupon);
if (!response.success && response.invalidFields) {
response.invalidFields.forEach((field) => {
console.log(`${field.fieldName}: ${field.reason}`);
// Display error next to the relevant form field
});
}
```
### General Errors
```javascript theme={null}
try {
await komerza.checkout(email, coupon);
} catch (error) {
// Network error or other exception
console.error("Checkout failed:", error);
alert("Could not complete checkout. Please check your connection.");
}
```
## Response Object
The `Order` object in the response contains:
```typescript theme={null}
interface Order {
id: string; // Order ID
status: string; // Order status (e.g., "pending")
amount: number; // Total amount
amountPaid: number; // Amount paid so far
currencyCode: string; // Currency (e.g., "USD")
gateway: string; // Payment gateway used
items: LineItem[]; // Order line items
customer: {
emailAddress: string;
countryCode: string;
};
gatewayMetadata?: {
clientSecret?: string; // Payment URL/secret
transactionId?: string;
// ... other gateway-specific fields
};
coupon?: {
code: string;
percentageDecrease?: number;
netDecrease: number;
};
dateCreated: string; // ISO 8601 timestamp
}
```
## Affiliate Tracking
The checkout function automatically includes affiliate tracking if an affiliate cookie (`kmrza_ref`) is present:
```javascript theme={null}
// Affiliate ID is automatically read from cookies
// No manual handling required
await komerza.checkout(email);
```
The affiliate cookie is set when customers visit your store through an affiliate link.
## Best Practices
**Validate before submission** - Check email format client-side for instant feedback
**Create basket backups** - Save basket state before checkout to restore if payment fails
**Disable during processing** - Prevent double-submissions by disabling the checkout button
**Handle all error cases** - Account for empty basket, validation errors, and network failures
**Show loading states** - Display progress indicators while checkout is processing
**Don't auto-clear basket** - The basket persists after checkout - clear it manually after payment confirmation if needed
## Next Steps
Learn how to manage cart items
Display and fetch product information
# Currency Formatting
Source: https://docs.komerza.com/storefront/formatting
Format prices consistently using your store's currency
## Overview
The `createFormatter()` function returns an `Intl.NumberFormat` instance configured for your store's currency. Use it to display prices consistently throughout your storefront.
## Getting the Formatter
```javascript theme={null}
const formatter = await komerza.createFormatter();
```
Returns a standard JavaScript `Intl.NumberFormat` object configured with your store's currency code (e.g., USD, EUR, GBP).
## Basic Usage
```javascript theme={null}
const formatter = await komerza.createFormatter();
// Format a price
const price = formatter.format(29.99);
console.log(price); // "$29.99" (for USD stores)
```
## Examples
```javascript theme={null}
const formatter = await komerza.createFormatter();
// Different amounts
formatter.format(9.99); // "$9.99"
formatter.format(100); // "$100.00"
formatter.format(1499.5); // "$1,499.50"
formatter.format(0.99); // "$0.99"
// Works with any number
formatter.format(29); // "$29.00"
formatter.format(29.9); // "$29.90"
formatter.format(29.999); // "$30.00" (rounds)
```
## Display Product Prices
```javascript theme={null}
async function displayProducts() {
const [storeResponse, formatter] = await Promise.all([
komerza.getStore(),
komerza.createFormatter(),
]);
if (!storeResponse.success) return;
storeResponse.data.products.forEach((product) => {
product.variants.forEach((variant) => {
console.log(`${variant.name}: ${formatter.format(variant.cost)}`);
});
});
}
```
## Price Ranges
Display "From \$X" for products with multiple variants:
```javascript theme={null}
async function displayPriceRange(product) {
const formatter = await komerza.createFormatter();
const prices = product.variants.map((v) => v.cost);
const minPrice = Math.min(...prices);
const maxPrice = Math.max(...prices);
if (minPrice === maxPrice) {
return formatter.format(minPrice);
}
return `${formatter.format(minPrice)} - ${formatter.format(maxPrice)}`;
}
```
## Cart Total
Calculate and format basket totals:
```javascript theme={null}
async function getCartTotal() {
const [storeResponse, formatter] = await Promise.all([
komerza.getStore(),
komerza.createFormatter(),
]);
if (!storeResponse.success) return null;
const basket = komerza.getBasket();
const products = storeResponse.data.products;
let total = 0;
basket.forEach((item) => {
const product = products.find((p) => p.id === item.productId);
if (!product) return;
const variant = product.variants.find((v) => v.id === item.variantId);
if (!variant) return;
total += variant.cost * item.quantity;
});
return formatter.format(total);
}
```
## Caching the Formatter
Create the formatter once and reuse it to avoid unnecessary API calls:
```javascript theme={null}
// Store formatter globally
let currencyFormatter = null;
async function getFormatter() {
if (!currencyFormatter) {
currencyFormatter = await komerza.createFormatter();
}
return currencyFormatter;
}
// Use throughout your app
async function formatPrice(amount) {
const formatter = await getFormatter();
return formatter.format(amount);
}
```
## Intl.NumberFormat Methods
Since `createFormatter()` returns a standard `Intl.NumberFormat`, you have access to all its methods:
### format()
Returns a formatted string:
```javascript theme={null}
formatter.format(1234.56); // "$1,234.56"
```
### formatToParts()
Returns an array of parts for custom rendering:
```javascript theme={null}
const parts = formatter.formatToParts(1234.56);
// [
// { type: "currency", value: "$" },
// { type: "integer", value: "1" },
// { type: "group", value: "," },
// { type: "integer", value: "234" },
// { type: "decimal", value: "." },
// { type: "fraction", value: "56" }
// ]
// Build custom display
const integer = parts
.filter((p) => p.type === "integer" || p.type === "group")
.map((p) => p.value)
.join("");
const fraction = parts.find((p) => p.type === "fraction")?.value || "00";
console.log(`$${integer}.${fraction}`);
```
## Currency by Store
The formatter automatically uses your store's configured currency:
| Store Currency | Example Output |
| -------------- | -------------- |
| USD | \$29.99 |
| EUR | €29.99 |
| GBP | £29.99 |
| JPY | ¥2,999 |
| CAD | CA\$29.99 |
| AUD | A\$29.99 |
## Complete Example
```html theme={null}
Products
```
## Next Steps
Learn about other store data you can fetch
Manage shopping cart items
# Installation & Setup
Source: https://docs.komerza.com/storefront/installation
Install the Komerza client library and initialize your storefront
## Installation
Include the Komerza client library via CDN. Add this script tag to your HTML:
```html theme={null}
```
The library is available globally as `globalThis.komerza` after the script loads.
### CDN Benefits
* **No build step required** - Works with plain HTML/CSS/JS
* **Always up-to-date** - Latest version automatically delivered
* **Fast loading** - Cached globally via CDN
* **Lightweight** - Minimal impact on page load time
## Initialization
Initialize the library with your store ID before using any other functions:
```javascript theme={null}
komerza.init("your-store-id");
```
You **must** call `init()` before using any other `komerza` function. All
other functions will fail if the library hasn't been initialized.
### Finding Your Store ID
Your store ID is available in your Komerza dashboard. It's a unique identifier for your store, typically in UUID format.
### Example
```html theme={null}
My Store
```
## When to Initialize
Call `init()` once when your page loads, before using any other `komerza` methods:
**✅ Good - Initialize on page load:**
```javascript theme={null}
```
**❌ Bad - Using methods before init:**
```javascript theme={null}
```
## Framework Integration
### Vanilla JavaScript
```html theme={null}
```
### React
```jsx theme={null}
import { useEffect } from "react";
function App() {
useEffect(() => {
// Initialize when component mounts
if (globalThis.komerza) {
globalThis.komerza.init("your-store-id");
}
}, []);
return My Store;
}
```
### Vue
```vue theme={null}
```
### Svelte
```svelte theme={null}
```
## Build Process
The Komerza client library works with any framework or build process-the script runs in the browser regardless of how you build your site.
**Use any framework:**
* Plain HTML/CSS/JS (no build needed)
* React, Vue, Svelte, Angular
* Next.js, Nuxt, SvelteKit, Astro
* Webpack, Vite, Rollup, Parcel
**Hosting requirement:**
To host your storefront on Komerza infrastructure or list it on the theme marketplace, your project must compile to static HTML/CSS/JS files. Server-side rendering at runtime is not supported.
## TypeScript Support
If you're using TypeScript, you can add type definitions:
```typescript theme={null}
declare global {
var komerza: {
init(storeId: string): void;
getStore(): Promise>;
getBasket(): BasketItem[];
checkout(email: string, coupon?: string): Promise>;
// ... other methods
};
}
// Use with types
globalThis.komerza.init("your-store-id");
```
Type definitions are available in the [script.json](/storefront/script.json) file for reference.
## Verification
Verify the library is loaded and initialized correctly:
```javascript theme={null}
// Check if library is loaded
if (typeof komerza === "undefined") {
console.error("Komerza library not loaded");
} else {
console.log("Komerza library loaded successfully");
// Initialize
komerza.init("your-store-id");
// Verify initialization by fetching store
komerza.getStore().then((response) => {
if (response.success) {
console.log("Store loaded:", response.data.name);
} else {
console.error("Failed to load store:", response.message);
}
});
}
```
## Troubleshooting
**"komerza is not defined"**
* Make sure the script tag is loaded before your code runs
* Check browser console for script loading errors
* Verify the CDN URL is correct
**"Komerza.init must be called before using this method"**
* Call `komerza.init()` before using other functions
* Ensure init is called only once per page load
**Store not loading**
* Verify your store ID is correct (check dashboard)
* Check browser console for API errors
* Ensure you're connected to the internet
## Next Steps
Now that you've initialized the library, learn how to use it:
Fetch store details, logo, and banner
Display and manage product listings
Handle shopping cart operations
Process orders and payments
# Introduction
Source: https://docs.komerza.com/storefront/introduction
Build custom storefront templates with the Komerza client library
## What is Storefront Development?
Komerza lets you build fully custom storefronts using any web technology you prefer. Create your store with HTML, CSS, and JavaScript-or use frameworks like React, Vue, or Svelte-and host it directly on Komerza's infrastructure.
Your storefront imports the **Komerza Client Library**, a lightweight script that handles all the e-commerce functionality:
* **Store & Products** - Fetch store info, products, variants, and reviews
* **Shopping Cart** - Manage cart state, quantities, and selections
* **Checkout** - Create orders and redirect to hosted payment pages
* **Analytics** - Automatic tracking for views and conversions (no code required)
## Why Build on Komerza?
No templates or themes to fight against. You control every pixel.
The client library handles all API communication. Just HTML, CSS, and JS.
Deploy to Komerza infrastructure or list on the theme marketplace.
Static files load instantly with global CDN delivery.
Your storefront stays online under any load. Built on Cloudflare for
unmatched resilience.
Every change and deployment is automatically backed up. Store up to 20
backups on selected plans to keep your storefront code safe.
## Quick Example
```html theme={null}
My Store
```
## Theme Marketplace
Build storefronts that other Komerza merchants can use. Your templates can be:
* Listed on the theme marketplace for other stores
* Customized with configuration options
* Deployed instantly by merchants
* Monetized through the marketplace
## Client Library vs API Reference
| | Client Library | API Reference |
| -------------- | ---------------------- | -------------------- |
| **For** | Customer storefronts | Store management |
| **Auth** | Public (store ID) | Private (API keys) |
| **Runs** | Client-side (browser) | Server-side |
| **Operations** | Browse, cart, checkout | Full CRUD, analytics |
The client library is for **customer-facing** storefronts. For admin
operations like creating products or managing orders, use the [API
Reference](/api-reference/introduction).
## Get Started
Install the client library and initialize your store
Fetch store details, logo, banner, and products
Add, remove, and manage cart items
Process orders and handle payments
# Products
Source: https://docs.komerza.com/storefront/products
Fetch and display products, variants, and reviews
## Overview
Access product data through two methods:
* **`getStore()`** - Returns all products in the store (included in store response)
* **`getProduct()`** - Fetch a single product by ID or slug
* **`getProductReviews()`** - Get paginated reviews for a product
## Get All Products
Products are included in the store response:
```javascript theme={null}
const response = await komerza.getStore();
if (response.success) {
const products = response.data.products;
products.forEach((product) => {
console.log(product.name, product.variants.length, "variants");
});
}
```
## Get Single Product
Fetch a product by ID or slug:
```javascript theme={null}
const response = await komerza.getProduct("product-id-or-slug");
```
### Parameters
The product's unique ID (UUID) or URL slug
### Example
```javascript theme={null}
// By ID
const byId = await komerza.getProduct("550e8400-e29b-41d4-a716-446655440000");
// By slug
const bySlug = await komerza.getProduct("premium-license");
if (bySlug.success) {
const product = bySlug.data;
console.log(product.name);
console.log(product.description);
console.log(product.variants);
}
```
## Product Object
```typescript theme={null}
interface Product {
id: string; // Unique identifier
name: string; // Product name
description: string; // Description (supports markdown)
slug?: string; // URL-friendly slug
imageNames: string[]; // Product image filenames (see Images page)
rating: number; // Average review rating
order: number; // Display order
visibility: number; // 0=Public, 1=Unlisted, 2=Private
storeId: string; // Parent store ID
variants: Variant[]; // Product variants
dateCreated: string; // ISO 8601 timestamp
}
```
## Variants
Every product has one or more variants. Each variant has its own price, stock, and settings:
```typescript theme={null}
interface Variant {
id: string; // Variant ID
name: string; // Variant name
productId: string; // Parent product ID
storeId: string; // Store ID
cost: number; // Price in store currency
stock: number; // Available stock
stockMode: number; // Stock calculation mode (see below)
minimumQuantity: number; // Min purchase quantity (default: 1)
maximumQuantity: number; // Max purchase quantity (-1 = unlimited)
order: number; // Display order
imageNames: string[]; // Variant-specific image filenames
type: number; // 0=One-time, 1=Subscription
subscriptionPeriod?: string; // Subscription duration if applicable
requireDiscordAuthorization: boolean;
dateCreated: string;
}
```
### Stock Calculation Mode
The `stockMode` field determines how stock availability is calculated:
| Value | Mode | Description |
| ----- | ---------- | ------------------------------------------------------ |
| 0 | Calculated | Stock is automatically calculated from available items |
| 1 | Ignored | Stock is not tracked-always available |
| 2 | Fixed | Stock is manually set and decremented on purchase |
See the [Stock Calculation Mode enum](/api-reference/enums#stock-calculation-mode) for full details.
### Displaying Images
Product and variant `imageNames` contain filenames, not full URLs. See the [Images](/storefront/images) page for how to construct full image URLs.
### Display Variants
```javascript theme={null}
async function displayProduct(productId) {
const [productResponse, formatter] = await Promise.all([
komerza.getProduct(productId),
komerza.createFormatter(),
]);
if (!productResponse.success) return;
const product = productResponse.data;
// Sort variants by order
const variants = product.variants.sort((a, b) => a.order - b.order);
variants.forEach((variant) => {
const price = formatter.format(variant.cost);
const inStock = variant.stockMode === 1 || variant.stock > 0;
console.log(`${variant.name}: ${price}`);
console.log(` Stock: ${inStock ? "Available" : "Out of stock"}`);
console.log(` Min qty: ${variant.minimumQuantity}`);
console.log(
` Max qty: ${
variant.maximumQuantity === -1 ? "Unlimited" : variant.maximumQuantity
}`
);
});
}
```
### Variant Selection
```html theme={null}
```
## Product Reviews
Get paginated reviews for a product:
```javascript theme={null}
const response = await komerza.getProductReviews(productId, page);
```
### Parameters
The product's unique ID
Page number (1-indexed)
### Returns
Returns `PaginatedApiResponse`:
```typescript theme={null}
interface PaginatedApiResponse {
success: boolean;
pages: number; // Total number of pages
data?: Review[]; // Array of reviews
message?: string;
}
interface Review {
id: string;
rating: number; // 1-5 stars
reason: string; // Review text
productId: string;
reply?: string; // Merchant reply if any
dateCreated: string;
}
```
### Display Reviews
```javascript theme={null}
async function displayReviews(productId, page = 1) {
const response = await komerza.getProductReviews(productId, page);
if (!response.success) return;
const container = document.getElementById("reviews");
container.innerHTML = response.data
.map(
(review) => `
${review.reason}
${
review.reply
? `Response: ${review.reply}
`
: ""
}
`
)
.join("");
// Pagination
if (response.pages > 1) {
container.innerHTML += `
${
page > 1
? ``
: ""
}
Page ${page} of ${response.pages}
${
page < response.pages
? ``
: ""
}
`;
}
}
```
## Product Images
Product images are referenced by filename. Construct the full URL:
```javascript theme={null}
function getProductImageUrl(storeId, productId, imageName) {
return `https://cdn.komerza.com/stores/${storeId}/products/${productId}/${imageName}`;
}
// Usage
const product = response.data;
const imageUrl = getProductImageUrl(
product.storeId,
product.id,
product.imageNames[0]
);
```
### Image Gallery
```javascript theme={null}
function displayProductImages(product) {
const gallery = document.getElementById("gallery");
// Use variant images if available, otherwise product images
const images =
product.variants[0].imageNames.length > 0
? product.variants[0].imageNames
: product.imageNames;
gallery.innerHTML = images
.map(
(img) => `
`
)
.join("");
}
```
## Stock Handling
Check stock availability before adding to cart:
```javascript theme={null}
function canAddToCart(variant, quantity) {
// Stock mode 1 = Ignored (unlimited)
if (variant.stockMode === 1) return true;
// Check available stock
if (variant.stock < quantity) return false;
// Check quantity limits
if (quantity < variant.minimumQuantity) return false;
if (variant.maximumQuantity !== -1 && quantity > variant.maximumQuantity)
return false;
return true;
}
function getStockStatus(variant) {
if (variant.stockMode === 1) return "In Stock";
if (variant.stock === 0) return "Out of Stock";
if (variant.stock < 10) return `Only ${variant.stock} left`;
return "In Stock";
}
```
## Complete Example
```html theme={null}
Product Page
Reviews
```
## Next Steps
Format prices in your store's currency
Add products to the shopping cart
# Store Information
Source: https://docs.komerza.com/storefront/store
Fetch store details, branding assets, and currency formatting
## Overview
Access your store's public information including name, description, logo, banner, and currency settings.
## Get Store Details
Retrieve complete information about your store.
```javascript theme={null}
const response = await komerza.getStore();
```
### Returns
Returns `ApiResponse`:
```typescript theme={null}
interface Store {
id: string; // Store identifier
name: string; // Store name
description: string; // Store description (supports markdown)
url: string; // Store subdomain
customDomain?: string; // Custom domain if set
currencyCode: string; // ISO 4217 currency code (e.g., "USD")
domain: string; // Full domain
rating: number; // Average store rating
maintenanceReason?: string; // If store is in maintenance mode
branding?: {
iconFileName?: string; // Logo filename
bannerFileName?: string; // Banner filename
accentColor?: string; // Store accent color
isAutomaticCurrencyConversionEnabled?: boolean;
};
products: ProductReference[]; // List of products
categories: Category[]; // List of store categories
affiliateOptions?: {
isEnabled: boolean;
defaultReturnPercentage: number;
defaultPercentageOff: number;
canConvertAffiliateBalance: boolean;
isPublicRegistrationEnabled: boolean;
isLinkEditingEnabled: boolean;
};
dateCreated: string; // ISO 8601 timestamp
}
interface Category {
id: string;
name: string;
slug: string;
visibility: number;
order: number;
storeId: string;
productCount: number;
products: string[]; // List of product IDs
dateCreated: string;
}
```
### Example
```javascript theme={null}
// Display store information
async function displayStoreInfo() {
const response = await komerza.getStore();
if (response.success) {
const store = response.data;
document.getElementById("store-name").textContent = store.name;
document.getElementById("store-description").textContent =
store.description;
document.getElementById(
"product-count"
).textContent = `${store.products.length} products`;
console.log(`Store uses ${store.currencyCode} currency`);
}
}
```
## Get Store Logo
Get the URL to your store's logo image.
```javascript theme={null}
const logoUrl = await komerza.getStoreLogoUrl();
```
### Returns
Returns a string containing the full URL to the logo image.
### Example
```javascript theme={null}
// Display store logo
async function displayLogo() {
const logoUrl = await komerza.getStoreLogoUrl();
const img = document.createElement("img");
img.src = logoUrl;
img.alt = "Store Logo";
document.getElementById("logo-container").appendChild(img);
}
```
## Get Store Banner
Get the URL to your store's banner image.
```javascript theme={null}
const bannerUrl = await komerza.getStoreBannerUrl();
```
### Returns
Returns a string containing the full URL to the banner image.
### Example
```javascript theme={null}
// Display store banner
async function displayBanner() {
const bannerUrl = await komerza.getStoreBannerUrl();
document.getElementById(
"hero-banner"
).style.backgroundImage = `url('${bannerUrl}')`;
}
```
## Currency Formatter
Create a number formatter for displaying prices in your store's currency.
```javascript theme={null}
const formatter = await komerza.createFormatter();
```
### Returns
Returns an `Intl.NumberFormat` instance configured for your store's currency.
### Example
```javascript theme={null}
// Format prices consistently
async function displayProduct(product) {
const formatter = await komerza.createFormatter();
product.variants.forEach((variant) => {
const priceFormatted = formatter.format(variant.cost);
console.log(`${variant.name}: ${priceFormatted}`);
});
}
```
### Advanced Formatting
```javascript theme={null}
const formatter = await komerza.createFormatter();
// Format different amounts
console.log(formatter.format(29.99)); // "$29.99" (USD)
console.log(formatter.format(100)); // "$100.00"
console.log(formatter.format(0.99)); // "$0.99"
// Use in templates
const price = formatter.format(product.price);
element.innerHTML = `${price}`;
```
## Complete Example
Here's a complete storefront header with store branding:
```html theme={null}
Store
```
## Caching
Store data is automatically cached by the library for performance:
**Cached data:**
* Store details (`getStore`)
* Logo URL (`getStoreLogoUrl`)
* Banner URL (`getStoreBannerUrl`)
* Currency formatter (`createFormatter`)
The library handles cache invalidation automatically. You don't need to worry about stale data.
## Error Handling
```javascript theme={null}
// Handle store loading errors
async function loadStoreInfo() {
const response = await komerza.getStore();
if (!response.success) {
console.error("Failed to load store:", response.message);
// Show user-friendly error
document.getElementById("error").textContent =
"Unable to load store information. Please try again later.";
return;
}
// Handle maintenance mode
if (response.data.maintenanceReason) {
document.getElementById("maintenance").textContent =
response.data.maintenanceReason;
document.getElementById("maintenance").style.display = "block";
}
// Process store data
displayStore(response.data);
}
```
## Best Practices
**Load in parallel** - Use `Promise.all()` to fetch store data, logo, and banner simultaneously for faster loading
**Cache the formatter** - Create the currency formatter once and reuse it throughout your page
**Handle maintenance mode** - Check `maintenanceReason` and display appropriate messages to customers
**Fallback for images** - Provide fallback images if logo/banner URLs fail to load
**Markdown support** - Store descriptions support markdown - use a markdown parser if needed
## Next Steps
Learn how to display and fetch products
Handle shopping cart operations
# Blacklist Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/blacklist
POST /stores/{storeId}/affiliates/{customerId}/blacklist
Disables the affiliate, blocks the customer on the store, and adds their email to the store's blacklist.
Requires the `stores.affiliates.update` permission.
Blacklisting stops the affiliate earning on new referrals. It does not claw
back commission already accrued, and it does not refund orders they referred.
# Configure Affiliate Program
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/configure
PUT /stores/{storeId}/affiliates/configure
Updates the configuration options for the affiliate program of the specified store.
Requires the `stores.affiliates.update` permission.
# Create Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/create
POST /stores/{storeId}/affiliates/{customerId}
Creates a new affiliate record for the given customer in this store.
Requires the `stores.affiliates.update` permission.
# Delete Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/delete
DELETE /stores/{storeId}/affiliates/{customerId}
Permanently removes an affiliate and all their payout records from the store.
Requires the `stores.affiliates.update` permission.
# Get Affiliates
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/get-list
GET /stores/{storeId}/affiliates
Retrieves affiliates for the specified store using query-based filter options.
Supports filtering, sorting, and pagination via query parameters.
Requires the `stores.affiliates.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ---------------------- | ------- | ----------------------------------------------------------- |
| `isEnabled` | boolean | Filter by affiliate enabled status |
| `email` | string | Filter by email address (contains) |
| `link` | string | Filter by affiliate link/code (contains) |
| `balanceFrom` | number | Minimum affiliate balance (inclusive) |
| `balanceTo` | number | Maximum affiliate balance (inclusive) |
| `returnPercentageFrom` | integer | Minimum return percentage (inclusive) |
| `returnPercentageTo` | integer | Maximum return percentage (inclusive) |
| `percentageOffFrom` | integer | Minimum discount percentage (inclusive) |
| `percentageOffTo` | integer | Maximum discount percentage (inclusive) |
| `referredOrdersFrom` | integer | Minimum referred delivered orders (inclusive) |
| `referredOrdersTo` | integer | Maximum referred delivered orders (inclusive) |
| `allTimeEarnedFrom` | number | Minimum all-time earned amount (inclusive) |
| `allTimeEarnedTo` | number | Maximum all-time earned amount (inclusive) |
| `payoutCountFrom` | integer | Minimum payout count (inclusive) |
| `payoutCountTo` | integer | Maximum payout count (inclusive) |
| `dateFrom` | string | Filter affiliates created from this date/time (inclusive) |
| `dateTo` | string | Filter affiliates created before this date/time (inclusive) |
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | -------- | ----------------------------------------------------- |
| `isEnabled` | boolean | Filter by whether the affiliate profile is enabled |
| `email` | string | Filter by affiliate email (case-insensitive contains) |
| `link` | string | Filter by referral link or code |
| `balance` | number | Current unpaid balance |
| `allTimeEarned` | number | Total commission earned across all time |
| `referredOrders` | integer | Number of orders attributed to this affiliate |
| `payoutCount` | integer | Number of payouts recorded against this affiliate |
| `returnPercentage` | number | The affiliate's commission rate |
| `percentageOff` | number | The customer discount applied to their referrals |
| `dateCreated` | datetime | When the affiliate profile was created |
Numeric and date fields take ranges through the comparison operators rather than separate from/to parameters.
**Sortable:** `isEnabled`, `email`, `link`, `referredOrders`, `allTimeEarned`, `payoutCount`, `dateCreated`.
**Example:**
```bash theme={null}
# Enabled affiliates who have earned over 100, biggest earners first
GET /stores/{storeId}/affiliates?filters=isEnabled==true,allTimeEarned>=100&sorts=-allTimeEarned
# Affiliates with an outstanding balance and at least one referred order
GET /stores/{storeId}/affiliates?filters=balance>0,referredOrders>=1
```
# List Affiliate Orders
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/get-orders
GET /stores/{storeId}/affiliates/{customerId}/orders/{page}
Retrieves a paginated list of orders attributed to a specific affiliate (by customerId) in the store.
Requires the `stores.affiliates.view` permission.
# List Affiliate Payouts
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/get-payouts
GET /stores/{storeId}/affiliates/{customerId}/payouts/{page}
Requires the `stores.affiliates.view` permission.
# Invite Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/invite
POST /stores/{storeId}/affiliates/invite
Invites an affiliate, this will create a customer profile if one does not exist and takes in a customer email
address instead.
Requires the `stores.affiliates.update` permission.
# Record Affiliate Payout
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/record-payout
POST /stores/{storeId}/affiliates/{customerId}/payouts
Requires the `stores.affiliates.update` permission.
**This records a payout, it does not send money.** Komerza reduces the
affiliate's balance by the amount; the actual transfer happens between you and
your affiliate outside Komerza.
Record it only once you have actually paid, or your balances will disagree with
reality.
# Search Affiliates
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/search
GET /stores/{storeId}/affiliates/search
Searches affiliates by the `query` parameter and applies filtering/sorting/pagination.
The query is matched against affiliate email, affiliate link/code, affiliate ID, and customer ID.
Requires the `stores.affiliates.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | -------- | ----------------------------------------------------- |
| `isEnabled` | boolean | Filter by whether the affiliate profile is enabled |
| `email` | string | Filter by affiliate email (case-insensitive contains) |
| `link` | string | Filter by referral link or code |
| `balance` | number | Current unpaid balance |
| `allTimeEarned` | number | Total commission earned across all time |
| `referredOrders` | integer | Number of orders attributed to this affiliate |
| `payoutCount` | integer | Number of payouts recorded against this affiliate |
| `returnPercentage` | number | The affiliate's commission rate |
| `percentageOff` | number | The customer discount applied to their referrals |
| `dateCreated` | datetime | When the affiliate profile was created |
Numeric and date fields take ranges through the comparison operators rather than separate from/to parameters.
**Sortable:** `isEnabled`, `email`, `link`, `referredOrders`, `allTimeEarned`, `payoutCount`, `dateCreated`.
**Example:**
```bash theme={null}
# Enabled affiliates who have earned over 100, biggest earners first
GET /stores/{storeId}/affiliates?filters=isEnabled==true,allTimeEarned>=100&sorts=-allTimeEarned
# Affiliates with an outstanding balance and at least one referred order
GET /stores/{storeId}/affiliates?filters=balance>0,referredOrders>=1
```
# Get Affiliate Statistics
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/stats
GET /stores/{storeId}/affiliates/stats
Retrieves the affiliate statistics associated with a specific store.
Requires the `stores.affiliates.view` permission.
# Update Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/update
PUT /stores/{storeId}/affiliates/{customerId}
Updates affiliate settings for the given customer in this store.
Requires the `stores.affiliates.update` permission.
# Void Affiliate Payout
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/void-payout
PATCH /stores/{storeId}/affiliates/{customerId}/payouts/{payoutId}
Marks the payout as voided, restores balances, and records the admin and reason.
Requires the `stores.affiliates.update` permission.
Voiding a recorded payout **restores the amount to the affiliate's balance**.
Use it when a payout was recorded in error or the transfer failed - not to
reverse money you actually sent.
# Create Customer
Source: https://docs.komerza.com/api-reference/endpoint/customers/create
POST /stores/{storeId}/customers
Registers a customer for the store using their email.
If the customer already exists and is linked to the store, a 409 conflict is returned.
Requires the `stores.customers.create` permission.
# Get Customer
Source: https://docs.komerza.com/api-reference/endpoint/customers/get
GET /stores/{storeId}/customers/{customerId}
Retrieves detailed information for a single customer in the specified store.
Requires the `stores.customers.view` permission.
# Get Customer by Email
Source: https://docs.komerza.com/api-reference/endpoint/customers/get-by-email
GET /stores/{storeId}/customers/{customerEmail}/email
Retrieves detailed information for a single customer in the specified store.
Requires the `stores.customers.view` permission.
# Get Customers
Source: https://docs.komerza.com/api-reference/endpoint/customers/get-list
GET /stores/{storeId}/customers
Retrieves a paginated list of customers for the specified store.
Requires the `stores.customers.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | ------- | ---------------------------------------------------- |
| `email` | string | Filter by email address (case-insensitive contains) |
| `ipAddress` | string | Filter by IP address (case-insensitive contains) |
| `balanceFrom` | decimal | Filter by minimum balance (inclusive) |
| `balanceTo` | decimal | Filter by maximum balance (inclusive) |
| `stripeCustomerId` | string | Filter by Stripe customer ID (exact match) |
| `affiliateCode` | string | Filter by affiliate code (case-insensitive contains) |
**Example:**
```bash theme={null}
# Customers with balance between $10 and $100
GET /stores/{storeId}/customers?filters=balanceFrom>=10,balanceTo<=100
# Customers by affiliate code
GET /stores/{storeId}/customers?filters=affiliateCode@=*PROMO
```
# Search Customers
Source: https://docs.komerza.com/api-reference/endpoint/customers/search
GET /stores/{storeId}/customers/search/{query}
Retrieves a paginated list of customers for the specified store, matching the provided query.
Requires the `stores.customers.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | ------- | ---------------------------------------------------- |
| `email` | string | Filter by email address (case-insensitive contains) |
| `ipAddress` | string | Filter by IP address (case-insensitive contains) |
| `balanceFrom` | decimal | Filter by minimum balance (inclusive) |
| `balanceTo` | decimal | Filter by maximum balance (inclusive) |
| `stripeCustomerId` | string | Filter by Stripe customer ID (exact match) |
| `affiliateCode` | string | Filter by affiliate code (case-insensitive contains) |
**Example:**
```bash theme={null}
# Customers with balance between $10 and $100
GET /stores/{storeId}/customers?filters=balanceFrom>=10,balanceTo<=100
# Customers by affiliate code
GET /stores/{storeId}/customers?filters=affiliateCode@=*PROMO
```
# Update Customer Balance
Source: https://docs.komerza.com/api-reference/endpoint/customers/update-balance
PATCH /stores/{storeId}/customers/{customerId}/balance
Updates the store-specific balance for a customer.
Requires the `stores.customers.update` permission.
**Store credit is real spendable value.** The balance applies immediately at
your checkout and never expires, so treat this like issuing money.
It is not cashable - customers cannot withdraw it - and a top-up order can
never be refunded to balance, since that would credit them twice.
# Approve Custom Payment
Source: https://docs.komerza.com/api-reference/endpoint/orders/approve-custom-payment
POST /stores/{storeId}/paymentMethods/custom/orders/{orderId}/approve
Approves a pending custom payment order, marking it as delivered.
Requires the `stores.orders.deliver` permission.
# Create Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/create
POST /stores/{storeId}/orders
Creates a new order for a customer in the admin dashboard.
Requires the `stores.orders.create` permission.
## Payment Methods
When creating an order, specify the `paymentMethod` field using one of the supported payment method codes. See the [Payment Methods reference](/api-reference/payment-methods) for the complete list of available options.
## Redirecting to Checkout
After successfully creating an order, redirect the customer to the hosted checkout page to complete payment:
```
https://checkout.komerza.com/checkout/{orderId}
```
### Example
```javascript theme={null}
const response = await fetch(`/stores/${storeId}/orders`, {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}` },
body: JSON.stringify(orderData),
});
const result = await response.json();
if (result.success) {
const orderId = result.data.id;
window.location.href = `https://checkout.komerza.com/checkout/${orderId}`;
}
```
The checkout page handles payment processing, order confirmation, and receipt
generation automatically.
# Deliver Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/deliver
PUT /stores/{storeId}/orders/{orderId}/deliver
Forces delivery of an existing order as if purchased normally.
No action is taken if the order is already delivered.
Requires the `stores.orders.deliver` permission.
**This does not verify that you were paid.** It runs the full delivery flow as
though payment had arrived: stock is consumed, license keys leave your
inventory, the receipt email is sent, `order.completed` fires and Discord roles
are granted.
Confirm the payment first. For custom payment methods, use [Approve Custom
Payment](/api-reference/endpoint/orders/approve-custom-payment) instead, which
is the reviewed path.
Manually delivered orders can only ever be refunded to **customer balance**,
never back to the gateway.
# Get Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/get
GET /stores/{storeId}/orders/{orderId}
Retrieve order details by ID in the given store.
Requires the `stores.orders.view` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
# Get Orders
Source: https://docs.komerza.com/api-reference/endpoint/orders/get-list
GET /stores/{storeId}/orders
Gets orders in the specified store and optionally filters by the given criteria.
Requires the `stores.orders.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ---------- | -------- | -------------------------------------------------------------------------------- |
| `dateFrom` | datetime | Filter orders created on or after this date |
| `dateTo` | datetime | Filter orders created on or before this date |
| `status` | string | Filter by order status. See [Order Statuses](/api-reference/payment-status) |
| `gateway` | string | Filter by payment gateway. See [Payment Methods](/api-reference/payment-methods) |
| `email` | string | Filter by customer email address |
**Example:**
```bash theme={null}
# Orders from last 30 days with "pending" status
GET /stores/{storeId}/orders?filters=dateFrom>=2025-10-28,status==pending
# Orders paid via Stripe, sorted by newest first
GET /stores/{storeId}/orders?filters=gateway==stripe&sorts=-dateFrom
```
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
# Get Proof of Payment
Source: https://docs.komerza.com/api-reference/endpoint/orders/get-payment-proof
GET /stores/{storeId}/paymentMethods/custom/orders/{orderId}/proof
Gets the proof of payment submitted by a customer for an order.
Requires the `stores.orders.view` permission.
# Re-deliver Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/redeliver
PUT /stores/{storeId}/orders/{orderId}/redeliver
Forces redelivery of an already delivered order as if purchased normally.
Requires the `stores.orders.deliver` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
**This reruns the entire delivery, exactly like a fresh purchase.** It takes
**new license keys** from your inventory, re-fires dynamic delivery webhooks,
and emails the customer again.
Call it once, deliberately. Repeated calls consume stock every time, and there
is no way to return issued keys to inventory.
# Refund Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/refund
DELETE /stores/{storeId}/orders/{orderId}/refund
Refunds a delivered order for a customer in the admin dashboard.
Requires the `stores.orders.refund` permission.
**Refunds move real money and cannot be undone.** Only **Delivered** orders can
be refunded, always for the **full amount** - there are no partial refunds.
A successful refund also reverses any affiliate commission and the Buyer
Protection accrual on that order.
For cryptocurrency, custom payment methods, Cash App and PayPal Friends &
Family, marking an order refunded **moves no money** - you must return the
funds yourself. Those methods can only refund to customer balance.
# Reject Custom Payment
Source: https://docs.komerza.com/api-reference/endpoint/orders/reject-custom-payment
POST /stores/{storeId}/paymentMethods/custom/orders/{orderId}/reject
Rejects a pending custom payment order, cancelling it.
Requires the `stores.orders.deliver` permission.
# Resend Receipt
Source: https://docs.komerza.com/api-reference/endpoint/orders/resend-receipt
PUT /stores/{storeId}/orders/{orderId}/resend
Resends the delivery receipt to the customer for a previously delivered order.
Rate limited due to sensitivity.
Requires the `stores.orders.deliver` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
# Search Orders
Source: https://docs.komerza.com/api-reference/endpoint/orders/search
GET /stores/{storeId}/orders/search/{query}
Search orders by ID or customer email in the specified store.
Requires the `stores.orders.view` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ---------- | -------- | -------------------------------------------------------------------------------- |
| `dateFrom` | datetime | Filter orders created on or after this date |
| `dateTo` | datetime | Filter orders created on or before this date |
| `status` | string | Filter by order status. See [Order Statuses](/api-reference/payment-status) |
| `gateway` | string | Filter by payment gateway. See [Payment Methods](/api-reference/payment-methods) |
| `email` | string | Filter by customer email address |
**Example:**
```bash theme={null}
# Orders from last 30 days with "pending" status
GET /stores/{storeId}/orders?filters=dateFrom>=2025-10-28,status==pending
# Orders paid via Stripe, sorted by newest first
GET /stores/{storeId}/orders?filters=gateway==stripe&sorts=-dateFrom
```
# Add to Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/add-to-catalogue
PUT /stores/{storeId}/reseller/catalogue/{variantId}
Adds a variant to the catalogue or updates its terms. The variant does not have to be visible on
the storefront — offering resellers something ordinary customers never see is a supported use.
Requires the `stores.resellers.update` permission.
# Configure Programme
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/configure
PUT /stores/{storeId}/reseller
Sets whether the store supplies resellers, how they enrol, the discount they pay, and the
optional exposure and top-up limits.
Requires the `stores.resellers.update` permission.
# Create Application
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-application
POST /stores/{storeId}/reseller/applications
For a merchant hosting their own application form. Collect whatever fields suit your site, then
post them here with the applicant's email — answers may reference the questions configured on
this store, carry a `label` of your own, or mix the two.
The email is taken as given and not verified: the form is yours, on your domain, and you
review what it produces. The applicant proves the address the first time they sign in to
deposit or reach their reseller panel.
The application lands in the same review queue as every other, at
`GET /stores/{storeId}/reseller/application/submissions`.
Requires the `stores.resellers.update` permission.
# Create Basket Discount
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-basket-tier
POST /stores/{storeId}/reseller/basket-tiers
Measured at list price before anything comes off, so qualifying for a discount cannot lower a
basket's value and disqualify it again. Where several apply, the largest wins.
Requires the `stores.resellers.update` permission.
# Create Bundle
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-bundle
POST /stores/{storeId}/reseller/bundles
A bundle is a basket you have composed and priced as one thing. Set a discount to advertise a
figure, or leave it off and let your basket-size discounts decide.
Every product in it has to be in your reseller catalogue already, and it can never cost a
reseller more than buying the same lines separately would.
Requires the `stores.resellers.update` permission.
# Create Invitation
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-invite
POST /stores/{storeId}/reseller/invites
Returns the invite token once and only once — only its hash is stored, because the token grants
an already-approved reseller relationship and a leaked invite list must not be usable. Send it on
immediately; it cannot be read back.
Requires the `stores.resellers.update` permission.
# Create Question
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-question
POST /stores/{storeId}/reseller/application/questions
Questions can be added, reworded and reordered at any time, including while applications are
waiting: an answer records the wording it was given against, so editing the form never changes
what someone already in the queue appears to have been asked.
Requires the `stores.resellers.update` permission.
# Delete Basket Discount
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-basket-tier
DELETE /stores/{storeId}/reseller/basket-tiers/{basketTierId}
Requires the `stores.resellers.update` permission.
# Delete Bundle
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-bundle
DELETE /stores/{storeId}/reseller/bundles/{bundleId}
Takes it off sale. What resellers already bought through it, and what they paid, stays on record.
Requires the `stores.resellers.update` permission.
# Delete Invitation
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-invite
DELETE /stores/{storeId}/reseller/invites/{inviteId}
Requires the `stores.resellers.update` permission.
# Delete Question
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-question
DELETE /stores/{storeId}/reseller/application/questions/{questionId}
Archived rather than deleted. Every answer ever given to it still points at it, and tidying the
form should not orphan the applications that answered it.
Requires the `stores.resellers.update` permission.
# Get Programme
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get
GET /stores/{storeId}/reseller
Returns the store's reseller settings plus headline counts. A store that has never configured a
programme comes back disabled with defaults rather than a 404.
Requires the `stores.resellers.view` permission.
# Get Applications
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-applications
GET /stores/{storeId}/reseller/application/submissions
The review queue: who has applied, what they answered, and what they have deposited since. The
deposit is summed from the balance ledger rather than stored, so it always matches the money
that actually arrived.
Requires the `stores.resellers.view` permission.
# Get Basket Discounts
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-basket-tiers
GET /stores/{storeId}/reseller/basket-tiers
Volume terms measured on what a whole basket comes to rather than on how many of one product it
holds, so a reseller taking a spread of your catalogue is credited for the whole commitment.
Requires the `stores.resellers.view` permission.
# Get Bundles
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-bundles
GET /stores/{storeId}/reseller/bundles
Each comes back priced the way your resellers will see it: what it contains, what that comes to
at list price, the discount, and what they would pay. A bundle whose products have left your
reseller catalogue comes back unavailable, with the reason, rather than disappearing.
Requires the `stores.resellers.view` permission.
# Get Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-catalogue
GET /stores/{storeId}/reseller/catalogue
The variants resellers can draw, each with its list price and the price a reseller pays, so the
merchant can see the margin they are giving away.
Requires the `stores.resellers.view` permission.
# Get Invitations
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-invites
GET /stores/{storeId}/reseller/invites
Tokens are never included — they exist only in the response that created them.
Requires the `stores.resellers.view` permission.
# Get Leaderboard
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-leaderboard
GET /stores/{storeId}/reseller/leaderboard
Your resellers ranked by what they spent with you over a period, biggest first. Built for
publishing: every reseller is identified by the store they sell from — its name, description,
storefront and branding, all of which is public already — so nothing here has to be masked
before it goes on a page.
`from` is inclusive and `to` exclusive. Omit them for the last thirty days.
Spend is net of refunds, so a reseller who was refunded more than they spent in the period
does not appear.
A reseller who sells outside Komerza still ranks, with no store attached — leaving them out
would produce a leaderboard whose total disagreed with your own revenue.
Requires the `stores.resellers.view` permission.
# Get Questions
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-questions
GET /stores/{storeId}/reseller/application/questions
The store's reseller application form, in the order applicants are asked. Retired questions are
left out — they exist only so answers already given to them stay readable.
Requires the `stores.resellers.view` permission.
# Get Resellers
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-resellers
GET /stores/{storeId}/reseller/resellers
Who resells for this store, what they have spent, and what they can currently draw against.
Does not include the resellers' own customers — who they sell to is their business.
Requires the `stores.resellers.view` permission.
# Get Variant Stats
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-variant-stats
GET /stores/{storeId}/reseller/catalogue/{variantId}/stats
How many resellers list this variant and what they have drawn, so the reselling side of a product
is visible on the product page rather than only in the reseller section.
Requires the `stores.resellers.view` permission.
# Grant Samples
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/grant-samples
POST /stores/{storeId}/reseller/resellers/{resellerId}/samples
Gives a reseller free units so they can prove delivery works end to end before funding a
balance. Samples are spent before money on a matching draw.
Requires the `stores.resellers.update` permission.
# Remove from Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/remove-from-catalogue
DELETE /stores/{storeId}/reseller/catalogue/{variantId}
Removes it from the catalogue entirely, discarding its price overrides. Resellers already
listing it keep the listing, but their next draw fails. To pause it and keep the terms, set
`isEnabled` to false instead.
Requires the `stores.resellers.update` permission.
# Set Volume Tiers
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/set-volume-tiers
PUT /stores/{storeId}/reseller/catalogue/{variantId}/tiers
Applies only when a reseller buys stock up front, not to ordinary draws — drawing one unit at a
time commits them to nothing, so there is nothing for a discount to buy. Replaces the whole set,
so what you send is exactly what applies.
Requires the `stores.resellers.update` permission.
# Update Basket Discount
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-basket-tier
PUT /stores/{storeId}/reseller/basket-tiers/{basketTierId}
Requires the `stores.resellers.update` permission.
# Update Bundle
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-bundle
PUT /stores/{storeId}/reseller/bundles/{bundleId}
Replaces the bundle wholesale, lines and all. Anyone who already bought it keeps what they
bought at the price they paid.
Requires the `stores.resellers.update` permission.
# Update Question
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-question
PUT /stores/{storeId}/reseller/application/questions/{questionId}
Requires the `stores.resellers.update` permission.
# Update Status
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-status
PUT /stores/{storeId}/reseller/resellers/{resellerId}/status
Revoking stops further draws but leaves the reseller's balance alone — they funded it and are
still owed it.
Requires the `stores.resellers.update` permission.
# Accept Invitation
Source: https://docs.komerza.com/api-reference/endpoint/reselling/accept-invite
POST /stores/{storeId}/reselling/invites/accept
Redeems an invite on behalf of the store in the route, which `PlatformAuthorize` has already
proven you own. The relationship is approved outright — the supplier decided that when they sent
it — and a draft product is created for everything the invite named, so you land on a dashboard
with stock to sell rather than a setup guide.
Only redeemable by an account on the address the invite was sent to.
Requires the `stores.reselling.update` permission.
# Add Funds
Source: https://docs.komerza.com/api-reference/endpoint/reselling/add-funds
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/topup
Creates an order on the supplier's store that credits your balance with them once paid. Drawing
stock and buying it in bulk both spend that balance, so this is what makes either possible.
The account credited is the one this store's relationship is held under, not whoever is
signed in — a staff member topping up must not end up crediting themselves.
Requires the `stores.reselling.update` permission.
# Apply to Supplier
Source: https://docs.komerza.com/api-reference/endpoint/reselling/apply
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}
Asks to resell a store's stock. A customer account on that store is created from the caller's
platform email, so there is nothing to sign up for separately. Stores that enrol openly approve
immediately; stores that take applications leave this pending.
Requires the `stores.reselling.update` permission.
# Buy Basket
Source: https://docs.komerza.com/api-reference/endpoint/reselling/buy-basket
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/basket
The basket is priced once and each line is then bought at its share of that price, landing on
your own products as ordinary stock exactly as a single-variant purchase would. Short deliveries
are refunded the same way.
Supply an idempotency key. Lines carry it with their own index appended, so a retried basket
replays line for line rather than buying twice.
Requires the `stores.reselling.update` permission.
# Buy Stock
Source: https://docs.komerza.com/api-reference/endpoint/reselling/buy-stock
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/stock
Buys units up front, at whatever volume price the supplier offers for that quantity, and puts
them on your own product as ordinary stock. Nothing is held or pending: the units are yours, they
sell through your normal delivery, and nothing the supplier does afterwards takes them away.
If the supplier delivers fewer than were bought, the difference is refunded to your balance
rather than quietly kept.
Requires the `stores.reselling.update` permission.
# Create Listing
Source: https://docs.komerza.com/api-reference/endpoint/reselling/create-listing
PUT /stores/{storeId}/reselling/suppliers/{supplierStoreId}/listings/{sourceVariantId}
Points one of this store's variants at a supplier's variant. Selling it then draws a unit from
the supplier automatically — no delivery configuration of any kind is needed on this side.
Requires the `stores.reselling.update` permission.
# Delete Listing
Source: https://docs.komerza.com/api-reference/endpoint/reselling/delete-listing
DELETE /stores/{storeId}/reselling/suppliers/{supplierStoreId}/listings/{sourceVariantId}
The store's own product keeps existing and keeps selling; it just no longer draws from the
supplier, which for a purely resold product means it can no longer be delivered. Worth
unpublishing it at the same time.
Requires the `stores.reselling.update` permission.
# Find Supplier
Source: https://docs.komerza.com/api-reference/endpoint/reselling/find-supplier
GET /stores/{storeId}/reselling/suppliers/lookup
There is no supplier directory, so this is how you reach a store you already know of: paste its
address.
Only stores currently taking applications are found. One running an invite-only programme, or
none at all, comes back as not found — which store has a reseller programme is not something
this should let anyone map out.
Requires the `stores.reselling.view` permission.
# Get Bundles
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-bundles
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/bundles
Baskets the supplier composed and priced as one thing, each quoted for this store: what is in
it, what that comes to at their list prices, the discount, and what you would pay. Bundles aimed
at other resellers are not listed, and cannot be bought by guessing an id.
Requires the `stores.reselling.view` permission.
# Get Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-catalogue
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/catalogue
Priced as it will actually cost this store, with whichever of its own products is already
sourced from each.
Requires the `stores.reselling.view` permission.
# Get Draws
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-draws
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/draws
One entry per draw, newest first, each quoting the supplier's own order id so it can be chased
upstream.
Requires the `stores.reselling.view` permission.
# Get Linkable Accounts
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-linkable
GET /stores/{storeId}/reselling/linkable
Relationships created by signing up on a supplier's own storefront, before this store existed or
before you thought to use it. They have no store attached, so none of the reselling here applies
to them and the supplier's panel keeps pointing you back at a dashboard that cannot see them.
Matched on your confirmed email address.
Requires the `stores.reselling.view` permission.
# Get Payment Methods
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-payment-methods
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/payment-methods
The supplier's own payment methods, since topping up is paid to them rather than to Komerza.
Only readable by a store that actually holds a relationship with them — a merchant's enabled
gateways are not public information.
Requires the `stores.reselling.view` permission.
# Get Resold Products
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-products
GET /stores/{storeId}/reselling/products
Every product of this store drawn from a supplier, with its cost, margin, and whether it would
currently sell. A resold product can stop being deliverable without its owner touching anything —
the supplier withdraws it, revokes them, raises the price past their ceiling, or runs out — so
each carries the reason it is blocked, if it is.
Requires the `stores.reselling.view` permission.
# Get Suppliers
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-suppliers
GET /stores/{storeId}/reselling/suppliers
Every store this one draws stock from, with what it can currently spend at each.
Requires the `stores.reselling.view` permission.
# Link Account
Source: https://docs.komerza.com/api-reference/endpoint/reselling/link-account
POST /stores/{storeId}/reselling/linkable/{resellerId}
Attaches an off-platform reseller relationship to this store. From then on its draws are served
in-process — stock lands on your own variants, there is no delivery URL to hold, and the balance
already held with that supplier comes across untouched.
Requires the `stores.reselling.update` permission.
# Quote Basket
Source: https://docs.komerza.com/api-reference/endpoint/reselling/quote-basket
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/basket/quote
Give it a bundle id, or lines of your own, and it comes back with the same arithmetic the
purchase will use — including what those lines would have cost bought separately, so a discount
you already have is not counted twice in what you tell yourself you saved.
Requires the `stores.reselling.view` permission.
# Get Review Analytics
Source: https://docs.komerza.com/api-reference/endpoint/reviews/analytics
GET /stores/{storeId}/reviews/analytics
Gets the review analytics for a store.
Requires the `stores.view` permission.
# Get Reviews
Source: https://docs.komerza.com/api-reference/endpoint/reviews/get-list
GET /stores/{storeId}/reviews
Gets the current and previous reviews for a store.
Requires the `stores.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| --------------- | -------- | ---------------------------------------------------- |
| `rating` | integer | Filter by star rating (1-5) |
| `emailAddress` | string | Filter by customer email (case-insensitive contains) |
| `hasReply` | boolean | Filter reviews that have a merchant reply |
| `dateRangeFrom` | datetime | Filter reviews created on or after this date |
| `dateRangeTo` | datetime | Filter reviews created on or before this date |
**Example:**
```bash theme={null}
# 5-star reviews from last 30 days
GET /stores/{storeId}/reviews?filters=rating==5,dateRangeFrom>=2025-10-28
# Reviews without replies, sorted by newest first
GET /stores/{storeId}/reviews?filters=hasReply==false&sorts=-dateRangeFrom
```
# Reply to Review
Source: https://docs.komerza.com/api-reference/endpoint/reviews/reply
PUT /stores/{storeId}/reviews/{reviewId}/reply
Requires the `stores.update` permission.
Replies are **public**. Whatever you send here appears under the review on
your storefront, next to the customer's original words.
## How it works
A review holds **one** reply, stored on the review itself rather than as a thread. That shapes the whole endpoint:
* **`PUT` overwrites.** Call it again on the same review and you replace the previous reply - this is also how you edit one. There is no separate update endpoint.
* **There is no delete.** `reply` is required and must be at least 8 characters, so you cannot blank it out to remove a reply. Replace it with better wording instead.
## Request
```json theme={null}
{
"reply": "Sorry this landed late - your key was reissued the same day and we've since fixed the delay."
}
```
| Field | Rules |
| ------- | ------------------------------------------- |
| `reply` | Required. Between **8 and 4096** characters |
## Permission
Requires **`stores.update`**, not a review-specific scope. A key that can reply to reviews can also change other store settings, so scope keys with that in mind.
## Finding what to reply to
Use [Get Reviews](/api-reference/endpoint/reviews/get-list) with `hasReply==false` to fetch the ones still waiting, worst first:
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/reviews" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=hasReply==false,rating<3" \
--data-urlencode "sorts=-dateRangeFrom"
```
A reply is read by far more future buyers than by the reviewer. Answer the
question the next shopper is silently asking - what went wrong, and what you
did about it.
# Add Ticket Response
Source: https://docs.komerza.com/api-reference/endpoint/tickets/add
PUT /stores/{storeId}/tickets/{ticketId}
Adds a response/reply to the given ticket.
Requires the `stores.tickets.update` permission.
# Close Ticket
Source: https://docs.komerza.com/api-reference/endpoint/tickets/delete
DELETE /stores/{storeId}/tickets/{ticketId}
Closes the given ticket.
Requires the `stores.tickets.delete` permission.
**Closed and Resolved are final states.** Neither you nor the customer can
reopen a closed ticket - they have to open a new one, losing the thread's
context. Reply instead if the conversation is still live.
# Get Tickets
Source: https://docs.komerza.com/api-reference/endpoint/tickets/get-list
GET /stores/{storeId}/tickets
Gets the tickets in the store.
Requires the `stores.tickets.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| --------------- | -------- | -------------------------------------------------------------------------------- |
| `subject` | string | Search ticket subject (case-insensitive contains) |
| `status` | integer | Filter by ticket status. See [Ticket Status](/api-reference/enums#ticket-status) |
| `customerEmail` | string | Filter by customer email (case-insensitive contains) |
| `dateFrom` | datetime | Filter tickets created on or after this date |
| `dateTo` | datetime | Filter tickets created on or before this date |
**Example:**
```bash theme={null}
# Open tickets from last 7 days
GET /stores/{storeId}/tickets?filters=status==0,dateFrom>=2025-11-20
# Tickets by customer email
GET /stores/{storeId}/tickets?filters=customerEmail@=*user@example.com
```
# Create Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/create
POST /webhooks
Creates a webhook that POSTs the events you subscribe to, for the stores you choose, as signed
JSON. The signing secret is returned once here, so save it to verify the X-Signature header.
Requires the `user.webhooks.create` permission.
**The signing secret is returned once, here, and never again.** There is no
endpoint or dashboard screen that will show it later. Store it when you
receive it; if you lose it, your only option is to delete the webhook and
create a new one.
You can hold up to **25 webhooks per user**, each scoped to one or more of
your stores. URLs must be HTTP or HTTPS, and any that resolve to private or
reserved addresses are rejected.
# Delete Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/delete
DELETE /webhooks/{webhookId}
Removes the webhook and all associated logs from the user account.
Requires the `user.webhooks.delete` permission.
**Deleting a webhook deletes all of its execution logs**, so any delivery
history you would need for debugging goes with it. Export anything you want to
keep first.
# Get Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/get
GET /webhooks/{webhookId}
Retrieves the webhook if it belongs to the currently authenticated user.
Requires the `user.webhooks.view` permission.
# Get Webhooks
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/get-list
GET /webhooks
Returns a list of all account-wide webhooks configured by the user.
Requires the `user.webhooks.view` permission.
# Get Webhook Logs
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/get-logs
GET /webhooks/{webhookId}/logs
Returns a webhook's delivery history: one entry per attempt, with the response code and, on
failures, the reason.
Supports filtering by response code and success/failure status via query parameters.
Requires the `user.webhooks.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| -------------- | ------- | --------------------------------------------------------------------------------- |
| `success` | boolean | Whether the delivery succeeded (a 2xx response) |
| `responseCode` | integer | The HTTP status your endpoint returned. `-1` means paused after repeated failures |
**Example:**
```bash theme={null}
# Failed deliveries only
GET /webhooks/{webhookId}/logs?filters=success==false
# Deliveries your endpoint rejected as unauthorised
GET /webhooks/{webhookId}/logs?filters=responseCode==401
```
# Resend Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/resend
POST /webhooks/{webhookId}/logs/{executionLogId}/resend
Re-queues the Hangfire job for a particular webhook execution log. If the job no longer exists or the user
does not own the log, it fails. This is a single execution log re-send.
Requires the `user.webhooks.update` permission.
# Search Webhook Logs
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/search-logs
GET /webhooks/{webhookId}/logs/search/{query}
Searches logs by log ID, order ID (if the query is a valid GUID), or by URL fragment.
All pagination and date/status filters from the listing endpoint are also supported.
Requires the `user.webhooks.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| -------------- | ------- | --------------------------------------------------------------------------------- |
| `success` | boolean | Whether the delivery succeeded (a 2xx response) |
| `responseCode` | integer | The HTTP status your endpoint returned. `-1` means paused after repeated failures |
**Example:**
```bash theme={null}
# Failed deliveries only
GET /webhooks/{webhookId}/logs?filters=success==false
# Deliveries your endpoint rejected as unauthorised
GET /webhooks/{webhookId}/logs?filters=responseCode==401
```
# Update Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/update
PUT /webhooks/{webhookId}
Overwrites the URL, events, and store IDs.
Requires the `user.webhooks.update` permission.
# Authentication
Source: https://docs.komerza.com/api-reference/authentication
API keys, required headers and scopes
Every endpoint requires an API key, sent as a bearer token.
```bash theme={null}
curl "https://api.komerza.com/stores/{storeId}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0"
```
## Creating a key
1. Open **[Account Settings → API Keys](https://dashboard.komerza.com/account?api)** in the dashboard
2. Click **Create New API Key** and name it after the integration that will use it
3. Select its scopes
4. Copy the key
The key is shown once and can't be retrieved later. Keep it in an environment
variable or a secrets manager, never in source control.
## Required headers
| Header | Required | Value |
| --------------- | --------- | ------------------------------------------------ |
| `Authorization` | Yes | `Bearer YOUR_API_KEY` |
| `User-Agent` | Yes | Something identifying your app, e.g. `MyApp/1.0` |
| `Content-Type` | On writes | `application/json` |
## Scopes
Each key carries a fixed set of scopes, chosen when you create it. They decide which endpoints it can reach: `stores.orders.view` to read orders, `stores.orders.refund` to refund them, and so on.
Scopes can't be edited afterwards. To change what a key can do, create a new one and retire the old.
Two habits worth adopting from the start:
* **One key per integration.** You can then revoke a single integration without breaking everything else.
* **Only the scopes it needs.** A reporting script has no business holding `stores.orders.refund`.
} href="/api-reference/scopes">
Every permission a key can carry, and what each one unlocks.
## When authentication fails
| Status | Meaning | Fix |
| ------ | --------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `401` | Key missing, malformed or revoked | Check the `Bearer ` prefix is present and the key still exists |
| `403` | Key is valid but lacks the scope | Scopes follow the resource, e.g. orders need `stores.orders.*`. Create a new key with the right ones |
Both return the standard error envelope, with `code` set to `AccessDenied`. See [Responses & Errors](/api-reference/responses).
# Conventions
Source: https://docs.komerza.com/api-reference/conventions
IDs, dates, enums, money, store scoping and paging
These hold across every endpoint. Knowing them up front saves a lot of trial and error.
## Base URL
```
https://api.komerza.com
```
There is **no `/api` prefix**. Paths start straight at the resource:
```bash theme={null}
https://api.komerza.com/stores/{storeId}/orders # correct
https://api.komerza.com/api/stores/{storeId}/orders # 404
```
## Store scoping
Nearly every path begins `/stores/{storeId}/…`, because almost everything belongs to a store rather than to your account. `GET /user` returns your account with its stores, and each store's `id` is the `{storeId}` used everywhere else.
An account can hold several stores, so treat the store ID as a required piece of configuration in your integration, not something to hardcode once and forget.
## IDs
UUIDs, in the standard hyphenated form:
```
550e8400-e29b-41d4-a716-446655440000
```
Stores, products, variants, orders, customers and coupons all use them. They're opaque - don't parse or generate them.
## Dates
ISO 8601, in UTC:
```
2026-01-15T10:30:00Z
```
Filters accept either a full timestamp or just the date, e.g. `dateFrom>=2026-01-15`.
## Enums
Sent and returned as **integers**, not names. A product's visibility is `0`, not `"Public"`.
```json theme={null}
{ "privacy": 0, "stockCalculationMode": 2 }
```
} href="/api-reference/enums">
Every enum value the API accepts and returns.
Order status and payment gateway are the exceptions - both are strings, documented on [Order Status](/api-reference/payment-status) and [Payment Methods](/api-reference/payment-methods).
## Money
Decimal values in the store's own currency, with `currencyCode` on the store telling you which. There's no minor-unit convention to decode: `29.99` means 29.99.
Amounts a buyer paid can differ from the order total on split payments, and crypto orders carry the amount actually received - check both rather than assuming.
## Paging
| Parameter | Default | Notes |
| ---------- | ------- | ---------------------------------------------------- |
| `page` | `1` | 1-indexed |
| `pageSize` | `20` | Maximum 100; larger values are clamped, not rejected |
List endpoints also take `filters` and `sorts`.
} href="/api-reference/filtering">
Operators, combining conditions, sorting and paging.
## Fair use
Requests are rate limited. On a `429`, back off and retry with increasing delays, and cache anything that doesn't change often.
If you're polling for order updates, use [webhooks](/guides/webhooks) instead. They're signed, logged and retried, and they'll reach you sooner than any poll interval you'd be comfortable running.
# Create Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/create
POST /stores/{storeId}/categories
Creates a new category in the store.
Requires the `stores.categories.create` permission.
# Delete Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/delete
DELETE /stores/{storeId}/categories/{categoryId}
Deletes an existing category from the store.
Requires the `stores.categories.delete` permission.
# Get Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/get
GET /stores/{storeId}/categories/{categoryId}
Retrieves a category by its ID with full product details.
Requires the `stores.categories.view` permission.
# Get All Categories
Source: https://docs.komerza.com/api-reference/endpoint/categories/get-all
GET /stores/{storeId}/categories/all
Retrieves all categories in the store as references (no pagination).
Requires the `stores.categories.view` permission.
# Get Categories
Source: https://docs.komerza.com/api-reference/endpoint/categories/get-list
GET /stores/{storeId}/categories
Gets the categories in the store with pagination and filtering.
Requires the `stores.categories.view` permission.
# Order Categories
Source: https://docs.komerza.com/api-reference/endpoint/categories/order
PATCH /stores/{storeId}/categories/order
Updates the display order of categories.
Requires the `stores.categories.update` permission.
# Order Products in Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/order-products
PATCH /stores/{storeId}/categories/{categoryId}/products/order
Updates the display order of products within a category.
Requires the `stores.categories.update` permission.
# Update Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/update
PUT /stores/{storeId}/categories/{categoryId}
Updates an existing category in the store.
Requires the `stores.categories.update` permission.
# Create Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/create
POST /stores/{storeId}/coupons
Creates a new coupon in the store.
Requires the `stores.coupons.create` permission.
# Delete Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/delete
DELETE /stores/{storeId}/coupons/{couponId}
Deletes an existing coupon from the store.
Requires the `stores.coupons.delete` permission.
# Get Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/get
GET /stores/{storeId}/coupons/{couponId}
Gets a coupon by ID.
Requires the `stores.coupons.view` permission.
# Get All Coupons
Source: https://docs.komerza.com/api-reference/endpoint/coupons/get-all
GET /stores/{storeId}/coupons/all
Gets the coupons in the store as references.
Requires the `stores.coupons.view` permission.
# Get Coupons
Source: https://docs.komerza.com/api-reference/endpoint/coupons/get-list
GET /stores/{storeId}/coupons
Gets the coupons in the store.
Requires the `stores.coupons.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------ |
| `usesFrom` | integer | Filter by minimum number of times coupon has been used |
| `usesTo` | integer | Filter by maximum number of times coupon has been used |
| `maximumUsesFrom` | integer | Filter by minimum maximum uses allowed |
| `maximumUsesTo` | integer | Filter by maximum maximum uses allowed |
| `percentageFrom` | decimal | Filter by minimum percentage discount |
| `percentageTo` | decimal | Filter by maximum percentage discount |
| `amountFrom` | decimal | Filter by minimum amount discount |
| `amountTo` | decimal | Filter by maximum amount discount |
| `isEnabled` | boolean | Filter by whether coupon is enabled |
**Example:**
```bash theme={null}
# Active coupons with 20-50% discount
GET /stores/{storeId}/coupons?filters=isEnabled==true,percentageFrom>=20,percentageTo<=50
# Heavily used coupons
GET /stores/{storeId}/coupons?filters=usesFrom>=100&sorts=-uses
```
# Search Coupons
Source: https://docs.komerza.com/api-reference/endpoint/coupons/search
GET /stores/{storeId}/coupons/search/{query}
Searches the coupons in the store by the given query.
Requires the `stores.coupons.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------ |
| `usesFrom` | integer | Filter by minimum number of times coupon has been used |
| `usesTo` | integer | Filter by maximum number of times coupon has been used |
| `maximumUsesFrom` | integer | Filter by minimum maximum uses allowed |
| `maximumUsesTo` | integer | Filter by maximum maximum uses allowed |
| `percentageFrom` | decimal | Filter by minimum percentage discount |
| `percentageTo` | decimal | Filter by maximum percentage discount |
| `amountFrom` | decimal | Filter by minimum amount discount |
| `amountTo` | decimal | Filter by maximum amount discount |
| `isEnabled` | boolean | Filter by whether coupon is enabled |
**Example:**
```bash theme={null}
# Active coupons with 20-50% discount
GET /stores/{storeId}/coupons?filters=isEnabled==true,percentageFrom>=20,percentageTo<=50
# Heavily used coupons
GET /stores/{storeId}/coupons?filters=usesFrom>=100&sorts=-uses
```
# Update Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/update
PUT /stores/{storeId}/coupons/{couponId}
Requires the `stores.coupons.update` permission.
# Create Customer
Source: https://docs.komerza.com/api-reference/endpoint/customers/create
POST /stores/{storeId}/customers
Registers a customer for the store using their email.
If the customer already exists and is linked to the store, a 409 conflict is returned.
Requires the `stores.customers.create` permission.
# Get Customer
Source: https://docs.komerza.com/api-reference/endpoint/customers/get
GET /stores/{storeId}/customers/{customerId}
Retrieves detailed information for a single customer in the specified store.
Requires the `stores.customers.view` permission.
# Get Customer by Email
Source: https://docs.komerza.com/api-reference/endpoint/customers/get-by-email
GET /stores/{storeId}/customers/{customerEmail}/email
Retrieves detailed information for a single customer in the specified store.
Requires the `stores.customers.view` permission.
# Get Customers
Source: https://docs.komerza.com/api-reference/endpoint/customers/get-list
GET /stores/{storeId}/customers
Retrieves a paginated list of customers for the specified store.
Requires the `stores.customers.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | ------- | ---------------------------------------------------- |
| `email` | string | Filter by email address (case-insensitive contains) |
| `ipAddress` | string | Filter by IP address (case-insensitive contains) |
| `balanceFrom` | decimal | Filter by minimum balance (inclusive) |
| `balanceTo` | decimal | Filter by maximum balance (inclusive) |
| `stripeCustomerId` | string | Filter by Stripe customer ID (exact match) |
| `affiliateCode` | string | Filter by affiliate code (case-insensitive contains) |
**Example:**
```bash theme={null}
# Customers with balance between $10 and $100
GET /stores/{storeId}/customers?filters=balanceFrom>=10,balanceTo<=100
# Customers by affiliate code
GET /stores/{storeId}/customers?filters=affiliateCode@=*PROMO
```
# Search Customers
Source: https://docs.komerza.com/api-reference/endpoint/customers/search
GET /stores/{storeId}/customers/search/{query}
Retrieves a paginated list of customers for the specified store, matching the provided query.
Requires the `stores.customers.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | ------- | ---------------------------------------------------- |
| `email` | string | Filter by email address (case-insensitive contains) |
| `ipAddress` | string | Filter by IP address (case-insensitive contains) |
| `balanceFrom` | decimal | Filter by minimum balance (inclusive) |
| `balanceTo` | decimal | Filter by maximum balance (inclusive) |
| `stripeCustomerId` | string | Filter by Stripe customer ID (exact match) |
| `affiliateCode` | string | Filter by affiliate code (case-insensitive contains) |
**Example:**
```bash theme={null}
# Customers with balance between $10 and $100
GET /stores/{storeId}/customers?filters=balanceFrom>=10,balanceTo<=100
# Customers by affiliate code
GET /stores/{storeId}/customers?filters=affiliateCode@=*PROMO
```
# Update Customer Balance
Source: https://docs.komerza.com/api-reference/endpoint/customers/update-balance
PATCH /stores/{storeId}/customers/{customerId}/balance
Updates the store-specific balance for a customer.
Requires the `stores.customers.update` permission.
**Store credit is real spendable value.** The balance applies immediately at
your checkout and never expires, so treat this like issuing money.
It is not cashable - customers cannot withdraw it - and a top-up order can
never be refunded to balance, since that would credit them twice.
# Create Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/create
POST /stores/{storeId}/orders
Creates a new order for a customer in the admin dashboard.
Requires the `stores.orders.create` permission.
## Payment Methods
When creating an order, specify the `paymentMethod` field using one of the supported payment method codes. See the [Payment Methods reference](/api-reference/payment-methods) for the complete list of available options.
## Redirecting to Checkout
After successfully creating an order, redirect the customer to the hosted checkout page to complete payment:
```
https://checkout.komerza.com/checkout/{orderId}
```
### Example
```javascript theme={null}
const response = await fetch(`/stores/${storeId}/orders`, {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}` },
body: JSON.stringify(orderData),
});
const result = await response.json();
if (result.success) {
const orderId = result.data.id;
window.location.href = `https://checkout.komerza.com/checkout/${orderId}`;
}
```
The checkout page handles payment processing, order confirmation, and receipt
generation automatically.
# Deliver Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/deliver
PUT /stores/{storeId}/orders/{orderId}/deliver
Forces delivery of an existing order as if purchased normally.
No action is taken if the order is already delivered.
Requires the `stores.orders.deliver` permission.
**This does not verify that you were paid.** It runs the full delivery flow as
though payment had arrived: stock is consumed, license keys leave your
inventory, the receipt email is sent, `order.completed` fires and Discord roles
are granted.
Confirm the payment first. For custom payment methods, use [Approve Custom
Payment](/api-reference/endpoint/orders/approve-custom-payment) instead, which
is the reviewed path.
Manually delivered orders can only ever be refunded to **customer balance**,
never back to the gateway.
# Get Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/get
GET /stores/{storeId}/orders/{orderId}
Retrieve order details by ID in the given store.
Requires the `stores.orders.view` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
# Get Orders
Source: https://docs.komerza.com/api-reference/endpoint/orders/get-list
GET /stores/{storeId}/orders
Gets orders in the specified store and optionally filters by the given criteria.
Requires the `stores.orders.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ---------- | -------- | -------------------------------------------------------------------------------- |
| `dateFrom` | datetime | Filter orders created on or after this date |
| `dateTo` | datetime | Filter orders created on or before this date |
| `status` | string | Filter by order status. See [Order Statuses](/api-reference/payment-status) |
| `gateway` | string | Filter by payment gateway. See [Payment Methods](/api-reference/payment-methods) |
| `email` | string | Filter by customer email address |
**Example:**
```bash theme={null}
# Orders from last 30 days with "pending" status
GET /stores/{storeId}/orders?filters=dateFrom>=2025-10-28,status==pending
# Orders paid via Stripe, sorted by newest first
GET /stores/{storeId}/orders?filters=gateway==stripe&sorts=-dateFrom
```
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
# Refund Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/refund
DELETE /stores/{storeId}/orders/{orderId}/refund
Refunds a delivered order for a customer in the admin dashboard.
Requires the `stores.orders.refund` permission.
**Refunds move real money and cannot be undone.** Only **Delivered** orders can
be refunded, always for the **full amount** - there are no partial refunds.
A successful refund also reverses any affiliate commission and the Buyer
Protection accrual on that order.
For cryptocurrency, custom payment methods, Cash App and PayPal Friends &
Family, marking an order refunded **moves no money** - you must return the
funds yourself. Those methods can only refund to customer balance.
# Search Orders
Source: https://docs.komerza.com/api-reference/endpoint/orders/search
GET /stores/{storeId}/orders/search/{query}
Search orders by ID or customer email in the specified store.
Requires the `stores.orders.view` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ---------- | -------- | -------------------------------------------------------------------------------- |
| `dateFrom` | datetime | Filter orders created on or after this date |
| `dateTo` | datetime | Filter orders created on or before this date |
| `status` | string | Filter by order status. See [Order Statuses](/api-reference/payment-status) |
| `gateway` | string | Filter by payment gateway. See [Payment Methods](/api-reference/payment-methods) |
| `email` | string | Filter by customer email address |
**Example:**
```bash theme={null}
# Orders from last 30 days with "pending" status
GET /stores/{storeId}/orders?filters=dateFrom>=2025-10-28,status==pending
# Orders paid via Stripe, sorted by newest first
GET /stores/{storeId}/orders?filters=gateway==stripe&sorts=-dateFrom
```
# Create Product
Source: https://docs.komerza.com/api-reference/endpoint/products/create
POST /stores/{storeId}/products
Creates a new product in the store.
Requires the `stores.products.create` permission.
# Delete Product
Source: https://docs.komerza.com/api-reference/endpoint/products/delete
DELETE /stores/{storeId}/products/{productId}
Deletes a product from the store.
Requires the `stores.products.delete` permission.
**Deleting a product takes its reviews with it.** Variants, custom fields,
volume discounts and images go too, and none of it can be restored.
Files already delivered to customers are kept, so past buyers keep their
downloads. If you only want a product off the storefront, set its privacy to
Private or On Hold instead of deleting it.
# Bulk Delete Products
Source: https://docs.komerza.com/api-reference/endpoint/products/delete-bulk
DELETE /stores/{storeId}/products
Deletes multiple products in one request. This will delete all variants, files, images, and reviews
associated with each product. Products that don't exist are silently skipped.
Requires the `stores.products.delete` permission.
**Irreversible, across every product in the request.** Each one loses its
variants, custom fields, volume discounts, images and reviews.
Files already delivered to customers are kept. To hide products rather than
destroy them, bulk-update their privacy instead.
# Duplicate Product
Source: https://docs.komerza.com/api-reference/endpoint/products/duplicate
POST /stores/{storeId}/products/{productId}/duplicate
Creates a copy of an existing product.
Requires the `stores.products.create` permission.
# Get Product
Source: https://docs.komerza.com/api-reference/endpoint/products/get
GET /stores/{storeId}/products/{productId}
Retrieves a product by its ID.
Requires the `stores.products.view` permission.
# Get All Products
Source: https://docs.komerza.com/api-reference/endpoint/products/get-all
GET /stores/{storeId}/products/all
Retrieves all products in the store as redacted references (no pagination).
Requires the `stores.products.view` permission.
# Get Products
Source: https://docs.komerza.com/api-reference/endpoint/products/get-list
GET /stores/{storeId}/products
Retrieves products in pages.
Requires the `stores.products.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------------------------------------ |
| `visibility` | integer | Filter by privacy level. See [Product Privacy](/api-reference/enums#product-privacy) |
| `isBestSeller` | boolean | Filter best-seller products |
| `shouldBlockVpns` | boolean | Filter products that block VPNs |
| `lowStock` | boolean | Filter products with low stock availability |
**Example:**
```bash theme={null}
# Public best-sellers with low stock
GET /stores/{storeId}/products?filters=visibility==0,isBestSeller==true,lowStock==true
# Products that don't block VPNs
GET /stores/{storeId}/products?filters=shouldBlockVpns==false
```
# Get Product by Slug
Source: https://docs.komerza.com/api-reference/endpoint/products/get-slug
GET /stores/{storeId}/products/slug/{slug}
Retrieves a product by its slug.
Requires the `stores.products.view` permission.
# Order Products
Source: https://docs.komerza.com/api-reference/endpoint/products/order
PATCH /stores/{storeId}/products/order
Updates the display order of products.
Requires the `stores.products.update` permission.
# Order Product Images
Source: https://docs.komerza.com/api-reference/endpoint/products/order-images
PATCH /stores/{storeId}/products/{productId}/images/order
Updates the display order of images for a product.
Requires the `stores.products.update` permission.
# Search Products
Source: https://docs.komerza.com/api-reference/endpoint/products/search
GET /stores/{storeId}/products/search/{query}
Searches products by query within the store.
Requires the `stores.products.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------------------------------------ |
| `visibility` | integer | Filter by privacy level. See [Product Privacy](/api-reference/enums#product-privacy) |
| `isBestSeller` | boolean | Filter best-seller products |
| `shouldBlockVpns` | boolean | Filter products that block VPNs |
| `lowStock` | boolean | Filter products with low stock availability |
**Example:**
```bash theme={null}
# Public best-sellers with low stock
GET /stores/{storeId}/products?filters=visibility==0,isBestSeller==true,lowStock==true
# Products that don't block VPNs
GET /stores/{storeId}/products?filters=shouldBlockVpns==false
```
# Update Product
Source: https://docs.komerza.com/api-reference/endpoint/products/update
PUT /stores/{storeId}/products/{productId}
Updates basic information of a product.
Requires the `stores.products.update` permission.
# Update Affiliate Discount
Source: https://docs.komerza.com/api-reference/endpoint/products/update-affiliate-discount
PUT /stores/{storeId}/products/{productId}/affiliateDiscount
Updates affiliate discount configuration of a product.
Requires the `stores.products.update` permission.
# Update Product Badges
Source: https://docs.komerza.com/api-reference/endpoint/products/update-badges
PUT /stores/{storeId}/products/{productId}/badges
Replaces every badge on the product at once (bulk set). An empty list clears all badges.
Requires the `stores.products.update` permission.
# Bulk Update Products
Source: https://docs.komerza.com/api-reference/endpoint/products/update-bulk
PUT /stores/{storeId}/products
Updates multiple products in one request.
Requires the `stores.products.update` permission.
# Update Payment Settings
Source: https://docs.komerza.com/api-reference/endpoint/products/update-payment
PUT /stores/{storeId}/products/{productId}/payment
Sets a custom fee structure for a product.
Requires the `stores.products.update` permission.
# Update Product Security
Source: https://docs.komerza.com/api-reference/endpoint/products/update-security
PUT /stores/{storeId}/products/{productId}/security
Updates security information of a product.
Requires the `stores.products.update` permission.
# Create Store
Source: https://docs.komerza.com/api-reference/endpoint/stores/create
POST /stores
Creates a new store for the current user.
Requires the `stores.create` permission.
# Get Store
Source: https://docs.komerza.com/api-reference/endpoint/stores/get
GET /stores/{storeId}
Retrieves the specified store's reference information.
Requires the `stores.view` permission.
# Get Permissions
Source: https://docs.komerza.com/api-reference/endpoint/stores/get-permissions
GET /stores/{storeId}/permissions
Gets the permissions that the currently authenticated user has against the store, along with the payment methods
enabled by the store owner
Requires the `stores.view` permission.
This method can return permissions (otherwise known as scopes), you can learn
more about scopes by clicking here.
# Update Store
Source: https://docs.komerza.com/api-reference/endpoint/stores/update
PUT /stores/{storeId}
Replaces the specified store's data with the information in the form.
Requires the `stores.update` permission.
# Maintenance Mode
Source: https://docs.komerza.com/api-reference/endpoint/stores/update-maintenance
PUT /stores/{storeId}/maintenance
Enables or disables maintenance mode for the specified store.
Requires the `stores.update` permission.
**This takes your storefront offline for shoppers.** They see your message
instead of the store and cannot buy anything until it is switched back off.
## The reason is the switch
There is no `enabled` flag - maintenance is on or off depending on whether `reason` has a value.
| Send | Result |
| --------------------------------- | ---------------------------------------- |
| `{ "reason": "Back at 3pm UTC" }` | Maintenance **on**, showing that message |
| `{ "reason": null }` | Maintenance **off**, store live again |
```bash theme={null}
curl -X PUT "https://api.komerza.com/stores/{storeId}/maintenance" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
-H "Content-Type: application/json" \
-d '{"reason": "Restocking, back at 3pm UTC"}'
```
## Message rules
Up to **1024 characters**, and only letters, digits, spaces and `. , ! ? ' -`.
Colons and slashes are rejected, so **a URL will fail validation** - as will
emoji, `@`, `#`, `&` and brackets.
The message is public, so write it for shoppers.
## Reading the current state
The store object returns `maintenanceReason`; `null` means live.
If the storefront is dark while `maintenanceReason` is `null`, this endpoint
won't fix it - that's an expired plan, an unpaid Buyer Protection invoice, or
a suspended account.
# Add Ticket Response
Source: https://docs.komerza.com/api-reference/endpoint/tickets/add
PUT /stores/{storeId}/tickets/{ticketId}
Adds a response/reply to the given ticket.
Requires the `stores.tickets.update` permission.
# Close Ticket
Source: https://docs.komerza.com/api-reference/endpoint/tickets/delete
DELETE /stores/{storeId}/tickets/{ticketId}
Closes the given ticket.
Requires the `stores.tickets.delete` permission.
**Closed and Resolved are final states.** Neither you nor the customer can
reopen a closed ticket - they have to open a new one, losing the thread's
context. Reply instead if the conversation is still live.
# Get Ticket
Source: https://docs.komerza.com/api-reference/endpoint/tickets/get
GET /stores/{storeId}/tickets/{ticketId}
Gets a ticket by the ID in the given store and returns it.
Requires the `stores.tickets.view` permission.
# Get Tickets
Source: https://docs.komerza.com/api-reference/endpoint/tickets/get-list
GET /stores/{storeId}/tickets
Gets the tickets in the store.
Requires the `stores.tickets.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| --------------- | -------- | -------------------------------------------------------------------------------- |
| `subject` | string | Search ticket subject (case-insensitive contains) |
| `status` | integer | Filter by ticket status. See [Ticket Status](/api-reference/enums#ticket-status) |
| `customerEmail` | string | Filter by customer email (case-insensitive contains) |
| `dateFrom` | datetime | Filter tickets created on or after this date |
| `dateTo` | datetime | Filter tickets created on or before this date |
**Example:**
```bash theme={null}
# Open tickets from last 7 days
GET /stores/{storeId}/tickets?filters=status==0,dateFrom>=2025-11-20
# Tickets by customer email
GET /stores/{storeId}/tickets?filters=customerEmail@=*user@example.com
```
# Get User
Source: https://docs.komerza.com/api-reference/endpoint/users/get
GET /user
Returns the currently authenticated user’s profile data.
Requires the `user.view` permission.
# Add License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/add-items
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/items
Appends delivery items to a variant.
Requires the `stores.products.update` permission.
On a variant using **Calculated** stock, the number of keys loaded here *is*
the stock level - adding keys is how you restock, and an Undeliverable order
can be retried with [Deliver](/api-reference/endpoint/orders/deliver) once
stock is back.
# Clear License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/clear-items
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/items/clear
Removes all items from a variant.
Requires the `stores.products.update` permission.
**This wipes every license key on the variant, permanently.** There is no undo
and no way to recover the keys afterwards - keep your own copy before calling
it.
On a variant using Calculated stock, this also drops its stock to zero, so the
product stops selling immediately.
# Create Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/create
POST /stores/{storeId}/products/{productId}/variants
Adds a new variant to the specified product.
Requires the `stores.products.update` permission.
# Delete Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/delete
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}
Removes the specified variant.
Requires the `stores.products.update` permission.
**Deletes the variant's license keys, custom fields and volume discounts with
it,** and cannot be undone. Any unsold keys loaded on it are gone.
# Remove File from Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/delete-file
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/file/{fileId}
Deletes the delivery file and its link.
Requires the `stores.products.update` permission.
# Delete License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/delete-items
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/items
Deletes specified items from a variant.
Requires the `stores.products.update` permission.
**Removed keys are gone for good** - they leave your inventory and cannot be
restored. On Calculated stock, removing keys lowers the sellable stock
straight away.
# Create Volume Discount
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/create
POST /stores/{storeId}/products/{productId}/variants/{variantId}/discounts
Adds a new volume discount tier to the specified variant. Each tier defines a minimum quantity
and the discount to apply when that quantity is reached.
Requires the `stores.products.update` permission.
# Delete Volume Discount
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/delete
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/discounts/{discountId}
Removes the specified volume discount tier from the variant.
Requires the `stores.products.update` permission.
# Get Volume Discounts
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/get
GET /stores/{storeId}/products/{productId}/variants/{variantId}/discounts
Retrieves all volume discount tiers configured for the specified variant, ordered by quantity threshold.
Requires the `stores.products.update` permission.
# Replace Volume Discounts
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/replace
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/discounts
Replaces all existing volume discount tiers for the variant with a new set.
This is useful for bulk updates where you want to set all tiers at once.
Requires the `stores.products.update` permission.
# Update Volume Discount
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/update
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/discounts/{discountId}
Updates the quantity threshold, discount type, or discount value of an existing volume discount tier.
Requires the `stores.products.update` permission.
# Duplicate Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/duplicate
POST /stores/{storeId}/products/{productId}/variants/{variantId}/duplicate
Creates a copy of an existing variant in the same product.
Requires the `stores.products.update` permission.
# Create Field
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/create
POST /stores/{storeId}/products/{productId}/variants/{variantId}/fields
Adds a new custom field to the specified variant. Options for select/radio fields are managed separately.
Requires the `stores.products.update` permission.
# Delete Field
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/delete
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}
Removes the specified custom field and all its options from the variant.
Requires the `stores.products.update` permission.
# Create Field Option
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/options/create
POST /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}/options
Adds a selectable option under the given custom field.
Requires the `stores.products.update` permission.
# Delete Field Option
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/options/delete
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}/options/{optionId}
Removes the specified option from its parent custom field.
Requires the `stores.products.update` permission.
# Update Field Option
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/options/update
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}/options/{optionId}
Updates the label, value, or display order of an existing option.
Requires the `stores.products.update` permission.
# Update Field
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/update
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}
Updates the label, placeholder, type, requirement, or hint of an existing custom field.
Requires the `stores.products.update` permission.
# Get Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/get
GET /stores/{storeId}/products/{productId}/variants/{variantId}
Retrieves a single variant by ID.
Requires the `stores.products.view` permission.
# Get License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/get-items
GET /stores/{storeId}/products/{productId}/variants/{variantId}/items/{page}
Retrieves files/items delivered by a variant in pages.
Requires the `stores.products.view` permission.
# Order Variants
Source: https://docs.komerza.com/api-reference/endpoint/variants/order
PATCH /stores/{storeId}/products/{productId}/variants/order
Updates the display order of variants.
Requires the `stores.products.update` permission.
# Order Variant Images
Source: https://docs.komerza.com/api-reference/endpoint/variants/order-images
PATCH /stores/{storeId}/products/{productId}/variants/{variantId}/images/order
Sets the display order of images.
Requires the `stores.products.update` permission.
# Search License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/search-items
GET /stores/{storeId}/products/{productId}/variants/{variantId}/items/search/{query}
Searches for license keys in the specified store using fuzzy matching.
Results are ordered by relevance score, with exact matches appearing first,
followed by prefix matches, substring matches, and then similar keys.
Requires the `stores.products.view` permission.
# Update Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/update
PUT /stores/{storeId}/products/{productId}/variants/{variantId}
Modifies a variant’s label, SKU, price, etc.
Requires the `stores.products.update` permission.
# Update Delivery Settings
Source: https://docs.komerza.com/api-reference/endpoint/variants/update-delivery
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/delivery
Configures how the variant is delivered (e.g., file, custom script).
Requires the `stores.products.update` permission.
# Enums & Constants
Source: https://docs.komerza.com/api-reference/enums
Every enum value the API accepts and returns
Most enums are **integers** - send and expect the numeric value, not the name. The names below are for readability only; they never appear in a payload. String enums are marked as such.
Order statuses have their own page: [Order Status](/api-reference/payment-status).
## Products
### Privacy
Controls where a product appears and whether it can be bought.
| Value | Name | Meaning |
| ----- | -------- | ------------------------------------------------------ |
| `0` | Public | Visible on the store page and purchasable |
| `1` | Unlisted | Hidden from the store page, purchasable via direct URL |
| `2` | Private | Hidden entirely, not purchasable |
| `3` | OnHold | Visible but blocked from purchase |
### Product type
| Value | Name | Meaning |
| ----- | ------------ | --------------------------------------------- |
| `0` | OneTime | A single purchase |
| `1` | Subscription | Creates or extends a subscription on delivery |
### Billing interval
Applies to subscription variants.
| Value | Name |
| ----- | ----------------------------- |
| `0` | Weekly |
| `1` | Monthly |
| `2` | Quarterly |
| `3` | Yearly |
| `4` | Custom (interval set in days) |
### Stock calculation mode
| Value | Name | Meaning |
| ----- | ---------- | --------------------------------------------------------------- |
| `0` | Calculated | Stock equals the number of delivery items loaded on the variant |
| `1` | Ignored | Stock is not tracked; never out of stock |
| `2` | Fixed | A number you set and maintain yourself |
### Volume discount type
| Value | Name | Meaning |
| ----- | ----------- | ------------------------ |
| `0` | Percentage | A percentage off |
| `1` | FixedAmount | An amount off per unit |
| `2` | FixedPrice | A replacement unit price |
### Affiliate discount override
Set per product to override the store's default affiliate discount.
| Value | Name | Meaning |
| ----- | ---------- | ---------------------------------------------------------- |
| `0` | UseDefault | Use the store's default discount |
| `1` | Disabled | No customer discount; the affiliate still earns commission |
| `2` | Custom | Use the percentage set on the product |
### Delivery types
**String values**, supplied as an array on a variant. A variant can combine several.
| Value | Delivers |
| --------------- | ---------------------------------------------------------------------- |
| `license_keys` | Keys drawn from the variant's inventory |
| `files` | The uploaded file, via a time-limited download link |
| `dynamic` | Whatever your endpoint returns at delivery time |
| `instructions` | A static message included in the delivery email |
| `custom_fields` | Not a delivery mechanism - collects answers from the buyer at checkout |
`keyauth` still appears in some request descriptions. It was removed from the
product and no longer works - do not send it.
## Orders
### Refund reason
| Value | Name |
| ----- | ------------------- |
| `0` | Fraudulent |
| `1` | RequestedByCustomer |
| `2` | Duplicate |
| `3` | Other |
### Refund destination
| Value | Name | Meaning |
| ----- | --------------- | ----------------------------------- |
| `0` | OriginalMethod | Reverse through the payment gateway |
| `1` | CustomerBalance | Issue store credit instead |
Several payment methods only support `1` - see [Managing Orders](/guides/orders#refunds).
### Custom payment method type
| Value | Name | Meaning |
| ----- | ------------ | -------------------------------------------------- |
| `0` | Instructions | Show the buyer instructions to pay outside Komerza |
| `1` | Redirect | Send the buyer to a URL you specify |
## Subscriptions
### Subscription status
| Value | Name | Meaning |
| ----- | -------- | ------------------------------------------------------ |
| `0` | Active | Paid and current |
| `1` | PastDue | An automatic charge failed; retries are scheduled |
| `2` | Canceled | Ended by the merchant or the customer |
| `3` | Expired | Retries exhausted, or the period ended without renewal |
| `4` | Paused | Resumes automatically at its resume date |
### Cancel mode
| Value | Name | Meaning |
| ----- | ----------- | -------------------------------------------- |
| `0` | AtPeriodEnd | Access continues until the period ends |
| `1` | Immediate | Access is revoked at once, with no proration |
## Discord
### Join mode
| Value | Name | Meaning |
| ----- | -------------------------- | ------------------------------------------------------ |
| `0` | AddAndGrantAfterPayment | Added to the server and granted the role after payment |
| `1` | AddOnLinkGrantAfterPayment | Added when they link Discord; role waits for payment |
### Requirement
| Value | Name | Meaning |
| ----- | -------- | ---------------------------------------- |
| `0` | Required | Cannot check out without linking Discord |
| `1` | Optional | Offered but not enforced |
| `2` | Disabled | Not shown at checkout |
### Revocation behavior
Applied when an order is refunded or charged back.
| Value | Name | Meaning |
| ----- | --------------- | -------------------------------- |
| `0` | RevokeAndRemove | Remove the role and the member |
| `1` | RevokeRoleOnly | Remove the role, keep the member |
| `2` | DoNothing | Leave access in place |
## Customers & support
### Ticket status
| Value | Name | Meaning |
| ----- | ----------------- | ------------------------------------- |
| `0` | Opened | Awaiting the seller |
| `1` | Resolved | Closed, issue sorted. Final |
| `2` | Closed | Closed without resolution. Final |
| `3` | Responded | Seller replied; awaiting the customer |
| `4` | CustomerResponded | Customer replied; awaiting the seller |
The values are not in flow order: a ticket moves `0` → `3` ⇄ `4` → `1` or `2`.
### Customer segment
Used to target email marketing recipients.
| Value | Name |
| ----- | ---------- |
| `0` | All |
| `1` | New |
| `2` | Active |
| `3` | Regulars |
| `4` | Vip |
| `5` | Affiliates |
### Blacklist type
The kind of value a blacklist rule matches on.
| Value | Name |
| ----- | ----------------------- |
| `0` | EmailAddress |
| `1` | EmailDomain |
| `2` | IpAddress |
| `3` | IpCidr |
| `4` | AutonomousSystemNumber |
| `5` | InternetServiceProvider |
| `6` | CountryCode |
| `7` | City |
## Email marketing
### Campaign status
| Value | Name | Meaning |
| ----- | ------------ | --------------------------------------------------- |
| `0` | Draft | Editable, not queued |
| `1` | Scheduled | Queued for a future time; still cancellable |
| `2` | Sending | In progress; cannot be cancelled from the dashboard |
| `3` | Sent | Finished |
| `4` | Cancelled | Cancelled before sending |
| `5` | Failed | The reason is not exposed; contact support |
| `6` | Under review | Held by automated screening before sending |
### Recipient send status
Per-recipient outcome within a campaign.
| Value | Name |
| ----- | ---------- |
| `0` | Queued |
| `1` | Sent |
| `2` | Delivered |
| `3` | Bounced |
| `4` | Complained |
| `5` | Failed |
### Suppression type
| Value | Name | Meaning |
| ----- | ----------- | ------------------------- |
| `0` | Unsubscribe | Removable by the merchant |
| `1` | HardBounce | Permanent |
| `2` | Complaint | Permanent |
## Storefront
### Discount dialog trigger
| Value | Name | Meaning |
| ----- | ---------- | ------------------------------------- |
| `0` | Entrance | Shown when the visitor arrives |
| `1` | ExitIntent | Shown when the visitor moves to leave |
Enums used only by dashboard-internal endpoints - platform billing, passkey
registration, account standing - are deliberately not listed. They are not
part of the public API and can change without notice.
# Error Codes
Source: https://docs.komerza.com/api-reference/error-codes
Complete reference for all API error codes and how to handle them
Every failed request carries a `code`: a stable, machine-readable identifier for what went wrong. This page lists them all.
```json theme={null}
{
"success": false,
"message": "The request contains invalid parameters",
"code": "ValidationError",
"data": null
}
```
For the response envelope itself, validation errors and HTTP status codes, see
[Responses & Errors](/api-reference/responses).
## Error Codes Reference
### Authentication & Authorization
**HTTP Status:** 403 Forbidden
**Description:** You don't have permission to access this resource or perform this action.
**Common causes:**
* API key lacks required scope
* Trying to access another user's resources
* Account restrictions or suspensions
* Store-level permissions insufficient
**How to fix:**
* Check your API key has the necessary [scopes](/api-reference/scopes)
* Verify you're accessing resources you own
* Check account status in dashboard
* Request additional permissions if needed
**Example:**
```json theme={null}
{
"success": false,
"message": "Your API key does not have permission to update products",
"code": "AccessDenied",
"data": null
}
```
**HTTP Status:** 401 Unauthorized
**Description:** The provided authentication token is invalid, expired, or malformed.
**Common causes:**
* Expired API key
* Revoked API key
* Malformed Authorization header
* API key not found
**How to fix:**
* Generate a new API key from dashboard
* Verify Authorization header format: `Bearer YOUR_API_KEY`
* Check for whitespace or special characters in token
* Ensure API key hasn't been revoked
**Example:**
```json theme={null}
{
"success": false,
"message": "The provided API key is invalid or has expired",
"code": "BadToken",
"data": null
}
```
**HTTP Status:** 403 Forbidden
**Description:** Two-factor authentication is required for this action.
**Common causes:**
* Sensitive operation requires 2FA
* Account security policy enforced
* Administrative action attempted
**How to fix:**
* Complete 2FA challenge
* Enable 2FA on your account
* Use session-based authentication for sensitive operations
**Example:**
```json theme={null}
{
"success": false,
"message": "This operation requires two-factor authentication",
"code": "TwoFactorRequired",
"data": null
}
```
**HTTP Status:** 401 Unauthorized
**Description:** OAuth authentication is required for this endpoint.
**Common causes:**
* Endpoint requires user session, not API key
* OAuth flow not completed
* Session expired
**How to fix:**
* Complete OAuth authentication flow
* Use correct authentication method for endpoint
* Check if endpoint supports API key authentication
**Example:**
```json theme={null}
{
"success": false,
"message": "This endpoint requires OAuth authentication",
"code": "OAuthLoginRequired",
"data": null
}
```
### Validation & Input Errors
**HTTP Status:** 400 Bad Request
**Description:** The request contains invalid or missing parameters.
**Common causes:**
* Missing required fields
* Invalid data types
* Values outside allowed ranges
* Invalid format (email, URL, UUID, etc.)
* Business rule violations
**How to fix:**
* Check request body against API documentation
* Validate data types and formats
* Ensure all required fields are present
* Review error details for specific field errors
**Example:**
```json theme={null}
{
"success": false,
"message": "Price must be greater than 0",
"code": "ValidationError",
"data": null
}
```
**HTTP Status:** 400 Bad Request
**Description:** CAPTCHA verification failed.
**Common causes:**
* Invalid CAPTCHA response
* Expired CAPTCHA token
* CAPTCHA not solved
* Bot detection triggered
**How to fix:**
* Request new CAPTCHA challenge
* Ensure user completes CAPTCHA
* Check CAPTCHA token hasn't expired
* Verify CAPTCHA integration is correct
**Example:**
```json theme={null}
{
"success": false,
"message": "CAPTCHA verification failed. Please try again.",
"code": "BadCaptcha",
"data": null
}
```
### Resource Errors
**HTTP Status:** 404 Not Found
**Description:** The requested resource does not exist.
**Common causes:**
* Invalid resource ID
* Resource was deleted
* Typo in endpoint URL
* Resource belongs to different store
**How to fix:**
* Verify resource ID is correct
* Check resource hasn't been deleted
* Ensure you're querying the right store
* Validate endpoint URL
**Example:**
```json theme={null}
{
"success": false,
"message": "Product not found",
"code": "NotFound",
"data": null
}
```
**HTTP Status:** 409 Conflict
**Description:** The operation conflicts with an existing resource.
**Common causes:**
* Duplicate unique field (email, slug, etc.)
* Resource already exists
* Concurrent modification conflict
* Business rule prevents operation
**How to fix:**
* Use unique values for unique fields
* Check if resource already exists
* Implement optimistic locking for concurrent updates
* Review business rules
**Example:**
```json theme={null}
{
"success": false,
"message": "A product with this slug already exists",
"code": "ObjectConflict",
"data": null
}
```
### Rate Limiting
**HTTP Status:** 429 Too Many Requests
**Description:** You have exceeded the API rate limit.
**Common causes:**
* Too many requests in short time period
* Burst limit exceeded
* Account-level rate limit reached
**How to fix:**
* Implement exponential backoff
* Respect `Retry-After` header
* Cache frequently accessed data
* Optimize API calls to reduce frequency
* Contact support for higher limits
**Response headers:**
```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1638360000
Retry-After: 60
```
**Example:**
```json theme={null}
{
"success": false,
"message": "Rate limit exceeded. Please retry after 60 seconds.",
"code": "RateLimited",
"data": null
}
```
### Business Logic Errors
**HTTP Status:** 400 Bad Request
**Description:** The requested product is not available for purchase.
**Common causes:**
* Product out of stock
* Product is private or unlisted
* Product deleted or disabled
* Product not available in customer's region
* Purchase limits exceeded
**How to fix:**
* Check product availability
* Verify product stock levels
* Ensure product is public
* Check regional restrictions
**Example:**
```json theme={null}
{
"success": false,
"message": "This product is currently out of stock",
"code": "ProductUnavailable",
"data": null
}
```
**HTTP Status:** 400 Bad Request
**Description:** Insufficient funds in customer balance.
**Common causes:**
* Customer balance too low
* Attempting to use balance payment method
* Withdrawal amount exceeds available balance
**How to fix:**
* Check customer balance
* Request customer add funds
* Use alternative payment method
* Reduce order amount
**Example:**
```json theme={null}
{
"success": false,
"message": "Insufficient customer balance",
"code": "NotEnoughFunds",
"data": null
}
```
**HTTP Status:** 403 Forbidden
**Description:** Account verification is required before proceeding.
**Common causes:**
* Email not verified
* Identity verification pending
* Payment method requires verification
* Account limits require verification
**How to fix:**
* Complete email verification
* Submit required verification documents
* Verify payment methods
* Contact support for verification status
**Example:**
```json theme={null}
{
"success": false,
"message": "Email verification required",
"code": "VerificationRequired",
"data": null
}
```
### Feature & Upgrade Errors
**HTTP Status:** 403 Forbidden
**Description:** This feature is not available for your account.
**Common causes:**
* Feature disabled for your plan
* Beta feature not enabled
* Regional restrictions
* Feature requires specific integration
**How to fix:**
* Check feature availability for your plan
* Contact support to enable beta features
* Verify regional availability
* Review feature requirements
**Example:**
```json theme={null}
{
"success": false,
"message": "Advanced analytics is not available on your current plan",
"code": "FeatureUnavailable",
"data": null
}
```
**HTTP Status:** 402 Payment Required
**Description:** Your current plan does not support this operation.
**Common causes:**
* Plan limits exceeded
* Feature requires higher plan tier
* Usage quota exhausted
**How to fix:**
* Upgrade to higher plan tier
* Review current plan limits
* Reduce usage to stay within limits
* Contact sales for enterprise options
**Example:**
```json theme={null}
{
"success": false,
"message": "You have reached the maximum number of products for your plan",
"code": "UpgradeRequired",
"data": null
}
```
**HTTP Status:** 400 Bad Request
**Description:** The requested operation is not supported.
**Common causes:**
* Deprecated API version
* Invalid operation combination
* Unsupported payment method
* Unsupported currency
**How to fix:**
* Check API documentation for supported operations
* Use current API version
* Verify operation compatibility
* Check supported payment methods/currencies
**Example:**
```json theme={null}
{
"success": false,
"message": "This payment method is not supported for subscriptions",
"code": "UnsupportedRequest",
"data": null
}
```
### File & Upload Errors
**HTTP Status:** 400 Bad Request
**Description:** File upload failed.
**Common causes:**
* File too large
* Invalid file type
* Corrupted file
* Network interruption
* Storage quota exceeded
**How to fix:**
* Check file size limits
* Verify file type is supported
* Ensure file is not corrupted
* Retry upload with stable connection
* Check storage quota
**Example:**
```json theme={null}
{
"success": false,
"message": "File size exceeds maximum allowed size",
"code": "UploadFailed",
"data": null
}
```
### Server Errors
**HTTP Status:** 500 Internal Server Error
**Description:** An unexpected error occurred on the server.
**Common causes:**
* Server-side bug
* Database connection issue
* External service failure
* Unexpected condition
**How to fix:**
* Retry the request after a short delay
* If problem persists, contact support
* Check [status page](https://status.komerza.com) for incidents
* Include error ID when contacting support
**Example:**
```json theme={null}
{
"success": false,
"message": "An unexpected error occurred",
"code": "InternalServerError",
"data": null
}
```
Always include the `errorId` when reporting issues to support for faster resolution.
## HTTP Status Code Summary
| Status Code | Error Codes | Description |
| ----------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| 400 | ValidationError, BadCaptcha, ProductUnavailable, NotEnoughFunds, UnsupportedRequest, UploadFailed | Bad Request - Invalid input |
| 401 | BadToken, OAuthLoginRequired | Unauthorized - Invalid or missing authentication |
| 402 | UpgradeRequired | Payment Required - Plan upgrade needed |
| 403 | AccessDenied, TwoFactorRequired, VerificationRequired, FeatureUnavailable | Forbidden - Insufficient permissions |
| 404 | NotFound | Not Found - Resource doesn't exist |
| 409 | ObjectConflict | Conflict - Resource conflict |
| 429 | RateLimited | Too Many Requests - Rate limit exceeded |
| 500 | InternalServerError | Internal Server Error - Server-side issue |
## Support Resources
Check API and service status
Browse error troubleshooting guides
Get help from the community
Report issues with error IDs
# Filtering & Sorting
Source: https://docs.komerza.com/api-reference/filtering
Narrow down, order and page through list endpoints
List endpoints return everything by default. Four query parameters let you narrow that down:
| Parameter | Does |
| ---------- | ---------------------------- |
| `filters` | Which items come back |
| `sorts` | What order they come back in |
| `page` | Which page you want |
| `pageSize` | How many per page |
You can use any of them on their own. Endpoints that support them show a **Filterable Fields** table on their reference page - that table is the list of field names you're allowed to use.
## Start with one filter
A filter is three parts stuck together: **field**, **operator**, **value**.
```bash theme={null}
GET /stores/{storeId}/orders?filters=status==pending
```
That's `status` (field) `==` (equals) `pending` (value). Add more by separating with commas - every condition has to match:
```bash theme={null}
# Pending orders, paid by Stripe
GET /stores/{storeId}/orders?filters=status==pending,gateway==stripe
```
Then order them, newest first, with `-` meaning descending:
```bash theme={null}
GET /stores/{storeId}/orders?filters=status==pending&sorts=-dateFrom
```
And take them 50 at a time:
```bash theme={null}
GET /stores/{storeId}/orders?filters=status==pending&sorts=-dateFrom&page=1&pageSize=50
```
That's the whole idea. The rest of this page is the detail.
**A field name that isn't on the endpoint's list is ignored, not rejected.**
You get a `200` with unfiltered results rather than an error. If a filter
seems to do nothing, check the spelling against that endpoint's Filterable
Fields table before looking anywhere else.
## Operators
**Comparing numbers and dates:**
| Operator | Meaning | Example |
| -------- | ------------ | ---------------------- |
| `==` | Equals | `status==pending` |
| `!=` | Not equals | `gateway!=stripe` |
| `>` | Greater than | `rating>3` |
| `<` | Less than | `rating<3` |
| `>=` | At least | `dateFrom>=2026-01-01` |
| `<=` | At most | `dateTo<=2026-06-30` |
**Matching text:**
| Operator | Meaning | Example |
| -------- | ------------------- | ------------------ |
| `@=` | Contains | `email@=gmail` |
| `_=` | Starts with | `subject_=Refund` |
| `_-=` | Ends with | `email_-=.com` |
| `!@=` | Does not contain | `email!@=test` |
| `!_=` | Does not start with | `subject!_=[spam]` |
| `!_-=` | Does not end with | `email!_-=.ru` |
Text matching is case-sensitive. Add a `*` to the end of any operator to ignore case:
```bash theme={null}
?filters=subject@=Premium # matches "Premium", not "premium"
?filters=subject@=*premium # matches both
```
Field names are always case-insensitive, so `dateFrom` and `datefrom` both work.
## Matching several values
Separate values with `|` to match any of them:
```bash theme={null}
# Orders paid by Stripe or PayPal
?filters=gateway==stripe|paypal
```
Put several **fields** in brackets to run the same test against each, matching if any one hits:
```bash theme={null}
# "urgent" in either the subject or the customer's email
?filters=(subject|customerEmail)@=urgent
```
## Values that need care
Enum fields take their numeric value: `visibility==0`, **not**
`visibility==Public`. Passing the name matches nothing and returns an empty
list. Every value is in [Enums & Constants](/api-reference/enums).
Order status and gateway are the exceptions - they take the string name, as
their field tables show.
`dateFrom>=2026-01-01`, or with a time `dateFrom>=2026-01-01T09:30:00Z`.
`isBestSeller==true`. Anything that isn't recognisably a boolean counts as
`false`.
These mean something to the parser, so escape them with a backslash:
| To match | Write |
| --------------- | -------------------- |
| `some,value` | `field@=some\,value` |
| `some\|value` | `field@=some\|value` |
| the text "null" | `field@=\null` |
Unescaped, `field@=some,value` is read as two separate filters.
`!=` leaves out rows where the field is empty. `gateway!=stripe` returns
orders paid another way, but not orders with no gateway at all.
## Two shortcut filters
These aren't real fields, they're prepared questions. Pass `true`; passing `false` is the same as leaving them out.
| Field | On | Matches |
| ---------- | -------- | ----------------------------------------------------------- |
| `lowStock` | Products | Has a license-key variant and fewer than 5 in stock overall |
| `hasReply` | Reviews | Already has a merchant reply |
## Sorting
One field, or several separated by commas. The first is the main order, the rest break ties. `-` means descending.
```bash theme={null}
?sorts=-dateFrom # newest first
?sorts=status,-dateFrom # by status, newest first inside each status
```
When paging through results, sort by something nearly unique. If lots of rows
share the same value, their order can shift between requests, so you may see
the same row twice or miss one entirely.
## Paging
`page` starts at 1. `pageSize` defaults to **20** and is capped at **100** - ask for 500 and you get 100, with no error.
Responses tell you how many pages there are:
```json theme={null}
{
"success": true,
"pages": 5,
"data": [
// items
]
}
```
There's no total item count, so keep requesting pages until you reach `pages`.
A few endpoints use an older style, with the page in the path and a `limit` or
`count` parameter:
```
GET /stores/{storeId}/products/{productId}/variants/{variantId}/items/2?count=50
```
Their reference pages show what they accept.
## Recipes
Real requests you can paste and edit. `curl -G` with `--data-urlencode` matters here: filter values are full of `=`, `>`, `|` and `*`, and letting curl encode them avoids the request being mangled on the way out.
**Last 30 days of pending orders, newest first**
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/orders" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=status==pending,dateFrom>=2026-01-02" \
--data-urlencode "sorts=-dateFrom"
```
**Unanswered 1-2 star reviews**
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/reviews" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=rating<3,hasReply==false" \
--data-urlencode "sorts=-dateRangeFrom"
```
**Open tickets from one customer, oldest first**
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/tickets" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=status==0,customerEmail@=*buyer@example.com" \
--data-urlencode "sorts=dateCreated" \
--data-urlencode "pageSize=50"
```
**Public best-sellers running low on stock**
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/products" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=visibility==0,isBestSeller==true,lowStock==true"
```
## When it doesn't work
| What you see | What's wrong |
| --------------------------------- | ---------------------------------------------------------------------------------------------- |
| The filter changes nothing | The field isn't on that endpoint's list, or is misspelled. Unknown fields are ignored silently |
| Empty list when filtering an enum | You passed the name instead of the number - use `visibility==0` |
| One value became two filters | It contains a comma; escape it as `\,` |
| `pageSize=500` returned 100 items | 100 is the maximum, and larger values are quietly clamped |
| Results shuffle between pages | Equal sort values have no guaranteed order - add a tie-breaker field |
# Introduction
Source: https://docs.komerza.com/api-reference/introduction
The Komerza REST API: what it covers and where to start
The Komerza API is a REST interface to everything in your store. Anything you can do in the dashboard, you can automate: manage your catalogue, read and fulfil orders, issue refunds, look after customers, run coupons and affiliates, and receive events on your own server.
```
https://api.komerza.com
```
} href="/api-reference/quickstart">
Create a key, find your store ID and get a real response back, in three steps.
## What you can build
}>
Sync products, variants, pricing and stock from wherever you already manage
them.
}>
Read orders, deliver them, issue refunds, and push status into your own
systems.
}>
Drive checkout from a site you build yourself, with your own design.
}>
Feed orders and customers into accounting, CRM, fulfilment or analytics.
## Start here
} href="/api-reference/authentication">
API keys, required headers and scopes.
} href="/api-reference/responses">
The response envelope, list responses and how failures look.
} href="/api-reference/conventions">
IDs, dates, enums, money and store scoping.
} href="/api-reference/filtering">
Narrow, order and page through list endpoints.
## Related
} href="/api-reference/sdks">
Official libraries for .NET, JavaScript, Python, Go and PHP.
} href="/guides/webhooks">
Order events pushed to your server, instead of polling for them.
} href="/storefront/introduction">
Cart and checkout on a site you host yourself.
} href="https://status.komerza.com">
Live API availability.
# Exchange Code
Source: https://docs.komerza.com/api-reference/oauth2/exchange
GET /oauth2/{appId}/exchange/{code}
## Overview
Exchange an authorization code for a JWT API key. This is the final step in the OAuth2 flow, converting the temporary code into a permanent access token.
This endpoint requires the `X-OAuth2-Client-Secret` header for authentication instead
of the standard `Authorization` header.
## Path Parameters
| Parameter | Type | Description |
| --------- | ------ | ------------------------------------------------- |
| `appId` | string | Your application's Client ID |
| `code` | string | The authorization code received from the callback |
## Headers
| Header | Required | Description |
| ------------------------ | -------- | --------------------------------------------- |
| `X-OAuth2-Client-Secret` | Yes | Your application's 72-character client secret |
| `User-Agent` | Yes | Your application identifier |
**Server-Side Only**: This request must be made from your backend server,
never from client-side code. The client secret must remain confidential.
## Response
```json theme={null}
{
"success": true,
"data": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```
The `data` field contains a JWT API key that you can use to make authenticated API requests.
## Using the API Key
Include the JWT as a Bearer token in subsequent API requests:
```bash theme={null}
curl -X GET https://api.komerza.com/stores/STORE_ID \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "User-Agent: MyApp/1.0"
```
## Token Properties
The JWT API key:
* **Grants access** only to the stores the user authorized
* **Has permissions** limited to the scopes you requested and the user approved
* **Does not expire** until the user revokes authorization
* **Is tied** to the specific user and application
## Error Responses
| Error | Description |
| ----------------------- | ----------------------------------------------------------------- |
| `invalid_code` | The authorization code is invalid or has already been used |
| `code_expired` | The authorization code has expired (codes are valid for 1 minute) |
| `invalid_client_secret` | The client secret is incorrect |
| `app_not_found` | The application ID is invalid |
## Complete Flow Example
```javascript theme={null}
// 1. User clicks "Connect with Komerza" in your app
const authUrl = `https://dashboard.komerza.com/auth/authorize?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&state=${randomState}`;
window.location.href = authUrl;
// 2. User authorizes on Komerza, redirected back to your app
// URL: https://myapp.com/callback?code=ENCRYPTED_CODE&state=randomState
// 3. Your backend exchanges the code for an API key
const response = await fetch(
`https://api.komerza.com/oauth2/${CLIENT_ID}/exchange/${code}`,
{
headers: {
"X-OAuth2-Client-Secret": process.env.CLIENT_SECRET,
"User-Agent": "MyApp/1.0",
},
}
);
const { data: apiKey } = await response.json();
// 4. Store the API key securely and use it for API calls
const stores = await fetch("https://api.komerza.com/stores", {
headers: {
Authorization: `Bearer ${apiKey}`,
"User-Agent": "MyApp/1.0",
},
});
```
# OAuth2 Integration
Source: https://docs.komerza.com/api-reference/oauth2/introduction
Allow third-party applications to access user accounts with their consent
## Overview
Komerza's OAuth2 system enables third-party developers to create applications that can securely access user accounts with explicit user consent. This allows you to build integrations, plugins, and services that interact with Komerza stores on behalf of users.
Register your OAuth2 application in the Komerza Dashboard
## How It Works
Register your application with Komerza, specifying the permissions you need
and your redirect URLs.
Redirect users to Komerza's authorization page where they can review and
approve your app's access.
After approval, users are redirected back to your app with a temporary
authorization code.
Exchange the authorization code for a JWT API key that grants access to the
authorized stores.
## Authorization URL
Send users to the following URL to initiate the OAuth2 flow:
```
https://dashboard.komerza.com/auth/authorize
```
### Query Parameters
| Parameter | Required | Description |
| -------------- | -------- | ------------------------------------------------------ |
| `client_id` | Yes | Your application's Client ID |
| `redirect_uri` | Yes | One of your registered redirect URLs |
| `state` | No | Optional value passed back to your app in the callback |
### Example Authorization URL
```
https://dashboard.komerza.com/auth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=https://myapp.com/callback&state=random123
```
## OAuth2 Flow Diagram
```
┌─────────────┐ ┌─────────────┐
│ Your App │ │ Komerza │
└──────┬──────┘ └──────┬──────┘
│ │
│ 1. Redirect user to authorization URL │
│ ─────────────────────────────────────────> │
│ │
│ 2. User reviews & approves │
│ │
│ 3. Redirect back with authorization code │
│ <───────────────────────────────────────── │
│ │
│ 4. Exchange code for API key │
│ ─────────────────────────────────────────> │
│ │
│ 5. Return JWT API key │
│ <───────────────────────────────────────── │
│ │
│ 6. Make API calls with Bearer token │
│ ─────────────────────────────────────────> │
│ │
```
## Permissions & Scopes
When creating your application, you must specify which permissions (scopes) your app requires. Users will see these permissions during the authorization flow and can choose which stores to grant access to.
Complete reference for all available permission scopes
## Security Best Practices
**Client Secret Security**: Your client secret is like a password. Never
expose it in client-side code, public repositories, or logs.
All redirect URLs should use HTTPS in production environments.
Always exchange authorization codes from your backend server, never from
client-side code.
Store client secrets and API keys securely using environment variables or
secret management systems.
Only request the permissions your application actually needs.
## Token Characteristics
| Property | Value |
| ------------------------------- | --------------------- |
| **Token Type** | JWT (JSON Web Token) |
| **Authorization Code Validity** | 1 minute (single-use) |
| **API Key Validity** | Until revoked |
| **Max Redirect URLs** | 10 per application |
## Quick Links
Exchange authorization code for API key
# Payment Methods
Source: https://docs.komerza.com/api-reference/payment-methods
Complete reference for all supported payment methods and cryptocurrencies
## Overview
Komerza supports a wide range of payment methods including traditional payment gateways and cryptocurrencies. Each payment method has a unique identifier used throughout the API.
## Payment Gateways
### Traditional Processors
**Stripe** - Industry-leading payment processor supporting cards, wallets, and
local payment methods
**SumUp** - European payment gateway for card processing
**Square** - Payment processor with integrated point-of-sale solutions
**Cash App** - Mobile payment service for instant transfers
**Authorize.Net** - Long-established payment gateway for online transactions
**NMI** - Payment gateway similar to Authorize.Net
**Mollie** - European payment service provider supporting cards, iDEAL,
Bancontact, and 20+ local payment methods
**VenPayr** - Card payment processor
**moneymotion** - Card payment processor
**Pandabase** - Card payment processor
**Storrik** - Card payment processor
**Shopify** - Card payments through Shopify's hosted checkout
### PayPal
**PayPal Business** - PayPal business accounts with Instant Payment
Notifications (IPNs)
**PayPal Friends & Family** - PayPal personal payment method
### Customer Balance
**Customer Balance** - Payment using customer's account balance
## Cryptocurrencies
### Major Cryptocurrencies
**Bitcoin** - The original and most widely recognized cryptocurrency
* Network: Bitcoin
* Symbol: BTC
* Type: Native coin
**Ethereum** - Leading smart contract platform - Network: Ethereum - Symbol:
ETH - Type: Native coin
**Litecoin** - Faster alternative to Bitcoin - Network: Litecoin - Symbol: LTC
* Type: Native coin
**Bitcoin Cash** - Bitcoin fork with larger block sizes - Network: Bitcoin
Cash - Symbol: BCH - Type: Native coin
**Monero** - Privacy-focused cryptocurrency - Network: Monero - Symbol: XMR -
Type: Native coin
**Dash** - Digital cash with instant transactions - Network: Dash - Symbol:
DASH - Type: Native coin
**Dogecoin** - Popular meme-based cryptocurrency
* Network: Dogecoin
* Symbol: DOGE
* Type: Native coin
### Smart Contract Platforms
**Solana** - High-performance blockchain platform
* Network: Solana
* Symbol: SOL
* Type: Native coin
**Cardano** - Proof-of-stake blockchain platform - Network: Cardano - Symbol:
ADA - Type: Native coin
**TRON** - Decentralized entertainment ecosystem - Network: TRON - Symbol: TRX
* Type: Native coin
**Nano** - Zero-fee instant transactions
* Network: Nano
* Symbol: NANO
* Type: Native coin
### Stablecoins
Stablecoins are cryptocurrencies designed to maintain a stable value, typically pegged to fiat currencies like USD.
**USDT** - Most widely used stablecoin (1 USDT ≈ 1 USD)
Available on multiple networks:
* `usdt` - USD Tether (primary)
* `usdterc20` - USD Tether on Ethereum (ERC-20)
* `usdtbsc` - USD Tether on Binance Smart Chain (BEP-20)
* `usdtsol` - USD Tether on Solana (SPL)
**USDC** - Regulated stablecoin (1 USDC ≈ 1 USD) Available on multiple
networks: - `usdc` - USD Coin (primary) - `usdcbsc` - USD Coin on Binance
Smart Chain (BEP-20) - `usdcsol` - USD Coin on Solana (SPL)
**BUSD** - Binance's regulated stablecoin
* Network: Multiple
* Symbol: BUSD
* Peg: 1 BUSD ≈ 1 USD
### Exchange Tokens
**BNB** - Binance Smart Chain native token - Network: Binance Smart Chain -
Symbol: BNB - Type: Exchange utility token
### Meme Tokens
**SHIB** - Popular meme token - Network: Ethereum - Symbol: SHIB - Type:
ERC-20 token
## Related Resources
Process payments and create orders
Track payment statuses
# Order Status
Source: https://docs.komerza.com/api-reference/payment-status
Reference for all order statuses and their meanings
The order has not received payment and is considered incomplete. This is the
initial status for all new orders.
The order has received payment and the funds are pending or being verified by
the payment gateway.
The funds have been received but are withheld for verification. Commonly used
for cryptocurrency payments awaiting blockchain confirmations or fraud checks.
The order is being delivered. This is a guard status to prevent multiple
deliveries from happening simultaneously.
The order has been verified, paid for, and the product has been delivered to
the customer.
The order has been verified and paid for, but issues were encountered during
delivery that prevented fulfillment. This is typically due to missing stock
(e.g., no license keys available).
The order has been partially paid. The customer sent less than the required
amount.
The order was cancelled by the customer or the store before completion.
The store refunded the amount or the customer disputed the payment and won.
The order has been disputed by the customer, usually through a chargeback with
their payment gateway.
# Quick Start
Source: https://docs.komerza.com/api-reference/quickstart
Make your first Komerza API call
Three steps: create a key, call `/user` to find your store, then call something with that store ID. You need a Komerza account and dashboard access.
## 1. Create an API key
1. Open **[Account Settings → API Keys](https://dashboard.komerza.com/account?api)** in the dashboard
2. Click **Create New API Key** and give it a name you'll recognise later
3. Select the scopes it needs - `user.view` and `stores.view` are enough for this guide
4. Copy the key
The key is shown once. Store it in an environment variable or a secrets
manager, never in source control, and give each integration its own key so you
can revoke one without breaking the rest.
Every request needs two headers:
| Header | Value |
| --------------- | ---------------------------------------------------- |
| `Authorization` | `Bearer YOUR_API_KEY` |
| `User-Agent` | Something that identifies your app, e.g. `MyApp/1.0` |
## 2. Find your store ID
Almost every endpoint is scoped to a store. `GET /user` returns your account, with the stores on it:
```bash theme={null}
curl "https://api.komerza.com/user" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0"
```
```json theme={null}
{
"success": true,
"message": null,
"code": null,
"data": {
"id": "…",
"email": "you@example.com",
"plan": 1,
"stores": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "My Store",
"url": "mystore",
"currencyCode": "USD"
}
]
}
}
```
The `id` of the store you want is what every `{storeId}` in these docs refers to.
You can also copy it from the dashboard: **Online Store → Settings →
General**, where the **Store ID** field has a copy button next to it.
## 3. Call an endpoint
Fetch the store you just found:
```bash theme={null}
curl "https://api.komerza.com/stores/{storeId}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0"
```
```json theme={null}
{
"success": true,
"message": null,
"code": null,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"dateCreated": "2026-01-15T10:30:00Z",
"name": "My Store",
"description": "Digital goods, delivered instantly",
"url": "mystore",
"customDomain": "shop.example.com",
"currencyCode": "USD",
"rating": 4.8,
"isTicketingEnabled": true,
"isCustomerBalanceEnabled": false
}
}
```
That's it - you're authenticated and reading real data.
## The response envelope
Every response has the same four fields:
| Field | What it is |
| --------- | ------------------------------------------------------ |
| `success` | Whether the request worked |
| `message` | Human-readable detail, often `null` on success |
| `code` | Error code when something went wrong, otherwise `null` |
| `data` | The payload - an object, an array, or `null` |
Errors keep the same shape:
```json theme={null}
{
"success": false,
"message": "Access denied."
}
```
Check `success` rather than assuming a `200` means you got what you asked for.
## When it doesn't work
The key is missing, malformed or revoked. Check you sent `Authorization:
Bearer YOUR_KEY` including the `Bearer ` prefix, and that the key still
exists in the dashboard.
The key is valid but lacks the scope for that endpoint. Scopes follow the
resource: reading orders needs `stores.orders.view`, refunding them needs
`stores.orders.refund`. The full list is in [Scopes](/api-reference/scopes).
Scopes are fixed when the key is created, so create a new key with the ones
you need.
Usually a store ID that doesn't exist, belongs to another account, or picked
up a stray character. Confirm it against `GET /user`. Check the path too:
the base URL is `https://api.komerza.com` with **no** `/api` prefix.
You're sending requests faster than allowed. Back off and retry with
increasing delays rather than immediately.
## Next steps
} href="/api-reference/filtering">
Narrow, order and page through list endpoints.
} href="/api-reference/scopes">
Every permission a key can carry.
} href="/api-reference/endpoint/products/get-list">
Read and manage your catalogue.
} href="/api-reference/endpoint/orders/get-list">
Query orders, deliver and refund them.
} href="/guides/webhooks">
Get order events pushed to your server instead of polling.
} href="/api-reference/sdks">
Official libraries for .NET, JavaScript, Python, Go and PHP.
# Responses & Errors
Source: https://docs.komerza.com/api-reference/responses
The response envelope, list responses, and how failures are reported
Every response uses the same envelope, whether it succeeded or not.
| Field | Type | Description |
| --------- | ----------------------- | -------------------------------------------------- |
| `success` | boolean | Whether the request did what you asked |
| `message` | string \| null | Human-readable detail, usually `null` on success |
| `code` | string \| null | Error code when something failed, otherwise `null` |
| `data` | object \| array \| null | The payload |
```json theme={null}
{
"success": true,
"message": null,
"code": null,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "My Store"
}
}
```
Branch on `success`, not on the status code alone, and read `code` rather than
`message` when handling failures. Messages are written for humans and can be
reworded; codes are stable.
## List responses
Endpoints returning a list add `pages`: how many pages exist at the current page size. There's no total item count, so page until you reach it.
```json theme={null}
{
"success": true,
"pages": 5,
"data": [
// items
]
}
```
See [Filtering & Sorting](/api-reference/filtering) for narrowing, ordering and paging those results.
## Errors
```json theme={null}
{
"success": false,
"message": "Access denied.",
"code": "AccessDenied",
"data": null
}
```
`code` is a stable PascalCase identifier: `AccessDenied`, `NotFound`, `ObjectConflict`, `ValidationError`, `UpgradeRequired` and so on.
### Validation errors
Validation failures add `invalidFields`, one entry per problem, each formatted `Field: what's wrong`:
```json theme={null}
{
"success": false,
"message": "Validation failed",
"code": "ValidationError",
"data": null,
"invalidFields": ["Name: Must be between 3 and 64 characters"]
}
```
Surface these to whoever submitted the data - they name the field and the rule it broke.
### HTTP status codes
| Status | Usually means |
| ------ | ----------------------------------------------------- |
| `400` | Malformed request, or validation failed |
| `401` | Missing, malformed or revoked API key |
| `403` | Valid key, but it lacks the scope for this endpoint |
| `404` | Wrong ID, or a resource on an account you can't reach |
| `409` | Conflicts with something that already exists |
| `429` | Too many requests |
| `5xx` | Our side |
## Retrying
Retry `429` and `5xx` with increasing delays rather than immediately. Don't retry `4xx` responses other than `429` - the request will fail the same way until you change it.
If a `5xx` persists, check [status.komerza.com](https://status.komerza.com) before digging into your own code.
} href="/api-reference/error-codes">
The full list of `code` values, what triggers each and how to recover.
# Scopes
Source: https://docs.komerza.com/api-reference/scopes
Complete reference for all available API key permission scopes
## Overview
API keys use a permission-based scope system that controls access to specific endpoints and operations. Each API key can be configured with one or more scopes to limit access according to your integration needs.
API keys can be generated and scopes can be configured from your [Komerza
Dashboard](https://dashboard.komerza.com/account?api).
## Scope Categories
### Store Management
**Permission to view stores** Read access to store information, settings,
and configuration. Required for most store-related read operations.
**Permission to update stores** Modify store configuration, settings,
branding, and operational parameters.
**Permission to create new stores** Create new store instances. Typically used
for multi-store applications or store management platforms.
**Permission to view store analytics** Access store performance metrics,
sales data, traffic statistics, and analytical insights.
### Categories
**Permission to view categories in stores** Read access to product
categories, category hierarchy, and organization structure.
**Permission to create new categories in stores** Add new product categories
to organize your store catalog.
**Permission to update categories in stores** Modify category information,
reorganize hierarchy, and update category settings.
**Permission to delete categories in stores** Remove categories from the
store. Ensure products are reassigned before deleting categories.
### Products & Inventory
**Permission to view products in stores** Read access to product catalog,
variants, pricing, and inventory information.
**Permission to create new products in stores** Add new products to the store
catalog, including variants and pricing.
**Permission to update products in stores** Modify existing products, update
pricing, inventory levels, and product details.
**Permission to delete products in stores** Remove products from the store
catalog. Use with caution as this action may affect order history.
### Customer Management
**Permission to view customers in stores** Access customer profiles, contact
information, and purchase history.
**Permission to create new customers in stores** Add new customer profiles to
the store database.
**Permission to update customers in stores** Modify customer information,
update balances, and manage customer accounts.
### Order Processing
**Permission to view orders in stores** Access order information, payment
status, and order history.
**Permission to create orders** Process new orders, including payment
processing and order creation.
**Permission to deliver orders in stores** Mark orders as delivered, update
tracking information, and manage fulfillment.
**Permission to refund orders** Process refunds, partial refunds, and handle
order cancellations.
### Marketing & Promotions
**Permission to view coupons in stores** Access coupon information, usage
statistics, and discount configurations.
**Permission to create new coupons in stores** Create discount codes,
promotional campaigns, and coupon configurations.
**Permission to update coupons in stores** Modify existing coupons, update
discount amounts, and change validity periods.
**Permission to delete coupons in stores** Remove coupons from the system.
Active coupons should be disabled rather than deleted.
### Affiliate Program
**Permission to view affiliates in stores** Access affiliate information,
commission data, and performance statistics.
**Permission to update affiliates in stores** Modify affiliate settings,
commission rates, and payout configurations.
### Support & Communication
**Permission to view tickets in stores** Access support tickets, customer
inquiries, and communication history.
**Permission to update tickets in stores** Respond to tickets, update ticket
status, and manage customer support workflows.
**Permission to delete tickets in stores** Remove tickets from the system.
Consider archiving instead of deleting for audit purposes.
**Permission to flag reviews in stores** Mark reviews as inappropriate,
spam, or requiring moderation.
### User Account & Platform
**Permission to view user profile information** Access user account details,
preferences, and profile information.
**Permission to view enabled cryptocurrencies for a user** Access
cryptocurrency payment settings and enabled payment methods.
This scope is deprecated and will be removed in future versions. Please avoid
using it.
### Webhooks & Integrations
**Permission to view webhooks** Access webhook configurations, event
subscriptions, and delivery logs.
**Permission to manage webhooks: create** Set up new webhook endpoints and
event subscriptions.
**Permission to manage webhooks: update** Modify existing webhook
configurations and event subscriptions.
**Permission to manage webhooks: delete** Remove webhook endpoints and
cancel event subscriptions.
## Best Practices
### Principle of Least Privilege
Only request the minimum scopes necessary for your application. This reduces security risks and makes it easier to audit API access.
### Scope Planning
Plan your required scopes during the design phase of your integration. Consider:
* Which resources you need to read vs. modify
* Whether you need create/delete permissions
* If your application handles sensitive customer data
# SDKs
Source: https://docs.komerza.com/api-reference/sdks
Official SDKs for the Komerza API - coming soon
## Overview
We're building official SDKs to make integrating with the Komerza API even easier. These SDKs will provide idiomatic interfaces, automatic error handling, and built-in best practices for each programming language.
All SDKs are currently in development. While you wait, you can use the API
directly via HTTP requests as shown in our [Quick Start
Guide](/api-reference/quickstart).
## Planned SDKs
**Coming Soon** Full-featured SDK for .NET applications with strong typing
and async support.
**Coming Soon** Modern SDK with TypeScript definitions for Node.js and browser
environments.
**Coming Soon** Pythonic interface with support for async/await and popular
frameworks.
**Coming Soon** Modern PHP SDK with Composer support and PSR-compliant design.
**Coming Soon** Idiomatic Go SDK with proper error handling and context
support.
## SDK Previews
Here's what the SDK interfaces will look like:
```csharp .NET theme={null}
using Komerza.Api;
var client = new KomerzaClient("your-api-key");
// Get store information
var store = await client.Stores.GetAsync(storeId);
// List products with pagination
var products = await client.Products.ListAsync(storeId, new ProductListOptions
{
Page = 1,
PageSize = 20
});
// Create a new product
var newProduct = await client.Products.CreateAsync(storeId, new CreateProductRequest
{
Name = "New Product",
Price = 29.99m,
Currency = "USD"
});
```
```javascript JavaScript/TypeScript theme={null}
import { KomerzaClient } from "@komerza/api";
const client = new KomerzaClient("your-api-key");
// Get store information
const store = await client.stores.get(storeId);
// List products with pagination
const products = await client.products.list(storeId, {
page: 1,
pageSize: 20,
});
// Create a new product
const newProduct = await client.products.create(storeId, {
name: "New Product",
price: 29.99,
currency: "USD",
});
```
```python Python theme={null}
from komerza import KomerzaClient
client = KomerzaClient('your-api-key')
# Get store information
store = await client.stores.get(store_id)
# List products with pagination
products = await client.products.list(store_id, page=1, page_size=20)
# Create a new product
new_product = await client.products.create(store_id, {
'name': 'New Product',
'price': 29.99,
'currency': 'USD'
})
```
```php PHP theme={null}
stores->get($storeId);
// List products with pagination
$products = $client->products->list($storeId, [
'page' => 1,
'pageSize' => 20
]);
// Create a new product
$newProduct = $client->products->create($storeId, [
'name' => 'New Product',
'price' => 29.99,
'currency' => 'USD'
]);
```
```go Go theme={null}
package main
import (
"context"
"github.com/komerza/go-sdk"
)
func main() {
client := komerza.NewClient("your-api-key")
ctx := context.Background()
// Get store information
store, err := client.Stores.Get(ctx, storeID)
if err != nil {
// Handle error
}
// List products with pagination
products, err := client.Products.List(ctx, storeID, &komerza.ProductListOptions{
Page: 1,
PageSize: 20,
})
// Create a new product
newProduct, err := client.Products.Create(ctx, storeID, &komerza.CreateProductRequest{
Name: "New Product",
Price: 29.99,
Currency: "USD",
})
}
```
## Get Notified
Want to be the first to know when an SDK is released?
Get updates in our Telegram developer community
Watch our GitHub organization for new SDK releases
## In the Meantime
While waiting for the official SDKs, you can:
### Use the REST API Directly
Follow our [Quick Start Guide](/api-reference/quickstart) to start building with HTTP requests right away.
### Build Your Own Wrapper
Create a simple wrapper around the API for your specific use case. Our [API Reference](/api-reference/introduction) provides all the details you need.
### Community SDKs
Check if there are any community-built SDKs available. We'll showcase quality community contributions here.
## Contributing
Interested in contributing to an SDK or building one for another language?
Get in touch about SDK development opportunities
We welcome contributions and are happy to work with the community to build SDKs for additional programming languages and frameworks.
## Stay Updated
Check our help center for SDK documentation as it becomes available
Learn the API while waiting for your preferred SDK
# Authentication
Source: https://docs.komerza.com/api-reference/authentication
API keys, required headers and scopes
Every endpoint requires an API key, sent as a bearer token.
```bash theme={null}
curl "https://api.komerza.com/stores/{storeId}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0"
```
## Creating a key
1. Open **[Account Settings → API Keys](https://dashboard.komerza.com/account?api)** in the dashboard
2. Click **Create New API Key** and name it after the integration that will use it
3. Select its scopes
4. Copy the key
The key is shown once and can't be retrieved later. Keep it in an environment
variable or a secrets manager, never in source control.
## Required headers
| Header | Required | Value |
| --------------- | --------- | ------------------------------------------------ |
| `Authorization` | Yes | `Bearer YOUR_API_KEY` |
| `User-Agent` | Yes | Something identifying your app, e.g. `MyApp/1.0` |
| `Content-Type` | On writes | `application/json` |
## Scopes
Each key carries a fixed set of scopes, chosen when you create it. They decide which endpoints it can reach: `stores.orders.view` to read orders, `stores.orders.refund` to refund them, and so on.
Scopes can't be edited afterwards. To change what a key can do, create a new one and retire the old.
Two habits worth adopting from the start:
* **One key per integration.** You can then revoke a single integration without breaking everything else.
* **Only the scopes it needs.** A reporting script has no business holding `stores.orders.refund`.
} href="/api-reference/scopes">
Every permission a key can carry, and what each one unlocks.
## When authentication fails
| Status | Meaning | Fix |
| ------ | --------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `401` | Key missing, malformed or revoked | Check the `Bearer ` prefix is present and the key still exists |
| `403` | Key is valid but lacks the scope | Scopes follow the resource, e.g. orders need `stores.orders.*`. Create a new key with the right ones |
Both return the standard error envelope, with `code` set to `AccessDenied`. See [Responses & Errors](/api-reference/responses).
# Conventions
Source: https://docs.komerza.com/api-reference/conventions
IDs, dates, enums, money, store scoping and paging
These hold across every endpoint. Knowing them up front saves a lot of trial and error.
## Base URL
```
https://api.komerza.com
```
There is **no `/api` prefix**. Paths start straight at the resource:
```bash theme={null}
https://api.komerza.com/stores/{storeId}/orders # correct
https://api.komerza.com/api/stores/{storeId}/orders # 404
```
## Store scoping
Nearly every path begins `/stores/{storeId}/…`, because almost everything belongs to a store rather than to your account. `GET /user` returns your account with its stores, and each store's `id` is the `{storeId}` used everywhere else.
An account can hold several stores, so treat the store ID as a required piece of configuration in your integration, not something to hardcode once and forget.
## IDs
UUIDs, in the standard hyphenated form:
```
550e8400-e29b-41d4-a716-446655440000
```
Stores, products, variants, orders, customers and coupons all use them. They're opaque - don't parse or generate them.
## Dates
ISO 8601, in UTC:
```
2026-01-15T10:30:00Z
```
Filters accept either a full timestamp or just the date, e.g. `dateFrom>=2026-01-15`.
## Enums
Sent and returned as **integers**, not names. A product's visibility is `0`, not `"Public"`.
```json theme={null}
{ "privacy": 0, "stockCalculationMode": 2 }
```
} href="/api-reference/enums">
Every enum value the API accepts and returns.
Order status and payment gateway are the exceptions - both are strings, documented on [Order Status](/api-reference/payment-status) and [Payment Methods](/api-reference/payment-methods).
## Money
Decimal values in the store's own currency, with `currencyCode` on the store telling you which. There's no minor-unit convention to decode: `29.99` means 29.99.
Amounts a buyer paid can differ from the order total on split payments, and crypto orders carry the amount actually received - check both rather than assuming.
## Paging
| Parameter | Default | Notes |
| ---------- | ------- | ---------------------------------------------------- |
| `page` | `1` | 1-indexed |
| `pageSize` | `20` | Maximum 100; larger values are clamped, not rejected |
List endpoints also take `filters` and `sorts`.
} href="/api-reference/filtering">
Operators, combining conditions, sorting and paging.
## Fair use
Requests are rate limited. On a `429`, back off and retry with increasing delays, and cache anything that doesn't change often.
If you're polling for order updates, use [webhooks](/guides/webhooks) instead. They're signed, logged and retried, and they'll reach you sooner than any poll interval you'd be comfortable running.
# Blacklist Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/blacklist
POST /stores/{storeId}/affiliates/{customerId}/blacklist
Disables the affiliate, blocks the customer on the store, and adds their email to the store's blacklist.
Requires the `stores.affiliates.update` permission.
Blacklisting stops the affiliate earning on new referrals. It does not claw
back commission already accrued, and it does not refund orders they referred.
# Configure Affiliate Program
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/configure
PUT /stores/{storeId}/affiliates/configure
Updates the configuration options for the affiliate program of the specified store.
Requires the `stores.affiliates.update` permission.
# Create Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/create
POST /stores/{storeId}/affiliates/{customerId}
Creates a new affiliate record for the given customer in this store.
Requires the `stores.affiliates.update` permission.
# Delete Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/delete
DELETE /stores/{storeId}/affiliates/{customerId}
Permanently removes an affiliate and all their payout records from the store.
Requires the `stores.affiliates.update` permission.
# Get Affiliates
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/get-list
GET /stores/{storeId}/affiliates
Retrieves affiliates for the specified store using query-based filter options.
Supports filtering, sorting, and pagination via query parameters.
Requires the `stores.affiliates.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ---------------------- | ------- | ----------------------------------------------------------- |
| `isEnabled` | boolean | Filter by affiliate enabled status |
| `email` | string | Filter by email address (contains) |
| `link` | string | Filter by affiliate link/code (contains) |
| `balanceFrom` | number | Minimum affiliate balance (inclusive) |
| `balanceTo` | number | Maximum affiliate balance (inclusive) |
| `returnPercentageFrom` | integer | Minimum return percentage (inclusive) |
| `returnPercentageTo` | integer | Maximum return percentage (inclusive) |
| `percentageOffFrom` | integer | Minimum discount percentage (inclusive) |
| `percentageOffTo` | integer | Maximum discount percentage (inclusive) |
| `referredOrdersFrom` | integer | Minimum referred delivered orders (inclusive) |
| `referredOrdersTo` | integer | Maximum referred delivered orders (inclusive) |
| `allTimeEarnedFrom` | number | Minimum all-time earned amount (inclusive) |
| `allTimeEarnedTo` | number | Maximum all-time earned amount (inclusive) |
| `payoutCountFrom` | integer | Minimum payout count (inclusive) |
| `payoutCountTo` | integer | Maximum payout count (inclusive) |
| `dateFrom` | string | Filter affiliates created from this date/time (inclusive) |
| `dateTo` | string | Filter affiliates created before this date/time (inclusive) |
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | -------- | ----------------------------------------------------- |
| `isEnabled` | boolean | Filter by whether the affiliate profile is enabled |
| `email` | string | Filter by affiliate email (case-insensitive contains) |
| `link` | string | Filter by referral link or code |
| `balance` | number | Current unpaid balance |
| `allTimeEarned` | number | Total commission earned across all time |
| `referredOrders` | integer | Number of orders attributed to this affiliate |
| `payoutCount` | integer | Number of payouts recorded against this affiliate |
| `returnPercentage` | number | The affiliate's commission rate |
| `percentageOff` | number | The customer discount applied to their referrals |
| `dateCreated` | datetime | When the affiliate profile was created |
Numeric and date fields take ranges through the comparison operators rather than separate from/to parameters.
**Sortable:** `isEnabled`, `email`, `link`, `referredOrders`, `allTimeEarned`, `payoutCount`, `dateCreated`.
**Example:**
```bash theme={null}
# Enabled affiliates who have earned over 100, biggest earners first
GET /stores/{storeId}/affiliates?filters=isEnabled==true,allTimeEarned>=100&sorts=-allTimeEarned
# Affiliates with an outstanding balance and at least one referred order
GET /stores/{storeId}/affiliates?filters=balance>0,referredOrders>=1
```
# List Affiliate Orders
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/get-orders
GET /stores/{storeId}/affiliates/{customerId}/orders/{page}
Retrieves a paginated list of orders attributed to a specific affiliate (by customerId) in the store.
Requires the `stores.affiliates.view` permission.
# List Affiliate Payouts
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/get-payouts
GET /stores/{storeId}/affiliates/{customerId}/payouts/{page}
Requires the `stores.affiliates.view` permission.
# Invite Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/invite
POST /stores/{storeId}/affiliates/invite
Invites an affiliate, this will create a customer profile if one does not exist and takes in a customer email
address instead.
Requires the `stores.affiliates.update` permission.
# Record Affiliate Payout
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/record-payout
POST /stores/{storeId}/affiliates/{customerId}/payouts
Requires the `stores.affiliates.update` permission.
**This records a payout, it does not send money.** Komerza reduces the
affiliate's balance by the amount; the actual transfer happens between you and
your affiliate outside Komerza.
Record it only once you have actually paid, or your balances will disagree with
reality.
# Search Affiliates
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/search
GET /stores/{storeId}/affiliates/search
Searches affiliates by the `query` parameter and applies filtering/sorting/pagination.
The query is matched against affiliate email, affiliate link/code, affiliate ID, and customer ID.
Requires the `stores.affiliates.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | -------- | ----------------------------------------------------- |
| `isEnabled` | boolean | Filter by whether the affiliate profile is enabled |
| `email` | string | Filter by affiliate email (case-insensitive contains) |
| `link` | string | Filter by referral link or code |
| `balance` | number | Current unpaid balance |
| `allTimeEarned` | number | Total commission earned across all time |
| `referredOrders` | integer | Number of orders attributed to this affiliate |
| `payoutCount` | integer | Number of payouts recorded against this affiliate |
| `returnPercentage` | number | The affiliate's commission rate |
| `percentageOff` | number | The customer discount applied to their referrals |
| `dateCreated` | datetime | When the affiliate profile was created |
Numeric and date fields take ranges through the comparison operators rather than separate from/to parameters.
**Sortable:** `isEnabled`, `email`, `link`, `referredOrders`, `allTimeEarned`, `payoutCount`, `dateCreated`.
**Example:**
```bash theme={null}
# Enabled affiliates who have earned over 100, biggest earners first
GET /stores/{storeId}/affiliates?filters=isEnabled==true,allTimeEarned>=100&sorts=-allTimeEarned
# Affiliates with an outstanding balance and at least one referred order
GET /stores/{storeId}/affiliates?filters=balance>0,referredOrders>=1
```
# Get Affiliate Statistics
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/stats
GET /stores/{storeId}/affiliates/stats
Retrieves the affiliate statistics associated with a specific store.
Requires the `stores.affiliates.view` permission.
# Update Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/update
PUT /stores/{storeId}/affiliates/{customerId}
Updates affiliate settings for the given customer in this store.
Requires the `stores.affiliates.update` permission.
# Void Affiliate Payout
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/void-payout
PATCH /stores/{storeId}/affiliates/{customerId}/payouts/{payoutId}
Marks the payout as voided, restores balances, and records the admin and reason.
Requires the `stores.affiliates.update` permission.
Voiding a recorded payout **restores the amount to the affiliate's balance**.
Use it when a payout was recorded in error or the transfer failed - not to
reverse money you actually sent.
# Approve Custom Payment
Source: https://docs.komerza.com/api-reference/endpoint/orders/approve-custom-payment
POST /stores/{storeId}/paymentMethods/custom/orders/{orderId}/approve
Approves a pending custom payment order, marking it as delivered.
Requires the `stores.orders.deliver` permission.
# Get Proof of Payment
Source: https://docs.komerza.com/api-reference/endpoint/orders/get-payment-proof
GET /stores/{storeId}/paymentMethods/custom/orders/{orderId}/proof
Gets the proof of payment submitted by a customer for an order.
Requires the `stores.orders.view` permission.
# Re-deliver Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/redeliver
PUT /stores/{storeId}/orders/{orderId}/redeliver
Forces redelivery of an already delivered order as if purchased normally.
Requires the `stores.orders.deliver` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
**This reruns the entire delivery, exactly like a fresh purchase.** It takes
**new license keys** from your inventory, re-fires dynamic delivery webhooks,
and emails the customer again.
Call it once, deliberately. Repeated calls consume stock every time, and there
is no way to return issued keys to inventory.
# Reject Custom Payment
Source: https://docs.komerza.com/api-reference/endpoint/orders/reject-custom-payment
POST /stores/{storeId}/paymentMethods/custom/orders/{orderId}/reject
Rejects a pending custom payment order, cancelling it.
Requires the `stores.orders.deliver` permission.
# Resend Receipt
Source: https://docs.komerza.com/api-reference/endpoint/orders/resend-receipt
PUT /stores/{storeId}/orders/{orderId}/resend
Resends the delivery receipt to the customer for a previously delivered order.
Rate limited due to sensitivity.
Requires the `stores.orders.deliver` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
# Add to Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/add-to-catalogue
PUT /stores/{storeId}/reseller/catalogue/{variantId}
Adds a variant to the catalogue or updates its terms. The variant does not have to be visible on
the storefront — offering resellers something ordinary customers never see is a supported use.
Requires the `stores.resellers.update` permission.
# Configure Programme
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/configure
PUT /stores/{storeId}/reseller
Sets whether the store supplies resellers, how they enrol, the discount they pay, and the
optional exposure and top-up limits.
Requires the `stores.resellers.update` permission.
# Create Application
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-application
POST /stores/{storeId}/reseller/applications
For a merchant hosting their own application form. Collect whatever fields suit your site, then
post them here with the applicant's email — answers may reference the questions configured on
this store, carry a `label` of your own, or mix the two.
The email is taken as given and not verified: the form is yours, on your domain, and you
review what it produces. The applicant proves the address the first time they sign in to
deposit or reach their reseller panel.
The application lands in the same review queue as every other, at
`GET /stores/{storeId}/reseller/application/submissions`.
Requires the `stores.resellers.update` permission.
# Create Basket Discount
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-basket-tier
POST /stores/{storeId}/reseller/basket-tiers
Measured at list price before anything comes off, so qualifying for a discount cannot lower a
basket's value and disqualify it again. Where several apply, the largest wins.
Requires the `stores.resellers.update` permission.
# Create Bundle
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-bundle
POST /stores/{storeId}/reseller/bundles
A bundle is a basket you have composed and priced as one thing. Set a discount to advertise a
figure, or leave it off and let your basket-size discounts decide.
Every product in it has to be in your reseller catalogue already, and it can never cost a
reseller more than buying the same lines separately would.
Requires the `stores.resellers.update` permission.
# Create Invitation
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-invite
POST /stores/{storeId}/reseller/invites
Returns the invite token once and only once — only its hash is stored, because the token grants
an already-approved reseller relationship and a leaked invite list must not be usable. Send it on
immediately; it cannot be read back.
Requires the `stores.resellers.update` permission.
# Create Question
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-question
POST /stores/{storeId}/reseller/application/questions
Questions can be added, reworded and reordered at any time, including while applications are
waiting: an answer records the wording it was given against, so editing the form never changes
what someone already in the queue appears to have been asked.
Requires the `stores.resellers.update` permission.
# Delete Basket Discount
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-basket-tier
DELETE /stores/{storeId}/reseller/basket-tiers/{basketTierId}
Requires the `stores.resellers.update` permission.
# Delete Bundle
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-bundle
DELETE /stores/{storeId}/reseller/bundles/{bundleId}
Takes it off sale. What resellers already bought through it, and what they paid, stays on record.
Requires the `stores.resellers.update` permission.
# Delete Invitation
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-invite
DELETE /stores/{storeId}/reseller/invites/{inviteId}
Requires the `stores.resellers.update` permission.
# Delete Question
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-question
DELETE /stores/{storeId}/reseller/application/questions/{questionId}
Archived rather than deleted. Every answer ever given to it still points at it, and tidying the
form should not orphan the applications that answered it.
Requires the `stores.resellers.update` permission.
# Get Programme
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get
GET /stores/{storeId}/reseller
Returns the store's reseller settings plus headline counts. A store that has never configured a
programme comes back disabled with defaults rather than a 404.
Requires the `stores.resellers.view` permission.
# Get Applications
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-applications
GET /stores/{storeId}/reseller/application/submissions
The review queue: who has applied, what they answered, and what they have deposited since. The
deposit is summed from the balance ledger rather than stored, so it always matches the money
that actually arrived.
Requires the `stores.resellers.view` permission.
# Get Basket Discounts
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-basket-tiers
GET /stores/{storeId}/reseller/basket-tiers
Volume terms measured on what a whole basket comes to rather than on how many of one product it
holds, so a reseller taking a spread of your catalogue is credited for the whole commitment.
Requires the `stores.resellers.view` permission.
# Get Bundles
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-bundles
GET /stores/{storeId}/reseller/bundles
Each comes back priced the way your resellers will see it: what it contains, what that comes to
at list price, the discount, and what they would pay. A bundle whose products have left your
reseller catalogue comes back unavailable, with the reason, rather than disappearing.
Requires the `stores.resellers.view` permission.
# Get Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-catalogue
GET /stores/{storeId}/reseller/catalogue
The variants resellers can draw, each with its list price and the price a reseller pays, so the
merchant can see the margin they are giving away.
Requires the `stores.resellers.view` permission.
# Get Invitations
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-invites
GET /stores/{storeId}/reseller/invites
Tokens are never included — they exist only in the response that created them.
Requires the `stores.resellers.view` permission.
# Get Leaderboard
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-leaderboard
GET /stores/{storeId}/reseller/leaderboard
Your resellers ranked by what they spent with you over a period, biggest first. Built for
publishing: every reseller is identified by the store they sell from — its name, description,
storefront and branding, all of which is public already — so nothing here has to be masked
before it goes on a page.
`from` is inclusive and `to` exclusive. Omit them for the last thirty days.
Spend is net of refunds, so a reseller who was refunded more than they spent in the period
does not appear.
A reseller who sells outside Komerza still ranks, with no store attached — leaving them out
would produce a leaderboard whose total disagreed with your own revenue.
Requires the `stores.resellers.view` permission.
# Get Questions
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-questions
GET /stores/{storeId}/reseller/application/questions
The store's reseller application form, in the order applicants are asked. Retired questions are
left out — they exist only so answers already given to them stay readable.
Requires the `stores.resellers.view` permission.
# Get Resellers
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-resellers
GET /stores/{storeId}/reseller/resellers
Who resells for this store, what they have spent, and what they can currently draw against.
Does not include the resellers' own customers — who they sell to is their business.
Requires the `stores.resellers.view` permission.
# Get Variant Stats
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-variant-stats
GET /stores/{storeId}/reseller/catalogue/{variantId}/stats
How many resellers list this variant and what they have drawn, so the reselling side of a product
is visible on the product page rather than only in the reseller section.
Requires the `stores.resellers.view` permission.
# Grant Samples
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/grant-samples
POST /stores/{storeId}/reseller/resellers/{resellerId}/samples
Gives a reseller free units so they can prove delivery works end to end before funding a
balance. Samples are spent before money on a matching draw.
Requires the `stores.resellers.update` permission.
# Remove from Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/remove-from-catalogue
DELETE /stores/{storeId}/reseller/catalogue/{variantId}
Removes it from the catalogue entirely, discarding its price overrides. Resellers already
listing it keep the listing, but their next draw fails. To pause it and keep the terms, set
`isEnabled` to false instead.
Requires the `stores.resellers.update` permission.
# Set Volume Tiers
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/set-volume-tiers
PUT /stores/{storeId}/reseller/catalogue/{variantId}/tiers
Applies only when a reseller buys stock up front, not to ordinary draws — drawing one unit at a
time commits them to nothing, so there is nothing for a discount to buy. Replaces the whole set,
so what you send is exactly what applies.
Requires the `stores.resellers.update` permission.
# Update Basket Discount
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-basket-tier
PUT /stores/{storeId}/reseller/basket-tiers/{basketTierId}
Requires the `stores.resellers.update` permission.
# Update Bundle
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-bundle
PUT /stores/{storeId}/reseller/bundles/{bundleId}
Replaces the bundle wholesale, lines and all. Anyone who already bought it keeps what they
bought at the price they paid.
Requires the `stores.resellers.update` permission.
# Update Question
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-question
PUT /stores/{storeId}/reseller/application/questions/{questionId}
Requires the `stores.resellers.update` permission.
# Update Status
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-status
PUT /stores/{storeId}/reseller/resellers/{resellerId}/status
Revoking stops further draws but leaves the reseller's balance alone — they funded it and are
still owed it.
Requires the `stores.resellers.update` permission.
# Accept Invitation
Source: https://docs.komerza.com/api-reference/endpoint/reselling/accept-invite
POST /stores/{storeId}/reselling/invites/accept
Redeems an invite on behalf of the store in the route, which `PlatformAuthorize` has already
proven you own. The relationship is approved outright — the supplier decided that when they sent
it — and a draft product is created for everything the invite named, so you land on a dashboard
with stock to sell rather than a setup guide.
Only redeemable by an account on the address the invite was sent to.
Requires the `stores.reselling.update` permission.
# Add Funds
Source: https://docs.komerza.com/api-reference/endpoint/reselling/add-funds
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/topup
Creates an order on the supplier's store that credits your balance with them once paid. Drawing
stock and buying it in bulk both spend that balance, so this is what makes either possible.
The account credited is the one this store's relationship is held under, not whoever is
signed in — a staff member topping up must not end up crediting themselves.
Requires the `stores.reselling.update` permission.
# Apply to Supplier
Source: https://docs.komerza.com/api-reference/endpoint/reselling/apply
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}
Asks to resell a store's stock. A customer account on that store is created from the caller's
platform email, so there is nothing to sign up for separately. Stores that enrol openly approve
immediately; stores that take applications leave this pending.
Requires the `stores.reselling.update` permission.
# Buy Basket
Source: https://docs.komerza.com/api-reference/endpoint/reselling/buy-basket
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/basket
The basket is priced once and each line is then bought at its share of that price, landing on
your own products as ordinary stock exactly as a single-variant purchase would. Short deliveries
are refunded the same way.
Supply an idempotency key. Lines carry it with their own index appended, so a retried basket
replays line for line rather than buying twice.
Requires the `stores.reselling.update` permission.
# Buy Stock
Source: https://docs.komerza.com/api-reference/endpoint/reselling/buy-stock
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/stock
Buys units up front, at whatever volume price the supplier offers for that quantity, and puts
them on your own product as ordinary stock. Nothing is held or pending: the units are yours, they
sell through your normal delivery, and nothing the supplier does afterwards takes them away.
If the supplier delivers fewer than were bought, the difference is refunded to your balance
rather than quietly kept.
Requires the `stores.reselling.update` permission.
# Create Listing
Source: https://docs.komerza.com/api-reference/endpoint/reselling/create-listing
PUT /stores/{storeId}/reselling/suppliers/{supplierStoreId}/listings/{sourceVariantId}
Points one of this store's variants at a supplier's variant. Selling it then draws a unit from
the supplier automatically — no delivery configuration of any kind is needed on this side.
Requires the `stores.reselling.update` permission.
# Delete Listing
Source: https://docs.komerza.com/api-reference/endpoint/reselling/delete-listing
DELETE /stores/{storeId}/reselling/suppliers/{supplierStoreId}/listings/{sourceVariantId}
The store's own product keeps existing and keeps selling; it just no longer draws from the
supplier, which for a purely resold product means it can no longer be delivered. Worth
unpublishing it at the same time.
Requires the `stores.reselling.update` permission.
# Find Supplier
Source: https://docs.komerza.com/api-reference/endpoint/reselling/find-supplier
GET /stores/{storeId}/reselling/suppliers/lookup
There is no supplier directory, so this is how you reach a store you already know of: paste its
address.
Only stores currently taking applications are found. One running an invite-only programme, or
none at all, comes back as not found — which store has a reseller programme is not something
this should let anyone map out.
Requires the `stores.reselling.view` permission.
# Get Bundles
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-bundles
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/bundles
Baskets the supplier composed and priced as one thing, each quoted for this store: what is in
it, what that comes to at their list prices, the discount, and what you would pay. Bundles aimed
at other resellers are not listed, and cannot be bought by guessing an id.
Requires the `stores.reselling.view` permission.
# Get Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-catalogue
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/catalogue
Priced as it will actually cost this store, with whichever of its own products is already
sourced from each.
Requires the `stores.reselling.view` permission.
# Get Draws
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-draws
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/draws
One entry per draw, newest first, each quoting the supplier's own order id so it can be chased
upstream.
Requires the `stores.reselling.view` permission.
# Get Linkable Accounts
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-linkable
GET /stores/{storeId}/reselling/linkable
Relationships created by signing up on a supplier's own storefront, before this store existed or
before you thought to use it. They have no store attached, so none of the reselling here applies
to them and the supplier's panel keeps pointing you back at a dashboard that cannot see them.
Matched on your confirmed email address.
Requires the `stores.reselling.view` permission.
# Get Payment Methods
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-payment-methods
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/payment-methods
The supplier's own payment methods, since topping up is paid to them rather than to Komerza.
Only readable by a store that actually holds a relationship with them — a merchant's enabled
gateways are not public information.
Requires the `stores.reselling.view` permission.
# Get Resold Products
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-products
GET /stores/{storeId}/reselling/products
Every product of this store drawn from a supplier, with its cost, margin, and whether it would
currently sell. A resold product can stop being deliverable without its owner touching anything —
the supplier withdraws it, revokes them, raises the price past their ceiling, or runs out — so
each carries the reason it is blocked, if it is.
Requires the `stores.reselling.view` permission.
# Get Suppliers
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-suppliers
GET /stores/{storeId}/reselling/suppliers
Every store this one draws stock from, with what it can currently spend at each.
Requires the `stores.reselling.view` permission.
# Link Account
Source: https://docs.komerza.com/api-reference/endpoint/reselling/link-account
POST /stores/{storeId}/reselling/linkable/{resellerId}
Attaches an off-platform reseller relationship to this store. From then on its draws are served
in-process — stock lands on your own variants, there is no delivery URL to hold, and the balance
already held with that supplier comes across untouched.
Requires the `stores.reselling.update` permission.
# Quote Basket
Source: https://docs.komerza.com/api-reference/endpoint/reselling/quote-basket
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/basket/quote
Give it a bundle id, or lines of your own, and it comes back with the same arithmetic the
purchase will use — including what those lines would have cost bought separately, so a discount
you already have is not counted twice in what you tell yourself you saved.
Requires the `stores.reselling.view` permission.
# Get Review Analytics
Source: https://docs.komerza.com/api-reference/endpoint/reviews/analytics
GET /stores/{storeId}/reviews/analytics
Gets the review analytics for a store.
Requires the `stores.view` permission.
# Get Reviews
Source: https://docs.komerza.com/api-reference/endpoint/reviews/get-list
GET /stores/{storeId}/reviews
Gets the current and previous reviews for a store.
Requires the `stores.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| --------------- | -------- | ---------------------------------------------------- |
| `rating` | integer | Filter by star rating (1-5) |
| `emailAddress` | string | Filter by customer email (case-insensitive contains) |
| `hasReply` | boolean | Filter reviews that have a merchant reply |
| `dateRangeFrom` | datetime | Filter reviews created on or after this date |
| `dateRangeTo` | datetime | Filter reviews created on or before this date |
**Example:**
```bash theme={null}
# 5-star reviews from last 30 days
GET /stores/{storeId}/reviews?filters=rating==5,dateRangeFrom>=2025-10-28
# Reviews without replies, sorted by newest first
GET /stores/{storeId}/reviews?filters=hasReply==false&sorts=-dateRangeFrom
```
# Reply to Review
Source: https://docs.komerza.com/api-reference/endpoint/reviews/reply
PUT /stores/{storeId}/reviews/{reviewId}/reply
Requires the `stores.update` permission.
Replies are **public**. Whatever you send here appears under the review on
your storefront, next to the customer's original words.
## How it works
A review holds **one** reply, stored on the review itself rather than as a thread. That shapes the whole endpoint:
* **`PUT` overwrites.** Call it again on the same review and you replace the previous reply - this is also how you edit one. There is no separate update endpoint.
* **There is no delete.** `reply` is required and must be at least 8 characters, so you cannot blank it out to remove a reply. Replace it with better wording instead.
## Request
```json theme={null}
{
"reply": "Sorry this landed late - your key was reissued the same day and we've since fixed the delay."
}
```
| Field | Rules |
| ------- | ------------------------------------------- |
| `reply` | Required. Between **8 and 4096** characters |
## Permission
Requires **`stores.update`**, not a review-specific scope. A key that can reply to reviews can also change other store settings, so scope keys with that in mind.
## Finding what to reply to
Use [Get Reviews](/api-reference/endpoint/reviews/get-list) with `hasReply==false` to fetch the ones still waiting, worst first:
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/reviews" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=hasReply==false,rating<3" \
--data-urlencode "sorts=-dateRangeFrom"
```
A reply is read by far more future buyers than by the reviewer. Answer the
question the next shopper is silently asking - what went wrong, and what you
did about it.
# Create Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/create
POST /webhooks
Creates a webhook that POSTs the events you subscribe to, for the stores you choose, as signed
JSON. The signing secret is returned once here, so save it to verify the X-Signature header.
Requires the `user.webhooks.create` permission.
**The signing secret is returned once, here, and never again.** There is no
endpoint or dashboard screen that will show it later. Store it when you
receive it; if you lose it, your only option is to delete the webhook and
create a new one.
You can hold up to **25 webhooks per user**, each scoped to one or more of
your stores. URLs must be HTTP or HTTPS, and any that resolve to private or
reserved addresses are rejected.
# Delete Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/delete
DELETE /webhooks/{webhookId}
Removes the webhook and all associated logs from the user account.
Requires the `user.webhooks.delete` permission.
**Deleting a webhook deletes all of its execution logs**, so any delivery
history you would need for debugging goes with it. Export anything you want to
keep first.
# Get Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/get
GET /webhooks/{webhookId}
Retrieves the webhook if it belongs to the currently authenticated user.
Requires the `user.webhooks.view` permission.
# Get Webhooks
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/get-list
GET /webhooks
Returns a list of all account-wide webhooks configured by the user.
Requires the `user.webhooks.view` permission.
# Get Webhook Logs
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/get-logs
GET /webhooks/{webhookId}/logs
Returns a webhook's delivery history: one entry per attempt, with the response code and, on
failures, the reason.
Supports filtering by response code and success/failure status via query parameters.
Requires the `user.webhooks.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| -------------- | ------- | --------------------------------------------------------------------------------- |
| `success` | boolean | Whether the delivery succeeded (a 2xx response) |
| `responseCode` | integer | The HTTP status your endpoint returned. `-1` means paused after repeated failures |
**Example:**
```bash theme={null}
# Failed deliveries only
GET /webhooks/{webhookId}/logs?filters=success==false
# Deliveries your endpoint rejected as unauthorised
GET /webhooks/{webhookId}/logs?filters=responseCode==401
```
# Resend Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/resend
POST /webhooks/{webhookId}/logs/{executionLogId}/resend
Re-queues the Hangfire job for a particular webhook execution log. If the job no longer exists or the user
does not own the log, it fails. This is a single execution log re-send.
Requires the `user.webhooks.update` permission.
# Search Webhook Logs
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/search-logs
GET /webhooks/{webhookId}/logs/search/{query}
Searches logs by log ID, order ID (if the query is a valid GUID), or by URL fragment.
All pagination and date/status filters from the listing endpoint are also supported.
Requires the `user.webhooks.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| -------------- | ------- | --------------------------------------------------------------------------------- |
| `success` | boolean | Whether the delivery succeeded (a 2xx response) |
| `responseCode` | integer | The HTTP status your endpoint returned. `-1` means paused after repeated failures |
**Example:**
```bash theme={null}
# Failed deliveries only
GET /webhooks/{webhookId}/logs?filters=success==false
# Deliveries your endpoint rejected as unauthorised
GET /webhooks/{webhookId}/logs?filters=responseCode==401
```
# Update Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/update
PUT /webhooks/{webhookId}
Overwrites the URL, events, and store IDs.
Requires the `user.webhooks.update` permission.
# Filtering & Sorting
Source: https://docs.komerza.com/api-reference/filtering
Narrow down, order and page through list endpoints
List endpoints return everything by default. Four query parameters let you narrow that down:
| Parameter | Does |
| ---------- | ---------------------------- |
| `filters` | Which items come back |
| `sorts` | What order they come back in |
| `page` | Which page you want |
| `pageSize` | How many per page |
You can use any of them on their own. Endpoints that support them show a **Filterable Fields** table on their reference page - that table is the list of field names you're allowed to use.
## Start with one filter
A filter is three parts stuck together: **field**, **operator**, **value**.
```bash theme={null}
GET /stores/{storeId}/orders?filters=status==pending
```
That's `status` (field) `==` (equals) `pending` (value). Add more by separating with commas - every condition has to match:
```bash theme={null}
# Pending orders, paid by Stripe
GET /stores/{storeId}/orders?filters=status==pending,gateway==stripe
```
Then order them, newest first, with `-` meaning descending:
```bash theme={null}
GET /stores/{storeId}/orders?filters=status==pending&sorts=-dateFrom
```
And take them 50 at a time:
```bash theme={null}
GET /stores/{storeId}/orders?filters=status==pending&sorts=-dateFrom&page=1&pageSize=50
```
That's the whole idea. The rest of this page is the detail.
**A field name that isn't on the endpoint's list is ignored, not rejected.**
You get a `200` with unfiltered results rather than an error. If a filter
seems to do nothing, check the spelling against that endpoint's Filterable
Fields table before looking anywhere else.
## Operators
**Comparing numbers and dates:**
| Operator | Meaning | Example |
| -------- | ------------ | ---------------------- |
| `==` | Equals | `status==pending` |
| `!=` | Not equals | `gateway!=stripe` |
| `>` | Greater than | `rating>3` |
| `<` | Less than | `rating<3` |
| `>=` | At least | `dateFrom>=2026-01-01` |
| `<=` | At most | `dateTo<=2026-06-30` |
**Matching text:**
| Operator | Meaning | Example |
| -------- | ------------------- | ------------------ |
| `@=` | Contains | `email@=gmail` |
| `_=` | Starts with | `subject_=Refund` |
| `_-=` | Ends with | `email_-=.com` |
| `!@=` | Does not contain | `email!@=test` |
| `!_=` | Does not start with | `subject!_=[spam]` |
| `!_-=` | Does not end with | `email!_-=.ru` |
Text matching is case-sensitive. Add a `*` to the end of any operator to ignore case:
```bash theme={null}
?filters=subject@=Premium # matches "Premium", not "premium"
?filters=subject@=*premium # matches both
```
Field names are always case-insensitive, so `dateFrom` and `datefrom` both work.
## Matching several values
Separate values with `|` to match any of them:
```bash theme={null}
# Orders paid by Stripe or PayPal
?filters=gateway==stripe|paypal
```
Put several **fields** in brackets to run the same test against each, matching if any one hits:
```bash theme={null}
# "urgent" in either the subject or the customer's email
?filters=(subject|customerEmail)@=urgent
```
## Values that need care
Enum fields take their numeric value: `visibility==0`, **not**
`visibility==Public`. Passing the name matches nothing and returns an empty
list. Every value is in [Enums & Constants](/api-reference/enums).
Order status and gateway are the exceptions - they take the string name, as
their field tables show.
`dateFrom>=2026-01-01`, or with a time `dateFrom>=2026-01-01T09:30:00Z`.
`isBestSeller==true`. Anything that isn't recognisably a boolean counts as
`false`.
These mean something to the parser, so escape them with a backslash:
| To match | Write |
| --------------- | -------------------- |
| `some,value` | `field@=some\,value` |
| `some\|value` | `field@=some\|value` |
| the text "null" | `field@=\null` |
Unescaped, `field@=some,value` is read as two separate filters.
`!=` leaves out rows where the field is empty. `gateway!=stripe` returns
orders paid another way, but not orders with no gateway at all.
## Two shortcut filters
These aren't real fields, they're prepared questions. Pass `true`; passing `false` is the same as leaving them out.
| Field | On | Matches |
| ---------- | -------- | ----------------------------------------------------------- |
| `lowStock` | Products | Has a license-key variant and fewer than 5 in stock overall |
| `hasReply` | Reviews | Already has a merchant reply |
## Sorting
One field, or several separated by commas. The first is the main order, the rest break ties. `-` means descending.
```bash theme={null}
?sorts=-dateFrom # newest first
?sorts=status,-dateFrom # by status, newest first inside each status
```
When paging through results, sort by something nearly unique. If lots of rows
share the same value, their order can shift between requests, so you may see
the same row twice or miss one entirely.
## Paging
`page` starts at 1. `pageSize` defaults to **20** and is capped at **100** - ask for 500 and you get 100, with no error.
Responses tell you how many pages there are:
```json theme={null}
{
"success": true,
"pages": 5,
"data": [
// items
]
}
```
There's no total item count, so keep requesting pages until you reach `pages`.
A few endpoints use an older style, with the page in the path and a `limit` or
`count` parameter:
```
GET /stores/{storeId}/products/{productId}/variants/{variantId}/items/2?count=50
```
Their reference pages show what they accept.
## Recipes
Real requests you can paste and edit. `curl -G` with `--data-urlencode` matters here: filter values are full of `=`, `>`, `|` and `*`, and letting curl encode them avoids the request being mangled on the way out.
**Last 30 days of pending orders, newest first**
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/orders" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=status==pending,dateFrom>=2026-01-02" \
--data-urlencode "sorts=-dateFrom"
```
**Unanswered 1-2 star reviews**
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/reviews" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=rating<3,hasReply==false" \
--data-urlencode "sorts=-dateRangeFrom"
```
**Open tickets from one customer, oldest first**
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/tickets" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=status==0,customerEmail@=*buyer@example.com" \
--data-urlencode "sorts=dateCreated" \
--data-urlencode "pageSize=50"
```
**Public best-sellers running low on stock**
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/products" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=visibility==0,isBestSeller==true,lowStock==true"
```
## When it doesn't work
| What you see | What's wrong |
| --------------------------------- | ---------------------------------------------------------------------------------------------- |
| The filter changes nothing | The field isn't on that endpoint's list, or is misspelled. Unknown fields are ignored silently |
| Empty list when filtering an enum | You passed the name instead of the number - use `visibility==0` |
| One value became two filters | It contains a comma; escape it as `\,` |
| `pageSize=500` returned 100 items | 100 is the maximum, and larger values are quietly clamped |
| Results shuffle between pages | Equal sort values have no guaranteed order - add a tie-breaker field |
# Introduction
Source: https://docs.komerza.com/api-reference/introduction
The Komerza REST API: what it covers and where to start
The Komerza API is a REST interface to everything in your store. Anything you can do in the dashboard, you can automate: manage your catalogue, read and fulfil orders, issue refunds, look after customers, run coupons and affiliates, and receive events on your own server.
```
https://api.komerza.com
```
} href="/api-reference/quickstart">
Create a key, find your store ID and get a real response back, in three steps.
## What you can build
}>
Sync products, variants, pricing and stock from wherever you already manage
them.
}>
Read orders, deliver them, issue refunds, and push status into your own
systems.
}>
Drive checkout from a site you build yourself, with your own design.
}>
Feed orders and customers into accounting, CRM, fulfilment or analytics.
## Start here
} href="/api-reference/authentication">
API keys, required headers and scopes.
} href="/api-reference/responses">
The response envelope, list responses and how failures look.
} href="/api-reference/conventions">
IDs, dates, enums, money and store scoping.
} href="/api-reference/filtering">
Narrow, order and page through list endpoints.
## Related
} href="/api-reference/sdks">
Official libraries for .NET, JavaScript, Python, Go and PHP.
} href="/guides/webhooks">
Order events pushed to your server, instead of polling for them.
} href="/storefront/introduction">
Cart and checkout on a site you host yourself.
} href="https://status.komerza.com">
Live API availability.
# Quick Start
Source: https://docs.komerza.com/api-reference/quickstart
Make your first Komerza API call
Three steps: create a key, call `/user` to find your store, then call something with that store ID. You need a Komerza account and dashboard access.
## 1. Create an API key
1. Open **[Account Settings → API Keys](https://dashboard.komerza.com/account?api)** in the dashboard
2. Click **Create New API Key** and give it a name you'll recognise later
3. Select the scopes it needs - `user.view` and `stores.view` are enough for this guide
4. Copy the key
The key is shown once. Store it in an environment variable or a secrets
manager, never in source control, and give each integration its own key so you
can revoke one without breaking the rest.
Every request needs two headers:
| Header | Value |
| --------------- | ---------------------------------------------------- |
| `Authorization` | `Bearer YOUR_API_KEY` |
| `User-Agent` | Something that identifies your app, e.g. `MyApp/1.0` |
## 2. Find your store ID
Almost every endpoint is scoped to a store. `GET /user` returns your account, with the stores on it:
```bash theme={null}
curl "https://api.komerza.com/user" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0"
```
```json theme={null}
{
"success": true,
"message": null,
"code": null,
"data": {
"id": "…",
"email": "you@example.com",
"plan": 1,
"stores": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "My Store",
"url": "mystore",
"currencyCode": "USD"
}
]
}
}
```
The `id` of the store you want is what every `{storeId}` in these docs refers to.
You can also copy it from the dashboard: **Online Store → Settings →
General**, where the **Store ID** field has a copy button next to it.
## 3. Call an endpoint
Fetch the store you just found:
```bash theme={null}
curl "https://api.komerza.com/stores/{storeId}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0"
```
```json theme={null}
{
"success": true,
"message": null,
"code": null,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"dateCreated": "2026-01-15T10:30:00Z",
"name": "My Store",
"description": "Digital goods, delivered instantly",
"url": "mystore",
"customDomain": "shop.example.com",
"currencyCode": "USD",
"rating": 4.8,
"isTicketingEnabled": true,
"isCustomerBalanceEnabled": false
}
}
```
That's it - you're authenticated and reading real data.
## The response envelope
Every response has the same four fields:
| Field | What it is |
| --------- | ------------------------------------------------------ |
| `success` | Whether the request worked |
| `message` | Human-readable detail, often `null` on success |
| `code` | Error code when something went wrong, otherwise `null` |
| `data` | The payload - an object, an array, or `null` |
Errors keep the same shape:
```json theme={null}
{
"success": false,
"message": "Access denied."
}
```
Check `success` rather than assuming a `200` means you got what you asked for.
## When it doesn't work
The key is missing, malformed or revoked. Check you sent `Authorization:
Bearer YOUR_KEY` including the `Bearer ` prefix, and that the key still
exists in the dashboard.
The key is valid but lacks the scope for that endpoint. Scopes follow the
resource: reading orders needs `stores.orders.view`, refunding them needs
`stores.orders.refund`. The full list is in [Scopes](/api-reference/scopes).
Scopes are fixed when the key is created, so create a new key with the ones
you need.
Usually a store ID that doesn't exist, belongs to another account, or picked
up a stray character. Confirm it against `GET /user`. Check the path too:
the base URL is `https://api.komerza.com` with **no** `/api` prefix.
You're sending requests faster than allowed. Back off and retry with
increasing delays rather than immediately.
## Next steps
} href="/api-reference/filtering">
Narrow, order and page through list endpoints.
} href="/api-reference/scopes">
Every permission a key can carry.
} href="/api-reference/endpoint/products/get-list">
Read and manage your catalogue.
} href="/api-reference/endpoint/orders/get-list">
Query orders, deliver and refund them.
} href="/guides/webhooks">
Get order events pushed to your server instead of polling.
} href="/api-reference/sdks">
Official libraries for .NET, JavaScript, Python, Go and PHP.
# Responses & Errors
Source: https://docs.komerza.com/api-reference/responses
The response envelope, list responses, and how failures are reported
Every response uses the same envelope, whether it succeeded or not.
| Field | Type | Description |
| --------- | ----------------------- | -------------------------------------------------- |
| `success` | boolean | Whether the request did what you asked |
| `message` | string \| null | Human-readable detail, usually `null` on success |
| `code` | string \| null | Error code when something failed, otherwise `null` |
| `data` | object \| array \| null | The payload |
```json theme={null}
{
"success": true,
"message": null,
"code": null,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "My Store"
}
}
```
Branch on `success`, not on the status code alone, and read `code` rather than
`message` when handling failures. Messages are written for humans and can be
reworded; codes are stable.
## List responses
Endpoints returning a list add `pages`: how many pages exist at the current page size. There's no total item count, so page until you reach it.
```json theme={null}
{
"success": true,
"pages": 5,
"data": [
// items
]
}
```
See [Filtering & Sorting](/api-reference/filtering) for narrowing, ordering and paging those results.
## Errors
```json theme={null}
{
"success": false,
"message": "Access denied.",
"code": "AccessDenied",
"data": null
}
```
`code` is a stable PascalCase identifier: `AccessDenied`, `NotFound`, `ObjectConflict`, `ValidationError`, `UpgradeRequired` and so on.
### Validation errors
Validation failures add `invalidFields`, one entry per problem, each formatted `Field: what's wrong`:
```json theme={null}
{
"success": false,
"message": "Validation failed",
"code": "ValidationError",
"data": null,
"invalidFields": ["Name: Must be between 3 and 64 characters"]
}
```
Surface these to whoever submitted the data - they name the field and the rule it broke.
### HTTP status codes
| Status | Usually means |
| ------ | ----------------------------------------------------- |
| `400` | Malformed request, or validation failed |
| `401` | Missing, malformed or revoked API key |
| `403` | Valid key, but it lacks the scope for this endpoint |
| `404` | Wrong ID, or a resource on an account you can't reach |
| `409` | Conflicts with something that already exists |
| `429` | Too many requests |
| `5xx` | Our side |
## Retrying
Retry `429` and `5xx` with increasing delays rather than immediately. Don't retry `4xx` responses other than `429` - the request will fail the same way until you change it.
If a `5xx` persists, check [status.komerza.com](https://status.komerza.com) before digging into your own code.
} href="/api-reference/error-codes">
The full list of `code` values, what triggers each and how to recover.
# Scopes
Source: https://docs.komerza.com/api-reference/scopes
Complete reference for all available API key permission scopes
## Overview
API keys use a permission-based scope system that controls access to specific endpoints and operations. Each API key can be configured with one or more scopes to limit access according to your integration needs.
API keys can be generated and scopes can be configured from your [Komerza
Dashboard](https://dashboard.komerza.com/account?api).
## Scope Categories
### Store Management
**Permission to view stores** Read access to store information, settings,
and configuration. Required for most store-related read operations.
**Permission to update stores** Modify store configuration, settings,
branding, and operational parameters.
**Permission to create new stores** Create new store instances. Typically used
for multi-store applications or store management platforms.
**Permission to view store analytics** Access store performance metrics,
sales data, traffic statistics, and analytical insights.
### Categories
**Permission to view categories in stores** Read access to product
categories, category hierarchy, and organization structure.
**Permission to create new categories in stores** Add new product categories
to organize your store catalog.
**Permission to update categories in stores** Modify category information,
reorganize hierarchy, and update category settings.
**Permission to delete categories in stores** Remove categories from the
store. Ensure products are reassigned before deleting categories.
### Products & Inventory
**Permission to view products in stores** Read access to product catalog,
variants, pricing, and inventory information.
**Permission to create new products in stores** Add new products to the store
catalog, including variants and pricing.
**Permission to update products in stores** Modify existing products, update
pricing, inventory levels, and product details.
**Permission to delete products in stores** Remove products from the store
catalog. Use with caution as this action may affect order history.
### Customer Management
**Permission to view customers in stores** Access customer profiles, contact
information, and purchase history.
**Permission to create new customers in stores** Add new customer profiles to
the store database.
**Permission to update customers in stores** Modify customer information,
update balances, and manage customer accounts.
### Order Processing
**Permission to view orders in stores** Access order information, payment
status, and order history.
**Permission to create orders** Process new orders, including payment
processing and order creation.
**Permission to deliver orders in stores** Mark orders as delivered, update
tracking information, and manage fulfillment.
**Permission to refund orders** Process refunds, partial refunds, and handle
order cancellations.
### Marketing & Promotions
**Permission to view coupons in stores** Access coupon information, usage
statistics, and discount configurations.
**Permission to create new coupons in stores** Create discount codes,
promotional campaigns, and coupon configurations.
**Permission to update coupons in stores** Modify existing coupons, update
discount amounts, and change validity periods.
**Permission to delete coupons in stores** Remove coupons from the system.
Active coupons should be disabled rather than deleted.
### Affiliate Program
**Permission to view affiliates in stores** Access affiliate information,
commission data, and performance statistics.
**Permission to update affiliates in stores** Modify affiliate settings,
commission rates, and payout configurations.
### Support & Communication
**Permission to view tickets in stores** Access support tickets, customer
inquiries, and communication history.
**Permission to update tickets in stores** Respond to tickets, update ticket
status, and manage customer support workflows.
**Permission to delete tickets in stores** Remove tickets from the system.
Consider archiving instead of deleting for audit purposes.
**Permission to flag reviews in stores** Mark reviews as inappropriate,
spam, or requiring moderation.
### User Account & Platform
**Permission to view user profile information** Access user account details,
preferences, and profile information.
**Permission to view enabled cryptocurrencies for a user** Access
cryptocurrency payment settings and enabled payment methods.
This scope is deprecated and will be removed in future versions. Please avoid
using it.
### Webhooks & Integrations
**Permission to view webhooks** Access webhook configurations, event
subscriptions, and delivery logs.
**Permission to manage webhooks: create** Set up new webhook endpoints and
event subscriptions.
**Permission to manage webhooks: update** Modify existing webhook
configurations and event subscriptions.
**Permission to manage webhooks: delete** Remove webhook endpoints and
cancel event subscriptions.
## Best Practices
### Principle of Least Privilege
Only request the minimum scopes necessary for your application. This reduces security risks and makes it easier to audit API access.
### Scope Planning
Plan your required scopes during the design phase of your integration. Consider:
* Which resources you need to read vs. modify
* Whether you need create/delete permissions
* If your application handles sensitive customer data
# SDKs
Source: https://docs.komerza.com/api-reference/sdks
Official SDKs for the Komerza API - coming soon
## Overview
We're building official SDKs to make integrating with the Komerza API even easier. These SDKs will provide idiomatic interfaces, automatic error handling, and built-in best practices for each programming language.
All SDKs are currently in development. While you wait, you can use the API
directly via HTTP requests as shown in our [Quick Start
Guide](/api-reference/quickstart).
## Planned SDKs
**Coming Soon** Full-featured SDK for .NET applications with strong typing
and async support.
**Coming Soon** Modern SDK with TypeScript definitions for Node.js and browser
environments.
**Coming Soon** Pythonic interface with support for async/await and popular
frameworks.
**Coming Soon** Modern PHP SDK with Composer support and PSR-compliant design.
**Coming Soon** Idiomatic Go SDK with proper error handling and context
support.
## SDK Previews
Here's what the SDK interfaces will look like:
```csharp .NET theme={null}
using Komerza.Api;
var client = new KomerzaClient("your-api-key");
// Get store information
var store = await client.Stores.GetAsync(storeId);
// List products with pagination
var products = await client.Products.ListAsync(storeId, new ProductListOptions
{
Page = 1,
PageSize = 20
});
// Create a new product
var newProduct = await client.Products.CreateAsync(storeId, new CreateProductRequest
{
Name = "New Product",
Price = 29.99m,
Currency = "USD"
});
```
```javascript JavaScript/TypeScript theme={null}
import { KomerzaClient } from "@komerza/api";
const client = new KomerzaClient("your-api-key");
// Get store information
const store = await client.stores.get(storeId);
// List products with pagination
const products = await client.products.list(storeId, {
page: 1,
pageSize: 20,
});
// Create a new product
const newProduct = await client.products.create(storeId, {
name: "New Product",
price: 29.99,
currency: "USD",
});
```
```python Python theme={null}
from komerza import KomerzaClient
client = KomerzaClient('your-api-key')
# Get store information
store = await client.stores.get(store_id)
# List products with pagination
products = await client.products.list(store_id, page=1, page_size=20)
# Create a new product
new_product = await client.products.create(store_id, {
'name': 'New Product',
'price': 29.99,
'currency': 'USD'
})
```
```php PHP theme={null}
stores->get($storeId);
// List products with pagination
$products = $client->products->list($storeId, [
'page' => 1,
'pageSize' => 20
]);
// Create a new product
$newProduct = $client->products->create($storeId, [
'name' => 'New Product',
'price' => 29.99,
'currency' => 'USD'
]);
```
```go Go theme={null}
package main
import (
"context"
"github.com/komerza/go-sdk"
)
func main() {
client := komerza.NewClient("your-api-key")
ctx := context.Background()
// Get store information
store, err := client.Stores.Get(ctx, storeID)
if err != nil {
// Handle error
}
// List products with pagination
products, err := client.Products.List(ctx, storeID, &komerza.ProductListOptions{
Page: 1,
PageSize: 20,
})
// Create a new product
newProduct, err := client.Products.Create(ctx, storeID, &komerza.CreateProductRequest{
Name: "New Product",
Price: 29.99,
Currency: "USD",
})
}
```
## Get Notified
Want to be the first to know when an SDK is released?
Get updates in our Telegram developer community
Watch our GitHub organization for new SDK releases
## In the Meantime
While waiting for the official SDKs, you can:
### Use the REST API Directly
Follow our [Quick Start Guide](/api-reference/quickstart) to start building with HTTP requests right away.
### Build Your Own Wrapper
Create a simple wrapper around the API for your specific use case. Our [API Reference](/api-reference/introduction) provides all the details you need.
### Community SDKs
Check if there are any community-built SDKs available. We'll showcase quality community contributions here.
## Contributing
Interested in contributing to an SDK or building one for another language?
Get in touch about SDK development opportunities
We welcome contributions and are happy to work with the community to build SDKs for additional programming languages and frameworks.
## Stay Updated
Check our help center for SDK documentation as it becomes available
Learn the API while waiting for your preferred SDK
# Create Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/create
POST /stores/{storeId}/affiliates/{customerId}
Creates a new affiliate record for the given customer in this store.
Requires the `stores.affiliates.update` permission.
# Delete Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/delete
DELETE /stores/{storeId}/affiliates/{customerId}
Permanently removes an affiliate and all their payout records from the store.
Requires the `stores.affiliates.update` permission.
# Get Affiliates
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/get-list
GET /stores/{storeId}/affiliates
Retrieves affiliates for the specified store using query-based filter options.
Supports filtering, sorting, and pagination via query parameters.
Requires the `stores.affiliates.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ---------------------- | ------- | ----------------------------------------------------------- |
| `isEnabled` | boolean | Filter by affiliate enabled status |
| `email` | string | Filter by email address (contains) |
| `link` | string | Filter by affiliate link/code (contains) |
| `balanceFrom` | number | Minimum affiliate balance (inclusive) |
| `balanceTo` | number | Maximum affiliate balance (inclusive) |
| `returnPercentageFrom` | integer | Minimum return percentage (inclusive) |
| `returnPercentageTo` | integer | Maximum return percentage (inclusive) |
| `percentageOffFrom` | integer | Minimum discount percentage (inclusive) |
| `percentageOffTo` | integer | Maximum discount percentage (inclusive) |
| `referredOrdersFrom` | integer | Minimum referred delivered orders (inclusive) |
| `referredOrdersTo` | integer | Maximum referred delivered orders (inclusive) |
| `allTimeEarnedFrom` | number | Minimum all-time earned amount (inclusive) |
| `allTimeEarnedTo` | number | Maximum all-time earned amount (inclusive) |
| `payoutCountFrom` | integer | Minimum payout count (inclusive) |
| `payoutCountTo` | integer | Maximum payout count (inclusive) |
| `dateFrom` | string | Filter affiliates created from this date/time (inclusive) |
| `dateTo` | string | Filter affiliates created before this date/time (inclusive) |
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | -------- | ----------------------------------------------------- |
| `isEnabled` | boolean | Filter by whether the affiliate profile is enabled |
| `email` | string | Filter by affiliate email (case-insensitive contains) |
| `link` | string | Filter by referral link or code |
| `balance` | number | Current unpaid balance |
| `allTimeEarned` | number | Total commission earned across all time |
| `referredOrders` | integer | Number of orders attributed to this affiliate |
| `payoutCount` | integer | Number of payouts recorded against this affiliate |
| `returnPercentage` | number | The affiliate's commission rate |
| `percentageOff` | number | The customer discount applied to their referrals |
| `dateCreated` | datetime | When the affiliate profile was created |
Numeric and date fields take ranges through the comparison operators rather than separate from/to parameters.
**Sortable:** `isEnabled`, `email`, `link`, `referredOrders`, `allTimeEarned`, `payoutCount`, `dateCreated`.
**Example:**
```bash theme={null}
# Enabled affiliates who have earned over 100, biggest earners first
GET /stores/{storeId}/affiliates?filters=isEnabled==true,allTimeEarned>=100&sorts=-allTimeEarned
# Affiliates with an outstanding balance and at least one referred order
GET /stores/{storeId}/affiliates?filters=balance>0,referredOrders>=1
```
# Invite Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/invite
POST /stores/{storeId}/affiliates/invite
Invites an affiliate, this will create a customer profile if one does not exist and takes in a customer email
address instead.
Requires the `stores.affiliates.update` permission.
# Search Affiliates
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/search
GET /stores/{storeId}/affiliates/search
Searches affiliates by the `query` parameter and applies filtering/sorting/pagination.
The query is matched against affiliate email, affiliate link/code, affiliate ID, and customer ID.
Requires the `stores.affiliates.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | -------- | ----------------------------------------------------- |
| `isEnabled` | boolean | Filter by whether the affiliate profile is enabled |
| `email` | string | Filter by affiliate email (case-insensitive contains) |
| `link` | string | Filter by referral link or code |
| `balance` | number | Current unpaid balance |
| `allTimeEarned` | number | Total commission earned across all time |
| `referredOrders` | integer | Number of orders attributed to this affiliate |
| `payoutCount` | integer | Number of payouts recorded against this affiliate |
| `returnPercentage` | number | The affiliate's commission rate |
| `percentageOff` | number | The customer discount applied to their referrals |
| `dateCreated` | datetime | When the affiliate profile was created |
Numeric and date fields take ranges through the comparison operators rather than separate from/to parameters.
**Sortable:** `isEnabled`, `email`, `link`, `referredOrders`, `allTimeEarned`, `payoutCount`, `dateCreated`.
**Example:**
```bash theme={null}
# Enabled affiliates who have earned over 100, biggest earners first
GET /stores/{storeId}/affiliates?filters=isEnabled==true,allTimeEarned>=100&sorts=-allTimeEarned
# Affiliates with an outstanding balance and at least one referred order
GET /stores/{storeId}/affiliates?filters=balance>0,referredOrders>=1
```
# Get Affiliate Statistics
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/stats
GET /stores/{storeId}/affiliates/stats
Retrieves the affiliate statistics associated with a specific store.
Requires the `stores.affiliates.view` permission.
# Update Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/update
PUT /stores/{storeId}/affiliates/{customerId}
Updates affiliate settings for the given customer in this store.
Requires the `stores.affiliates.update` permission.
# Create Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/create
POST /stores/{storeId}/categories
Creates a new category in the store.
Requires the `stores.categories.create` permission.
# Delete Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/delete
DELETE /stores/{storeId}/categories/{categoryId}
Deletes an existing category from the store.
Requires the `stores.categories.delete` permission.
# Get Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/get
GET /stores/{storeId}/categories/{categoryId}
Retrieves a category by its ID with full product details.
Requires the `stores.categories.view` permission.
# Get All Categories
Source: https://docs.komerza.com/api-reference/endpoint/categories/get-all
GET /stores/{storeId}/categories/all
Retrieves all categories in the store as references (no pagination).
Requires the `stores.categories.view` permission.
# Get Categories
Source: https://docs.komerza.com/api-reference/endpoint/categories/get-list
GET /stores/{storeId}/categories
Gets the categories in the store with pagination and filtering.
Requires the `stores.categories.view` permission.
# Order Categories
Source: https://docs.komerza.com/api-reference/endpoint/categories/order
PATCH /stores/{storeId}/categories/order
Updates the display order of categories.
Requires the `stores.categories.update` permission.
# Order Products in Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/order-products
PATCH /stores/{storeId}/categories/{categoryId}/products/order
Updates the display order of products within a category.
Requires the `stores.categories.update` permission.
# Update Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/update
PUT /stores/{storeId}/categories/{categoryId}
Updates an existing category in the store.
Requires the `stores.categories.update` permission.
# Create Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/create
POST /stores/{storeId}/coupons
Creates a new coupon in the store.
Requires the `stores.coupons.create` permission.
# Delete Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/delete
DELETE /stores/{storeId}/coupons/{couponId}
Deletes an existing coupon from the store.
Requires the `stores.coupons.delete` permission.
# Get Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/get
GET /stores/{storeId}/coupons/{couponId}
Gets a coupon by ID.
Requires the `stores.coupons.view` permission.
# Get All Coupons
Source: https://docs.komerza.com/api-reference/endpoint/coupons/get-all
GET /stores/{storeId}/coupons/all
Gets the coupons in the store as references.
Requires the `stores.coupons.view` permission.
# Get Coupons
Source: https://docs.komerza.com/api-reference/endpoint/coupons/get-list
GET /stores/{storeId}/coupons
Gets the coupons in the store.
Requires the `stores.coupons.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------ |
| `usesFrom` | integer | Filter by minimum number of times coupon has been used |
| `usesTo` | integer | Filter by maximum number of times coupon has been used |
| `maximumUsesFrom` | integer | Filter by minimum maximum uses allowed |
| `maximumUsesTo` | integer | Filter by maximum maximum uses allowed |
| `percentageFrom` | decimal | Filter by minimum percentage discount |
| `percentageTo` | decimal | Filter by maximum percentage discount |
| `amountFrom` | decimal | Filter by minimum amount discount |
| `amountTo` | decimal | Filter by maximum amount discount |
| `isEnabled` | boolean | Filter by whether coupon is enabled |
**Example:**
```bash theme={null}
# Active coupons with 20-50% discount
GET /stores/{storeId}/coupons?filters=isEnabled==true,percentageFrom>=20,percentageTo<=50
# Heavily used coupons
GET /stores/{storeId}/coupons?filters=usesFrom>=100&sorts=-uses
```
# Search Coupons
Source: https://docs.komerza.com/api-reference/endpoint/coupons/search
GET /stores/{storeId}/coupons/search/{query}
Searches the coupons in the store by the given query.
Requires the `stores.coupons.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------ |
| `usesFrom` | integer | Filter by minimum number of times coupon has been used |
| `usesTo` | integer | Filter by maximum number of times coupon has been used |
| `maximumUsesFrom` | integer | Filter by minimum maximum uses allowed |
| `maximumUsesTo` | integer | Filter by maximum maximum uses allowed |
| `percentageFrom` | decimal | Filter by minimum percentage discount |
| `percentageTo` | decimal | Filter by maximum percentage discount |
| `amountFrom` | decimal | Filter by minimum amount discount |
| `amountTo` | decimal | Filter by maximum amount discount |
| `isEnabled` | boolean | Filter by whether coupon is enabled |
**Example:**
```bash theme={null}
# Active coupons with 20-50% discount
GET /stores/{storeId}/coupons?filters=isEnabled==true,percentageFrom>=20,percentageTo<=50
# Heavily used coupons
GET /stores/{storeId}/coupons?filters=usesFrom>=100&sorts=-uses
```
# Update Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/update
PUT /stores/{storeId}/coupons/{couponId}
Requires the `stores.coupons.update` permission.
# Create Customer
Source: https://docs.komerza.com/api-reference/endpoint/customers/create
POST /stores/{storeId}/customers
Registers a customer for the store using their email.
If the customer already exists and is linked to the store, a 409 conflict is returned.
Requires the `stores.customers.create` permission.
# Get Customer
Source: https://docs.komerza.com/api-reference/endpoint/customers/get
GET /stores/{storeId}/customers/{customerId}
Retrieves detailed information for a single customer in the specified store.
Requires the `stores.customers.view` permission.
# Get Customer by Email
Source: https://docs.komerza.com/api-reference/endpoint/customers/get-by-email
GET /stores/{storeId}/customers/{customerEmail}/email
Retrieves detailed information for a single customer in the specified store.
Requires the `stores.customers.view` permission.
# Get Customers
Source: https://docs.komerza.com/api-reference/endpoint/customers/get-list
GET /stores/{storeId}/customers
Retrieves a paginated list of customers for the specified store.
Requires the `stores.customers.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | ------- | ---------------------------------------------------- |
| `email` | string | Filter by email address (case-insensitive contains) |
| `ipAddress` | string | Filter by IP address (case-insensitive contains) |
| `balanceFrom` | decimal | Filter by minimum balance (inclusive) |
| `balanceTo` | decimal | Filter by maximum balance (inclusive) |
| `stripeCustomerId` | string | Filter by Stripe customer ID (exact match) |
| `affiliateCode` | string | Filter by affiliate code (case-insensitive contains) |
**Example:**
```bash theme={null}
# Customers with balance between $10 and $100
GET /stores/{storeId}/customers?filters=balanceFrom>=10,balanceTo<=100
# Customers by affiliate code
GET /stores/{storeId}/customers?filters=affiliateCode@=*PROMO
```
# Search Customers
Source: https://docs.komerza.com/api-reference/endpoint/customers/search
GET /stores/{storeId}/customers/search/{query}
Retrieves a paginated list of customers for the specified store, matching the provided query.
Requires the `stores.customers.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | ------- | ---------------------------------------------------- |
| `email` | string | Filter by email address (case-insensitive contains) |
| `ipAddress` | string | Filter by IP address (case-insensitive contains) |
| `balanceFrom` | decimal | Filter by minimum balance (inclusive) |
| `balanceTo` | decimal | Filter by maximum balance (inclusive) |
| `stripeCustomerId` | string | Filter by Stripe customer ID (exact match) |
| `affiliateCode` | string | Filter by affiliate code (case-insensitive contains) |
**Example:**
```bash theme={null}
# Customers with balance between $10 and $100
GET /stores/{storeId}/customers?filters=balanceFrom>=10,balanceTo<=100
# Customers by affiliate code
GET /stores/{storeId}/customers?filters=affiliateCode@=*PROMO
```
# Update Customer Balance
Source: https://docs.komerza.com/api-reference/endpoint/customers/update-balance
PATCH /stores/{storeId}/customers/{customerId}/balance
Updates the store-specific balance for a customer.
Requires the `stores.customers.update` permission.
**Store credit is real spendable value.** The balance applies immediately at
your checkout and never expires, so treat this like issuing money.
It is not cashable - customers cannot withdraw it - and a top-up order can
never be refunded to balance, since that would credit them twice.
# Approve Custom Payment
Source: https://docs.komerza.com/api-reference/endpoint/orders/approve-custom-payment
POST /stores/{storeId}/paymentMethods/custom/orders/{orderId}/approve
Approves a pending custom payment order, marking it as delivered.
Requires the `stores.orders.deliver` permission.
# Create Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/create
POST /stores/{storeId}/orders
Creates a new order for a customer in the admin dashboard.
Requires the `stores.orders.create` permission.
## Payment Methods
When creating an order, specify the `paymentMethod` field using one of the supported payment method codes. See the [Payment Methods reference](/api-reference/payment-methods) for the complete list of available options.
## Redirecting to Checkout
After successfully creating an order, redirect the customer to the hosted checkout page to complete payment:
```
https://checkout.komerza.com/checkout/{orderId}
```
### Example
```javascript theme={null}
const response = await fetch(`/stores/${storeId}/orders`, {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}` },
body: JSON.stringify(orderData),
});
const result = await response.json();
if (result.success) {
const orderId = result.data.id;
window.location.href = `https://checkout.komerza.com/checkout/${orderId}`;
}
```
The checkout page handles payment processing, order confirmation, and receipt
generation automatically.
# Deliver Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/deliver
PUT /stores/{storeId}/orders/{orderId}/deliver
Forces delivery of an existing order as if purchased normally.
No action is taken if the order is already delivered.
Requires the `stores.orders.deliver` permission.
**This does not verify that you were paid.** It runs the full delivery flow as
though payment had arrived: stock is consumed, license keys leave your
inventory, the receipt email is sent, `order.completed` fires and Discord roles
are granted.
Confirm the payment first. For custom payment methods, use [Approve Custom
Payment](/api-reference/endpoint/orders/approve-custom-payment) instead, which
is the reviewed path.
Manually delivered orders can only ever be refunded to **customer balance**,
never back to the gateway.
# Get Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/get
GET /stores/{storeId}/orders/{orderId}
Retrieve order details by ID in the given store.
Requires the `stores.orders.view` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
# Get Orders
Source: https://docs.komerza.com/api-reference/endpoint/orders/get-list
GET /stores/{storeId}/orders
Gets orders in the specified store and optionally filters by the given criteria.
Requires the `stores.orders.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ---------- | -------- | -------------------------------------------------------------------------------- |
| `dateFrom` | datetime | Filter orders created on or after this date |
| `dateTo` | datetime | Filter orders created on or before this date |
| `status` | string | Filter by order status. See [Order Statuses](/api-reference/payment-status) |
| `gateway` | string | Filter by payment gateway. See [Payment Methods](/api-reference/payment-methods) |
| `email` | string | Filter by customer email address |
**Example:**
```bash theme={null}
# Orders from last 30 days with "pending" status
GET /stores/{storeId}/orders?filters=dateFrom>=2025-10-28,status==pending
# Orders paid via Stripe, sorted by newest first
GET /stores/{storeId}/orders?filters=gateway==stripe&sorts=-dateFrom
```
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
# Get Proof of Payment
Source: https://docs.komerza.com/api-reference/endpoint/orders/get-payment-proof
GET /stores/{storeId}/paymentMethods/custom/orders/{orderId}/proof
Gets the proof of payment submitted by a customer for an order.
Requires the `stores.orders.view` permission.
# Re-deliver Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/redeliver
PUT /stores/{storeId}/orders/{orderId}/redeliver
Forces redelivery of an already delivered order as if purchased normally.
Requires the `stores.orders.deliver` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
**This reruns the entire delivery, exactly like a fresh purchase.** It takes
**new license keys** from your inventory, re-fires dynamic delivery webhooks,
and emails the customer again.
Call it once, deliberately. Repeated calls consume stock every time, and there
is no way to return issued keys to inventory.
# Refund Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/refund
DELETE /stores/{storeId}/orders/{orderId}/refund
Refunds a delivered order for a customer in the admin dashboard.
Requires the `stores.orders.refund` permission.
**Refunds move real money and cannot be undone.** Only **Delivered** orders can
be refunded, always for the **full amount** - there are no partial refunds.
A successful refund also reverses any affiliate commission and the Buyer
Protection accrual on that order.
For cryptocurrency, custom payment methods, Cash App and PayPal Friends &
Family, marking an order refunded **moves no money** - you must return the
funds yourself. Those methods can only refund to customer balance.
# Reject Custom Payment
Source: https://docs.komerza.com/api-reference/endpoint/orders/reject-custom-payment
POST /stores/{storeId}/paymentMethods/custom/orders/{orderId}/reject
Rejects a pending custom payment order, cancelling it.
Requires the `stores.orders.deliver` permission.
# Resend Receipt
Source: https://docs.komerza.com/api-reference/endpoint/orders/resend-receipt
PUT /stores/{storeId}/orders/{orderId}/resend
Resends the delivery receipt to the customer for a previously delivered order.
Rate limited due to sensitivity.
Requires the `stores.orders.deliver` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
# Search Orders
Source: https://docs.komerza.com/api-reference/endpoint/orders/search
GET /stores/{storeId}/orders/search/{query}
Search orders by ID or customer email in the specified store.
Requires the `stores.orders.view` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ---------- | -------- | -------------------------------------------------------------------------------- |
| `dateFrom` | datetime | Filter orders created on or after this date |
| `dateTo` | datetime | Filter orders created on or before this date |
| `status` | string | Filter by order status. See [Order Statuses](/api-reference/payment-status) |
| `gateway` | string | Filter by payment gateway. See [Payment Methods](/api-reference/payment-methods) |
| `email` | string | Filter by customer email address |
**Example:**
```bash theme={null}
# Orders from last 30 days with "pending" status
GET /stores/{storeId}/orders?filters=dateFrom>=2025-10-28,status==pending
# Orders paid via Stripe, sorted by newest first
GET /stores/{storeId}/orders?filters=gateway==stripe&sorts=-dateFrom
```
# Create Product
Source: https://docs.komerza.com/api-reference/endpoint/products/create
POST /stores/{storeId}/products
Creates a new product in the store.
Requires the `stores.products.create` permission.
# Delete Product
Source: https://docs.komerza.com/api-reference/endpoint/products/delete
DELETE /stores/{storeId}/products/{productId}
Deletes a product from the store.
Requires the `stores.products.delete` permission.
**Deleting a product takes its reviews with it.** Variants, custom fields,
volume discounts and images go too, and none of it can be restored.
Files already delivered to customers are kept, so past buyers keep their
downloads. If you only want a product off the storefront, set its privacy to
Private or On Hold instead of deleting it.
# Bulk Delete Products
Source: https://docs.komerza.com/api-reference/endpoint/products/delete-bulk
DELETE /stores/{storeId}/products
Deletes multiple products in one request. This will delete all variants, files, images, and reviews
associated with each product. Products that don't exist are silently skipped.
Requires the `stores.products.delete` permission.
**Irreversible, across every product in the request.** Each one loses its
variants, custom fields, volume discounts, images and reviews.
Files already delivered to customers are kept. To hide products rather than
destroy them, bulk-update their privacy instead.
# Duplicate Product
Source: https://docs.komerza.com/api-reference/endpoint/products/duplicate
POST /stores/{storeId}/products/{productId}/duplicate
Creates a copy of an existing product.
Requires the `stores.products.create` permission.
# Get Product
Source: https://docs.komerza.com/api-reference/endpoint/products/get
GET /stores/{storeId}/products/{productId}
Retrieves a product by its ID.
Requires the `stores.products.view` permission.
# Get All Products
Source: https://docs.komerza.com/api-reference/endpoint/products/get-all
GET /stores/{storeId}/products/all
Retrieves all products in the store as redacted references (no pagination).
Requires the `stores.products.view` permission.
# Get Products
Source: https://docs.komerza.com/api-reference/endpoint/products/get-list
GET /stores/{storeId}/products
Retrieves products in pages.
Requires the `stores.products.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------------------------------------ |
| `visibility` | integer | Filter by privacy level. See [Product Privacy](/api-reference/enums#product-privacy) |
| `isBestSeller` | boolean | Filter best-seller products |
| `shouldBlockVpns` | boolean | Filter products that block VPNs |
| `lowStock` | boolean | Filter products with low stock availability |
**Example:**
```bash theme={null}
# Public best-sellers with low stock
GET /stores/{storeId}/products?filters=visibility==0,isBestSeller==true,lowStock==true
# Products that don't block VPNs
GET /stores/{storeId}/products?filters=shouldBlockVpns==false
```
# Get Product by Slug
Source: https://docs.komerza.com/api-reference/endpoint/products/get-slug
GET /stores/{storeId}/products/slug/{slug}
Retrieves a product by its slug.
Requires the `stores.products.view` permission.
# Order Products
Source: https://docs.komerza.com/api-reference/endpoint/products/order
PATCH /stores/{storeId}/products/order
Updates the display order of products.
Requires the `stores.products.update` permission.
# Order Product Images
Source: https://docs.komerza.com/api-reference/endpoint/products/order-images
PATCH /stores/{storeId}/products/{productId}/images/order
Updates the display order of images for a product.
Requires the `stores.products.update` permission.
# Search Products
Source: https://docs.komerza.com/api-reference/endpoint/products/search
GET /stores/{storeId}/products/search/{query}
Searches products by query within the store.
Requires the `stores.products.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------------------------------------ |
| `visibility` | integer | Filter by privacy level. See [Product Privacy](/api-reference/enums#product-privacy) |
| `isBestSeller` | boolean | Filter best-seller products |
| `shouldBlockVpns` | boolean | Filter products that block VPNs |
| `lowStock` | boolean | Filter products with low stock availability |
**Example:**
```bash theme={null}
# Public best-sellers with low stock
GET /stores/{storeId}/products?filters=visibility==0,isBestSeller==true,lowStock==true
# Products that don't block VPNs
GET /stores/{storeId}/products?filters=shouldBlockVpns==false
```
# Update Product
Source: https://docs.komerza.com/api-reference/endpoint/products/update
PUT /stores/{storeId}/products/{productId}
Updates basic information of a product.
Requires the `stores.products.update` permission.
# Update Affiliate Discount
Source: https://docs.komerza.com/api-reference/endpoint/products/update-affiliate-discount
PUT /stores/{storeId}/products/{productId}/affiliateDiscount
Updates affiliate discount configuration of a product.
Requires the `stores.products.update` permission.
# Update Product Badges
Source: https://docs.komerza.com/api-reference/endpoint/products/update-badges
PUT /stores/{storeId}/products/{productId}/badges
Replaces every badge on the product at once (bulk set). An empty list clears all badges.
Requires the `stores.products.update` permission.
# Bulk Update Products
Source: https://docs.komerza.com/api-reference/endpoint/products/update-bulk
PUT /stores/{storeId}/products
Updates multiple products in one request.
Requires the `stores.products.update` permission.
# Update Payment Settings
Source: https://docs.komerza.com/api-reference/endpoint/products/update-payment
PUT /stores/{storeId}/products/{productId}/payment
Sets a custom fee structure for a product.
Requires the `stores.products.update` permission.
# Update Product Security
Source: https://docs.komerza.com/api-reference/endpoint/products/update-security
PUT /stores/{storeId}/products/{productId}/security
Updates security information of a product.
Requires the `stores.products.update` permission.
# Get Review Analytics
Source: https://docs.komerza.com/api-reference/endpoint/reviews/analytics
GET /stores/{storeId}/reviews/analytics
Gets the review analytics for a store.
Requires the `stores.view` permission.
# Get Reviews
Source: https://docs.komerza.com/api-reference/endpoint/reviews/get-list
GET /stores/{storeId}/reviews
Gets the current and previous reviews for a store.
Requires the `stores.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| --------------- | -------- | ---------------------------------------------------- |
| `rating` | integer | Filter by star rating (1-5) |
| `emailAddress` | string | Filter by customer email (case-insensitive contains) |
| `hasReply` | boolean | Filter reviews that have a merchant reply |
| `dateRangeFrom` | datetime | Filter reviews created on or after this date |
| `dateRangeTo` | datetime | Filter reviews created on or before this date |
**Example:**
```bash theme={null}
# 5-star reviews from last 30 days
GET /stores/{storeId}/reviews?filters=rating==5,dateRangeFrom>=2025-10-28
# Reviews without replies, sorted by newest first
GET /stores/{storeId}/reviews?filters=hasReply==false&sorts=-dateRangeFrom
```
# Reply to Review
Source: https://docs.komerza.com/api-reference/endpoint/reviews/reply
PUT /stores/{storeId}/reviews/{reviewId}/reply
Requires the `stores.update` permission.
Replies are **public**. Whatever you send here appears under the review on
your storefront, next to the customer's original words.
## How it works
A review holds **one** reply, stored on the review itself rather than as a thread. That shapes the whole endpoint:
* **`PUT` overwrites.** Call it again on the same review and you replace the previous reply - this is also how you edit one. There is no separate update endpoint.
* **There is no delete.** `reply` is required and must be at least 8 characters, so you cannot blank it out to remove a reply. Replace it with better wording instead.
## Request
```json theme={null}
{
"reply": "Sorry this landed late - your key was reissued the same day and we've since fixed the delay."
}
```
| Field | Rules |
| ------- | ------------------------------------------- |
| `reply` | Required. Between **8 and 4096** characters |
## Permission
Requires **`stores.update`**, not a review-specific scope. A key that can reply to reviews can also change other store settings, so scope keys with that in mind.
## Finding what to reply to
Use [Get Reviews](/api-reference/endpoint/reviews/get-list) with `hasReply==false` to fetch the ones still waiting, worst first:
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/reviews" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=hasReply==false,rating<3" \
--data-urlencode "sorts=-dateRangeFrom"
```
A reply is read by far more future buyers than by the reviewer. Answer the
question the next shopper is silently asking - what went wrong, and what you
did about it.
# Create Store
Source: https://docs.komerza.com/api-reference/endpoint/stores/create
POST /stores
Creates a new store for the current user.
Requires the `stores.create` permission.
# Get Store
Source: https://docs.komerza.com/api-reference/endpoint/stores/get
GET /stores/{storeId}
Retrieves the specified store's reference information.
Requires the `stores.view` permission.
# Get Permissions
Source: https://docs.komerza.com/api-reference/endpoint/stores/get-permissions
GET /stores/{storeId}/permissions
Gets the permissions that the currently authenticated user has against the store, along with the payment methods
enabled by the store owner
Requires the `stores.view` permission.
This method can return permissions (otherwise known as scopes), you can learn
more about scopes by clicking here.
# Update Store
Source: https://docs.komerza.com/api-reference/endpoint/stores/update
PUT /stores/{storeId}
Replaces the specified store's data with the information in the form.
Requires the `stores.update` permission.
# Maintenance Mode
Source: https://docs.komerza.com/api-reference/endpoint/stores/update-maintenance
PUT /stores/{storeId}/maintenance
Enables or disables maintenance mode for the specified store.
Requires the `stores.update` permission.
**This takes your storefront offline for shoppers.** They see your message
instead of the store and cannot buy anything until it is switched back off.
## The reason is the switch
There is no `enabled` flag - maintenance is on or off depending on whether `reason` has a value.
| Send | Result |
| --------------------------------- | ---------------------------------------- |
| `{ "reason": "Back at 3pm UTC" }` | Maintenance **on**, showing that message |
| `{ "reason": null }` | Maintenance **off**, store live again |
```bash theme={null}
curl -X PUT "https://api.komerza.com/stores/{storeId}/maintenance" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
-H "Content-Type: application/json" \
-d '{"reason": "Restocking, back at 3pm UTC"}'
```
## Message rules
Up to **1024 characters**, and only letters, digits, spaces and `. , ! ? ' -`.
Colons and slashes are rejected, so **a URL will fail validation** - as will
emoji, `@`, `#`, `&` and brackets.
The message is public, so write it for shoppers.
## Reading the current state
The store object returns `maintenanceReason`; `null` means live.
If the storefront is dark while `maintenanceReason` is `null`, this endpoint
won't fix it - that's an expired plan, an unpaid Buyer Protection invoice, or
a suspended account.
# Add Ticket Response
Source: https://docs.komerza.com/api-reference/endpoint/tickets/add
PUT /stores/{storeId}/tickets/{ticketId}
Adds a response/reply to the given ticket.
Requires the `stores.tickets.update` permission.
# Close Ticket
Source: https://docs.komerza.com/api-reference/endpoint/tickets/delete
DELETE /stores/{storeId}/tickets/{ticketId}
Closes the given ticket.
Requires the `stores.tickets.delete` permission.
**Closed and Resolved are final states.** Neither you nor the customer can
reopen a closed ticket - they have to open a new one, losing the thread's
context. Reply instead if the conversation is still live.
# Get Ticket
Source: https://docs.komerza.com/api-reference/endpoint/tickets/get
GET /stores/{storeId}/tickets/{ticketId}
Gets a ticket by the ID in the given store and returns it.
Requires the `stores.tickets.view` permission.
# Get Tickets
Source: https://docs.komerza.com/api-reference/endpoint/tickets/get-list
GET /stores/{storeId}/tickets
Gets the tickets in the store.
Requires the `stores.tickets.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| --------------- | -------- | -------------------------------------------------------------------------------- |
| `subject` | string | Search ticket subject (case-insensitive contains) |
| `status` | integer | Filter by ticket status. See [Ticket Status](/api-reference/enums#ticket-status) |
| `customerEmail` | string | Filter by customer email (case-insensitive contains) |
| `dateFrom` | datetime | Filter tickets created on or after this date |
| `dateTo` | datetime | Filter tickets created on or before this date |
**Example:**
```bash theme={null}
# Open tickets from last 7 days
GET /stores/{storeId}/tickets?filters=status==0,dateFrom>=2025-11-20
# Tickets by customer email
GET /stores/{storeId}/tickets?filters=customerEmail@=*user@example.com
```
# Get User
Source: https://docs.komerza.com/api-reference/endpoint/users/get
GET /user
Returns the currently authenticated user’s profile data.
Requires the `user.view` permission.
# Add License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/add-items
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/items
Appends delivery items to a variant.
Requires the `stores.products.update` permission.
On a variant using **Calculated** stock, the number of keys loaded here *is*
the stock level - adding keys is how you restock, and an Undeliverable order
can be retried with [Deliver](/api-reference/endpoint/orders/deliver) once
stock is back.
# Clear License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/clear-items
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/items/clear
Removes all items from a variant.
Requires the `stores.products.update` permission.
**This wipes every license key on the variant, permanently.** There is no undo
and no way to recover the keys afterwards - keep your own copy before calling
it.
On a variant using Calculated stock, this also drops its stock to zero, so the
product stops selling immediately.
# Create Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/create
POST /stores/{storeId}/products/{productId}/variants
Adds a new variant to the specified product.
Requires the `stores.products.update` permission.
# Delete Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/delete
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}
Removes the specified variant.
Requires the `stores.products.update` permission.
**Deletes the variant's license keys, custom fields and volume discounts with
it,** and cannot be undone. Any unsold keys loaded on it are gone.
# Remove File from Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/delete-file
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/file/{fileId}
Deletes the delivery file and its link.
Requires the `stores.products.update` permission.
# Delete License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/delete-items
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/items
Deletes specified items from a variant.
Requires the `stores.products.update` permission.
**Removed keys are gone for good** - they leave your inventory and cannot be
restored. On Calculated stock, removing keys lowers the sellable stock
straight away.
# Create Volume Discount
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/create
POST /stores/{storeId}/products/{productId}/variants/{variantId}/discounts
Adds a new volume discount tier to the specified variant. Each tier defines a minimum quantity
and the discount to apply when that quantity is reached.
Requires the `stores.products.update` permission.
# Delete Volume Discount
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/delete
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/discounts/{discountId}
Removes the specified volume discount tier from the variant.
Requires the `stores.products.update` permission.
# Get Volume Discounts
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/get
GET /stores/{storeId}/products/{productId}/variants/{variantId}/discounts
Retrieves all volume discount tiers configured for the specified variant, ordered by quantity threshold.
Requires the `stores.products.update` permission.
# Replace Volume Discounts
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/replace
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/discounts
Replaces all existing volume discount tiers for the variant with a new set.
This is useful for bulk updates where you want to set all tiers at once.
Requires the `stores.products.update` permission.
# Update Volume Discount
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/update
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/discounts/{discountId}
Updates the quantity threshold, discount type, or discount value of an existing volume discount tier.
Requires the `stores.products.update` permission.
# Duplicate Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/duplicate
POST /stores/{storeId}/products/{productId}/variants/{variantId}/duplicate
Creates a copy of an existing variant in the same product.
Requires the `stores.products.update` permission.
# Create Field
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/create
POST /stores/{storeId}/products/{productId}/variants/{variantId}/fields
Adds a new custom field to the specified variant. Options for select/radio fields are managed separately.
Requires the `stores.products.update` permission.
# Delete Field
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/delete
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}
Removes the specified custom field and all its options from the variant.
Requires the `stores.products.update` permission.
# Create Field Option
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/options/create
POST /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}/options
Adds a selectable option under the given custom field.
Requires the `stores.products.update` permission.
# Delete Field Option
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/options/delete
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}/options/{optionId}
Removes the specified option from its parent custom field.
Requires the `stores.products.update` permission.
# Update Field Option
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/options/update
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}/options/{optionId}
Updates the label, value, or display order of an existing option.
Requires the `stores.products.update` permission.
# Update Field
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/update
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}
Updates the label, placeholder, type, requirement, or hint of an existing custom field.
Requires the `stores.products.update` permission.
# Get Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/get
GET /stores/{storeId}/products/{productId}/variants/{variantId}
Retrieves a single variant by ID.
Requires the `stores.products.view` permission.
# Get License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/get-items
GET /stores/{storeId}/products/{productId}/variants/{variantId}/items/{page}
Retrieves files/items delivered by a variant in pages.
Requires the `stores.products.view` permission.
# Order Variants
Source: https://docs.komerza.com/api-reference/endpoint/variants/order
PATCH /stores/{storeId}/products/{productId}/variants/order
Updates the display order of variants.
Requires the `stores.products.update` permission.
# Order Variant Images
Source: https://docs.komerza.com/api-reference/endpoint/variants/order-images
PATCH /stores/{storeId}/products/{productId}/variants/{variantId}/images/order
Sets the display order of images.
Requires the `stores.products.update` permission.
# Search License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/search-items
GET /stores/{storeId}/products/{productId}/variants/{variantId}/items/search/{query}
Searches for license keys in the specified store using fuzzy matching.
Results are ordered by relevance score, with exact matches appearing first,
followed by prefix matches, substring matches, and then similar keys.
Requires the `stores.products.view` permission.
# Update Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/update
PUT /stores/{storeId}/products/{productId}/variants/{variantId}
Modifies a variant’s label, SKU, price, etc.
Requires the `stores.products.update` permission.
# Update Delivery Settings
Source: https://docs.komerza.com/api-reference/endpoint/variants/update-delivery
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/delivery
Configures how the variant is delivered (e.g., file, custom script).
Requires the `stores.products.update` permission.
# Enums & Constants
Source: https://docs.komerza.com/api-reference/enums
Every enum value the API accepts and returns
Most enums are **integers** - send and expect the numeric value, not the name. The names below are for readability only; they never appear in a payload. String enums are marked as such.
Order statuses have their own page: [Order Status](/api-reference/payment-status).
## Products
### Privacy
Controls where a product appears and whether it can be bought.
| Value | Name | Meaning |
| ----- | -------- | ------------------------------------------------------ |
| `0` | Public | Visible on the store page and purchasable |
| `1` | Unlisted | Hidden from the store page, purchasable via direct URL |
| `2` | Private | Hidden entirely, not purchasable |
| `3` | OnHold | Visible but blocked from purchase |
### Product type
| Value | Name | Meaning |
| ----- | ------------ | --------------------------------------------- |
| `0` | OneTime | A single purchase |
| `1` | Subscription | Creates or extends a subscription on delivery |
### Billing interval
Applies to subscription variants.
| Value | Name |
| ----- | ----------------------------- |
| `0` | Weekly |
| `1` | Monthly |
| `2` | Quarterly |
| `3` | Yearly |
| `4` | Custom (interval set in days) |
### Stock calculation mode
| Value | Name | Meaning |
| ----- | ---------- | --------------------------------------------------------------- |
| `0` | Calculated | Stock equals the number of delivery items loaded on the variant |
| `1` | Ignored | Stock is not tracked; never out of stock |
| `2` | Fixed | A number you set and maintain yourself |
### Volume discount type
| Value | Name | Meaning |
| ----- | ----------- | ------------------------ |
| `0` | Percentage | A percentage off |
| `1` | FixedAmount | An amount off per unit |
| `2` | FixedPrice | A replacement unit price |
### Affiliate discount override
Set per product to override the store's default affiliate discount.
| Value | Name | Meaning |
| ----- | ---------- | ---------------------------------------------------------- |
| `0` | UseDefault | Use the store's default discount |
| `1` | Disabled | No customer discount; the affiliate still earns commission |
| `2` | Custom | Use the percentage set on the product |
### Delivery types
**String values**, supplied as an array on a variant. A variant can combine several.
| Value | Delivers |
| --------------- | ---------------------------------------------------------------------- |
| `license_keys` | Keys drawn from the variant's inventory |
| `files` | The uploaded file, via a time-limited download link |
| `dynamic` | Whatever your endpoint returns at delivery time |
| `instructions` | A static message included in the delivery email |
| `custom_fields` | Not a delivery mechanism - collects answers from the buyer at checkout |
`keyauth` still appears in some request descriptions. It was removed from the
product and no longer works - do not send it.
## Orders
### Refund reason
| Value | Name |
| ----- | ------------------- |
| `0` | Fraudulent |
| `1` | RequestedByCustomer |
| `2` | Duplicate |
| `3` | Other |
### Refund destination
| Value | Name | Meaning |
| ----- | --------------- | ----------------------------------- |
| `0` | OriginalMethod | Reverse through the payment gateway |
| `1` | CustomerBalance | Issue store credit instead |
Several payment methods only support `1` - see [Managing Orders](/guides/orders#refunds).
### Custom payment method type
| Value | Name | Meaning |
| ----- | ------------ | -------------------------------------------------- |
| `0` | Instructions | Show the buyer instructions to pay outside Komerza |
| `1` | Redirect | Send the buyer to a URL you specify |
## Subscriptions
### Subscription status
| Value | Name | Meaning |
| ----- | -------- | ------------------------------------------------------ |
| `0` | Active | Paid and current |
| `1` | PastDue | An automatic charge failed; retries are scheduled |
| `2` | Canceled | Ended by the merchant or the customer |
| `3` | Expired | Retries exhausted, or the period ended without renewal |
| `4` | Paused | Resumes automatically at its resume date |
### Cancel mode
| Value | Name | Meaning |
| ----- | ----------- | -------------------------------------------- |
| `0` | AtPeriodEnd | Access continues until the period ends |
| `1` | Immediate | Access is revoked at once, with no proration |
## Discord
### Join mode
| Value | Name | Meaning |
| ----- | -------------------------- | ------------------------------------------------------ |
| `0` | AddAndGrantAfterPayment | Added to the server and granted the role after payment |
| `1` | AddOnLinkGrantAfterPayment | Added when they link Discord; role waits for payment |
### Requirement
| Value | Name | Meaning |
| ----- | -------- | ---------------------------------------- |
| `0` | Required | Cannot check out without linking Discord |
| `1` | Optional | Offered but not enforced |
| `2` | Disabled | Not shown at checkout |
### Revocation behavior
Applied when an order is refunded or charged back.
| Value | Name | Meaning |
| ----- | --------------- | -------------------------------- |
| `0` | RevokeAndRemove | Remove the role and the member |
| `1` | RevokeRoleOnly | Remove the role, keep the member |
| `2` | DoNothing | Leave access in place |
## Customers & support
### Ticket status
| Value | Name | Meaning |
| ----- | ----------------- | ------------------------------------- |
| `0` | Opened | Awaiting the seller |
| `1` | Resolved | Closed, issue sorted. Final |
| `2` | Closed | Closed without resolution. Final |
| `3` | Responded | Seller replied; awaiting the customer |
| `4` | CustomerResponded | Customer replied; awaiting the seller |
The values are not in flow order: a ticket moves `0` → `3` ⇄ `4` → `1` or `2`.
### Customer segment
Used to target email marketing recipients.
| Value | Name |
| ----- | ---------- |
| `0` | All |
| `1` | New |
| `2` | Active |
| `3` | Regulars |
| `4` | Vip |
| `5` | Affiliates |
### Blacklist type
The kind of value a blacklist rule matches on.
| Value | Name |
| ----- | ----------------------- |
| `0` | EmailAddress |
| `1` | EmailDomain |
| `2` | IpAddress |
| `3` | IpCidr |
| `4` | AutonomousSystemNumber |
| `5` | InternetServiceProvider |
| `6` | CountryCode |
| `7` | City |
## Email marketing
### Campaign status
| Value | Name | Meaning |
| ----- | ------------ | --------------------------------------------------- |
| `0` | Draft | Editable, not queued |
| `1` | Scheduled | Queued for a future time; still cancellable |
| `2` | Sending | In progress; cannot be cancelled from the dashboard |
| `3` | Sent | Finished |
| `4` | Cancelled | Cancelled before sending |
| `5` | Failed | The reason is not exposed; contact support |
| `6` | Under review | Held by automated screening before sending |
### Recipient send status
Per-recipient outcome within a campaign.
| Value | Name |
| ----- | ---------- |
| `0` | Queued |
| `1` | Sent |
| `2` | Delivered |
| `3` | Bounced |
| `4` | Complained |
| `5` | Failed |
### Suppression type
| Value | Name | Meaning |
| ----- | ----------- | ------------------------- |
| `0` | Unsubscribe | Removable by the merchant |
| `1` | HardBounce | Permanent |
| `2` | Complaint | Permanent |
## Storefront
### Discount dialog trigger
| Value | Name | Meaning |
| ----- | ---------- | ------------------------------------- |
| `0` | Entrance | Shown when the visitor arrives |
| `1` | ExitIntent | Shown when the visitor moves to leave |
Enums used only by dashboard-internal endpoints - platform billing, passkey
registration, account standing - are deliberately not listed. They are not
part of the public API and can change without notice.
# Error Codes
Source: https://docs.komerza.com/api-reference/error-codes
Complete reference for all API error codes and how to handle them
Every failed request carries a `code`: a stable, machine-readable identifier for what went wrong. This page lists them all.
```json theme={null}
{
"success": false,
"message": "The request contains invalid parameters",
"code": "ValidationError",
"data": null
}
```
For the response envelope itself, validation errors and HTTP status codes, see
[Responses & Errors](/api-reference/responses).
## Error Codes Reference
### Authentication & Authorization
**HTTP Status:** 403 Forbidden
**Description:** You don't have permission to access this resource or perform this action.
**Common causes:**
* API key lacks required scope
* Trying to access another user's resources
* Account restrictions or suspensions
* Store-level permissions insufficient
**How to fix:**
* Check your API key has the necessary [scopes](/api-reference/scopes)
* Verify you're accessing resources you own
* Check account status in dashboard
* Request additional permissions if needed
**Example:**
```json theme={null}
{
"success": false,
"message": "Your API key does not have permission to update products",
"code": "AccessDenied",
"data": null
}
```
**HTTP Status:** 401 Unauthorized
**Description:** The provided authentication token is invalid, expired, or malformed.
**Common causes:**
* Expired API key
* Revoked API key
* Malformed Authorization header
* API key not found
**How to fix:**
* Generate a new API key from dashboard
* Verify Authorization header format: `Bearer YOUR_API_KEY`
* Check for whitespace or special characters in token
* Ensure API key hasn't been revoked
**Example:**
```json theme={null}
{
"success": false,
"message": "The provided API key is invalid or has expired",
"code": "BadToken",
"data": null
}
```
**HTTP Status:** 403 Forbidden
**Description:** Two-factor authentication is required for this action.
**Common causes:**
* Sensitive operation requires 2FA
* Account security policy enforced
* Administrative action attempted
**How to fix:**
* Complete 2FA challenge
* Enable 2FA on your account
* Use session-based authentication for sensitive operations
**Example:**
```json theme={null}
{
"success": false,
"message": "This operation requires two-factor authentication",
"code": "TwoFactorRequired",
"data": null
}
```
**HTTP Status:** 401 Unauthorized
**Description:** OAuth authentication is required for this endpoint.
**Common causes:**
* Endpoint requires user session, not API key
* OAuth flow not completed
* Session expired
**How to fix:**
* Complete OAuth authentication flow
* Use correct authentication method for endpoint
* Check if endpoint supports API key authentication
**Example:**
```json theme={null}
{
"success": false,
"message": "This endpoint requires OAuth authentication",
"code": "OAuthLoginRequired",
"data": null
}
```
### Validation & Input Errors
**HTTP Status:** 400 Bad Request
**Description:** The request contains invalid or missing parameters.
**Common causes:**
* Missing required fields
* Invalid data types
* Values outside allowed ranges
* Invalid format (email, URL, UUID, etc.)
* Business rule violations
**How to fix:**
* Check request body against API documentation
* Validate data types and formats
* Ensure all required fields are present
* Review error details for specific field errors
**Example:**
```json theme={null}
{
"success": false,
"message": "Price must be greater than 0",
"code": "ValidationError",
"data": null
}
```
**HTTP Status:** 400 Bad Request
**Description:** CAPTCHA verification failed.
**Common causes:**
* Invalid CAPTCHA response
* Expired CAPTCHA token
* CAPTCHA not solved
* Bot detection triggered
**How to fix:**
* Request new CAPTCHA challenge
* Ensure user completes CAPTCHA
* Check CAPTCHA token hasn't expired
* Verify CAPTCHA integration is correct
**Example:**
```json theme={null}
{
"success": false,
"message": "CAPTCHA verification failed. Please try again.",
"code": "BadCaptcha",
"data": null
}
```
### Resource Errors
**HTTP Status:** 404 Not Found
**Description:** The requested resource does not exist.
**Common causes:**
* Invalid resource ID
* Resource was deleted
* Typo in endpoint URL
* Resource belongs to different store
**How to fix:**
* Verify resource ID is correct
* Check resource hasn't been deleted
* Ensure you're querying the right store
* Validate endpoint URL
**Example:**
```json theme={null}
{
"success": false,
"message": "Product not found",
"code": "NotFound",
"data": null
}
```
**HTTP Status:** 409 Conflict
**Description:** The operation conflicts with an existing resource.
**Common causes:**
* Duplicate unique field (email, slug, etc.)
* Resource already exists
* Concurrent modification conflict
* Business rule prevents operation
**How to fix:**
* Use unique values for unique fields
* Check if resource already exists
* Implement optimistic locking for concurrent updates
* Review business rules
**Example:**
```json theme={null}
{
"success": false,
"message": "A product with this slug already exists",
"code": "ObjectConflict",
"data": null
}
```
### Rate Limiting
**HTTP Status:** 429 Too Many Requests
**Description:** You have exceeded the API rate limit.
**Common causes:**
* Too many requests in short time period
* Burst limit exceeded
* Account-level rate limit reached
**How to fix:**
* Implement exponential backoff
* Respect `Retry-After` header
* Cache frequently accessed data
* Optimize API calls to reduce frequency
* Contact support for higher limits
**Response headers:**
```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1638360000
Retry-After: 60
```
**Example:**
```json theme={null}
{
"success": false,
"message": "Rate limit exceeded. Please retry after 60 seconds.",
"code": "RateLimited",
"data": null
}
```
### Business Logic Errors
**HTTP Status:** 400 Bad Request
**Description:** The requested product is not available for purchase.
**Common causes:**
* Product out of stock
* Product is private or unlisted
* Product deleted or disabled
* Product not available in customer's region
* Purchase limits exceeded
**How to fix:**
* Check product availability
* Verify product stock levels
* Ensure product is public
* Check regional restrictions
**Example:**
```json theme={null}
{
"success": false,
"message": "This product is currently out of stock",
"code": "ProductUnavailable",
"data": null
}
```
**HTTP Status:** 400 Bad Request
**Description:** Insufficient funds in customer balance.
**Common causes:**
* Customer balance too low
* Attempting to use balance payment method
* Withdrawal amount exceeds available balance
**How to fix:**
* Check customer balance
* Request customer add funds
* Use alternative payment method
* Reduce order amount
**Example:**
```json theme={null}
{
"success": false,
"message": "Insufficient customer balance",
"code": "NotEnoughFunds",
"data": null
}
```
**HTTP Status:** 403 Forbidden
**Description:** Account verification is required before proceeding.
**Common causes:**
* Email not verified
* Identity verification pending
* Payment method requires verification
* Account limits require verification
**How to fix:**
* Complete email verification
* Submit required verification documents
* Verify payment methods
* Contact support for verification status
**Example:**
```json theme={null}
{
"success": false,
"message": "Email verification required",
"code": "VerificationRequired",
"data": null
}
```
### Feature & Upgrade Errors
**HTTP Status:** 403 Forbidden
**Description:** This feature is not available for your account.
**Common causes:**
* Feature disabled for your plan
* Beta feature not enabled
* Regional restrictions
* Feature requires specific integration
**How to fix:**
* Check feature availability for your plan
* Contact support to enable beta features
* Verify regional availability
* Review feature requirements
**Example:**
```json theme={null}
{
"success": false,
"message": "Advanced analytics is not available on your current plan",
"code": "FeatureUnavailable",
"data": null
}
```
**HTTP Status:** 402 Payment Required
**Description:** Your current plan does not support this operation.
**Common causes:**
* Plan limits exceeded
* Feature requires higher plan tier
* Usage quota exhausted
**How to fix:**
* Upgrade to higher plan tier
* Review current plan limits
* Reduce usage to stay within limits
* Contact sales for enterprise options
**Example:**
```json theme={null}
{
"success": false,
"message": "You have reached the maximum number of products for your plan",
"code": "UpgradeRequired",
"data": null
}
```
**HTTP Status:** 400 Bad Request
**Description:** The requested operation is not supported.
**Common causes:**
* Deprecated API version
* Invalid operation combination
* Unsupported payment method
* Unsupported currency
**How to fix:**
* Check API documentation for supported operations
* Use current API version
* Verify operation compatibility
* Check supported payment methods/currencies
**Example:**
```json theme={null}
{
"success": false,
"message": "This payment method is not supported for subscriptions",
"code": "UnsupportedRequest",
"data": null
}
```
### File & Upload Errors
**HTTP Status:** 400 Bad Request
**Description:** File upload failed.
**Common causes:**
* File too large
* Invalid file type
* Corrupted file
* Network interruption
* Storage quota exceeded
**How to fix:**
* Check file size limits
* Verify file type is supported
* Ensure file is not corrupted
* Retry upload with stable connection
* Check storage quota
**Example:**
```json theme={null}
{
"success": false,
"message": "File size exceeds maximum allowed size",
"code": "UploadFailed",
"data": null
}
```
### Server Errors
**HTTP Status:** 500 Internal Server Error
**Description:** An unexpected error occurred on the server.
**Common causes:**
* Server-side bug
* Database connection issue
* External service failure
* Unexpected condition
**How to fix:**
* Retry the request after a short delay
* If problem persists, contact support
* Check [status page](https://status.komerza.com) for incidents
* Include error ID when contacting support
**Example:**
```json theme={null}
{
"success": false,
"message": "An unexpected error occurred",
"code": "InternalServerError",
"data": null
}
```
Always include the `errorId` when reporting issues to support for faster resolution.
## HTTP Status Code Summary
| Status Code | Error Codes | Description |
| ----------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| 400 | ValidationError, BadCaptcha, ProductUnavailable, NotEnoughFunds, UnsupportedRequest, UploadFailed | Bad Request - Invalid input |
| 401 | BadToken, OAuthLoginRequired | Unauthorized - Invalid or missing authentication |
| 402 | UpgradeRequired | Payment Required - Plan upgrade needed |
| 403 | AccessDenied, TwoFactorRequired, VerificationRequired, FeatureUnavailable | Forbidden - Insufficient permissions |
| 404 | NotFound | Not Found - Resource doesn't exist |
| 409 | ObjectConflict | Conflict - Resource conflict |
| 429 | RateLimited | Too Many Requests - Rate limit exceeded |
| 500 | InternalServerError | Internal Server Error - Server-side issue |
## Support Resources
Check API and service status
Browse error troubleshooting guides
Get help from the community
Report issues with error IDs
# Exchange Code
Source: https://docs.komerza.com/api-reference/oauth2/exchange
GET /oauth2/{appId}/exchange/{code}
## Overview
Exchange an authorization code for a JWT API key. This is the final step in the OAuth2 flow, converting the temporary code into a permanent access token.
This endpoint requires the `X-OAuth2-Client-Secret` header for authentication instead
of the standard `Authorization` header.
## Path Parameters
| Parameter | Type | Description |
| --------- | ------ | ------------------------------------------------- |
| `appId` | string | Your application's Client ID |
| `code` | string | The authorization code received from the callback |
## Headers
| Header | Required | Description |
| ------------------------ | -------- | --------------------------------------------- |
| `X-OAuth2-Client-Secret` | Yes | Your application's 72-character client secret |
| `User-Agent` | Yes | Your application identifier |
**Server-Side Only**: This request must be made from your backend server,
never from client-side code. The client secret must remain confidential.
## Response
```json theme={null}
{
"success": true,
"data": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```
The `data` field contains a JWT API key that you can use to make authenticated API requests.
## Using the API Key
Include the JWT as a Bearer token in subsequent API requests:
```bash theme={null}
curl -X GET https://api.komerza.com/stores/STORE_ID \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "User-Agent: MyApp/1.0"
```
## Token Properties
The JWT API key:
* **Grants access** only to the stores the user authorized
* **Has permissions** limited to the scopes you requested and the user approved
* **Does not expire** until the user revokes authorization
* **Is tied** to the specific user and application
## Error Responses
| Error | Description |
| ----------------------- | ----------------------------------------------------------------- |
| `invalid_code` | The authorization code is invalid or has already been used |
| `code_expired` | The authorization code has expired (codes are valid for 1 minute) |
| `invalid_client_secret` | The client secret is incorrect |
| `app_not_found` | The application ID is invalid |
## Complete Flow Example
```javascript theme={null}
// 1. User clicks "Connect with Komerza" in your app
const authUrl = `https://dashboard.komerza.com/auth/authorize?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&state=${randomState}`;
window.location.href = authUrl;
// 2. User authorizes on Komerza, redirected back to your app
// URL: https://myapp.com/callback?code=ENCRYPTED_CODE&state=randomState
// 3. Your backend exchanges the code for an API key
const response = await fetch(
`https://api.komerza.com/oauth2/${CLIENT_ID}/exchange/${code}`,
{
headers: {
"X-OAuth2-Client-Secret": process.env.CLIENT_SECRET,
"User-Agent": "MyApp/1.0",
},
}
);
const { data: apiKey } = await response.json();
// 4. Store the API key securely and use it for API calls
const stores = await fetch("https://api.komerza.com/stores", {
headers: {
Authorization: `Bearer ${apiKey}`,
"User-Agent": "MyApp/1.0",
},
});
```
# OAuth2 Integration
Source: https://docs.komerza.com/api-reference/oauth2/introduction
Allow third-party applications to access user accounts with their consent
## Overview
Komerza's OAuth2 system enables third-party developers to create applications that can securely access user accounts with explicit user consent. This allows you to build integrations, plugins, and services that interact with Komerza stores on behalf of users.
Register your OAuth2 application in the Komerza Dashboard
## How It Works
Register your application with Komerza, specifying the permissions you need
and your redirect URLs.
Redirect users to Komerza's authorization page where they can review and
approve your app's access.
After approval, users are redirected back to your app with a temporary
authorization code.
Exchange the authorization code for a JWT API key that grants access to the
authorized stores.
## Authorization URL
Send users to the following URL to initiate the OAuth2 flow:
```
https://dashboard.komerza.com/auth/authorize
```
### Query Parameters
| Parameter | Required | Description |
| -------------- | -------- | ------------------------------------------------------ |
| `client_id` | Yes | Your application's Client ID |
| `redirect_uri` | Yes | One of your registered redirect URLs |
| `state` | No | Optional value passed back to your app in the callback |
### Example Authorization URL
```
https://dashboard.komerza.com/auth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=https://myapp.com/callback&state=random123
```
## OAuth2 Flow Diagram
```
┌─────────────┐ ┌─────────────┐
│ Your App │ │ Komerza │
└──────┬──────┘ └──────┬──────┘
│ │
│ 1. Redirect user to authorization URL │
│ ─────────────────────────────────────────> │
│ │
│ 2. User reviews & approves │
│ │
│ 3. Redirect back with authorization code │
│ <───────────────────────────────────────── │
│ │
│ 4. Exchange code for API key │
│ ─────────────────────────────────────────> │
│ │
│ 5. Return JWT API key │
│ <───────────────────────────────────────── │
│ │
│ 6. Make API calls with Bearer token │
│ ─────────────────────────────────────────> │
│ │
```
## Permissions & Scopes
When creating your application, you must specify which permissions (scopes) your app requires. Users will see these permissions during the authorization flow and can choose which stores to grant access to.
Complete reference for all available permission scopes
## Security Best Practices
**Client Secret Security**: Your client secret is like a password. Never
expose it in client-side code, public repositories, or logs.
All redirect URLs should use HTTPS in production environments.
Always exchange authorization codes from your backend server, never from
client-side code.
Store client secrets and API keys securely using environment variables or
secret management systems.
Only request the permissions your application actually needs.
## Token Characteristics
| Property | Value |
| ------------------------------- | --------------------- |
| **Token Type** | JWT (JSON Web Token) |
| **Authorization Code Validity** | 1 minute (single-use) |
| **API Key Validity** | Until revoked |
| **Max Redirect URLs** | 10 per application |
## Quick Links
Exchange authorization code for API key
# Payment Methods
Source: https://docs.komerza.com/api-reference/payment-methods
Complete reference for all supported payment methods and cryptocurrencies
## Overview
Komerza supports a wide range of payment methods including traditional payment gateways and cryptocurrencies. Each payment method has a unique identifier used throughout the API.
## Payment Gateways
### Traditional Processors
**Stripe** - Industry-leading payment processor supporting cards, wallets, and
local payment methods
**SumUp** - European payment gateway for card processing
**Square** - Payment processor with integrated point-of-sale solutions
**Cash App** - Mobile payment service for instant transfers
**Authorize.Net** - Long-established payment gateway for online transactions
**NMI** - Payment gateway similar to Authorize.Net
**Mollie** - European payment service provider supporting cards, iDEAL,
Bancontact, and 20+ local payment methods
**VenPayr** - Card payment processor
**moneymotion** - Card payment processor
**Pandabase** - Card payment processor
**Storrik** - Card payment processor
**Shopify** - Card payments through Shopify's hosted checkout
### PayPal
**PayPal Business** - PayPal business accounts with Instant Payment
Notifications (IPNs)
**PayPal Friends & Family** - PayPal personal payment method
### Customer Balance
**Customer Balance** - Payment using customer's account balance
## Cryptocurrencies
### Major Cryptocurrencies
**Bitcoin** - The original and most widely recognized cryptocurrency
* Network: Bitcoin
* Symbol: BTC
* Type: Native coin
**Ethereum** - Leading smart contract platform - Network: Ethereum - Symbol:
ETH - Type: Native coin
**Litecoin** - Faster alternative to Bitcoin - Network: Litecoin - Symbol: LTC
* Type: Native coin
**Bitcoin Cash** - Bitcoin fork with larger block sizes - Network: Bitcoin
Cash - Symbol: BCH - Type: Native coin
**Monero** - Privacy-focused cryptocurrency - Network: Monero - Symbol: XMR -
Type: Native coin
**Dash** - Digital cash with instant transactions - Network: Dash - Symbol:
DASH - Type: Native coin
**Dogecoin** - Popular meme-based cryptocurrency
* Network: Dogecoin
* Symbol: DOGE
* Type: Native coin
### Smart Contract Platforms
**Solana** - High-performance blockchain platform
* Network: Solana
* Symbol: SOL
* Type: Native coin
**Cardano** - Proof-of-stake blockchain platform - Network: Cardano - Symbol:
ADA - Type: Native coin
**TRON** - Decentralized entertainment ecosystem - Network: TRON - Symbol: TRX
* Type: Native coin
**Nano** - Zero-fee instant transactions
* Network: Nano
* Symbol: NANO
* Type: Native coin
### Stablecoins
Stablecoins are cryptocurrencies designed to maintain a stable value, typically pegged to fiat currencies like USD.
**USDT** - Most widely used stablecoin (1 USDT ≈ 1 USD)
Available on multiple networks:
* `usdt` - USD Tether (primary)
* `usdterc20` - USD Tether on Ethereum (ERC-20)
* `usdtbsc` - USD Tether on Binance Smart Chain (BEP-20)
* `usdtsol` - USD Tether on Solana (SPL)
**USDC** - Regulated stablecoin (1 USDC ≈ 1 USD) Available on multiple
networks: - `usdc` - USD Coin (primary) - `usdcbsc` - USD Coin on Binance
Smart Chain (BEP-20) - `usdcsol` - USD Coin on Solana (SPL)
**BUSD** - Binance's regulated stablecoin
* Network: Multiple
* Symbol: BUSD
* Peg: 1 BUSD ≈ 1 USD
### Exchange Tokens
**BNB** - Binance Smart Chain native token - Network: Binance Smart Chain -
Symbol: BNB - Type: Exchange utility token
### Meme Tokens
**SHIB** - Popular meme token - Network: Ethereum - Symbol: SHIB - Type:
ERC-20 token
## Related Resources
Process payments and create orders
Track payment statuses
# Order Status
Source: https://docs.komerza.com/api-reference/payment-status
Reference for all order statuses and their meanings
The order has not received payment and is considered incomplete. This is the
initial status for all new orders.
The order has received payment and the funds are pending or being verified by
the payment gateway.
The funds have been received but are withheld for verification. Commonly used
for cryptocurrency payments awaiting blockchain confirmations or fraud checks.
The order is being delivered. This is a guard status to prevent multiple
deliveries from happening simultaneously.
The order has been verified, paid for, and the product has been delivered to
the customer.
The order has been verified and paid for, but issues were encountered during
delivery that prevented fulfillment. This is typically due to missing stock
(e.g., no license keys available).
The order has been partially paid. The customer sent less than the required
amount.
The order was cancelled by the customer or the store before completion.
The store refunded the amount or the customer disputed the payment and won.
The order has been disputed by the customer, usually through a chargeback with
their payment gateway.
# Blacklist Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/blacklist
POST /stores/{storeId}/affiliates/{customerId}/blacklist
Disables the affiliate, blocks the customer on the store, and adds their email to the store's blacklist.
Requires the `stores.affiliates.update` permission.
Blacklisting stops the affiliate earning on new referrals. It does not claw
back commission already accrued, and it does not refund orders they referred.
# Configure Affiliate Program
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/configure
PUT /stores/{storeId}/affiliates/configure
Updates the configuration options for the affiliate program of the specified store.
Requires the `stores.affiliates.update` permission.
# List Affiliate Orders
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/get-orders
GET /stores/{storeId}/affiliates/{customerId}/orders/{page}
Retrieves a paginated list of orders attributed to a specific affiliate (by customerId) in the store.
Requires the `stores.affiliates.view` permission.
# List Affiliate Payouts
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/get-payouts
GET /stores/{storeId}/affiliates/{customerId}/payouts/{page}
Requires the `stores.affiliates.view` permission.
# Record Affiliate Payout
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/record-payout
POST /stores/{storeId}/affiliates/{customerId}/payouts
Requires the `stores.affiliates.update` permission.
**This records a payout, it does not send money.** Komerza reduces the
affiliate's balance by the amount; the actual transfer happens between you and
your affiliate outside Komerza.
Record it only once you have actually paid, or your balances will disagree with
reality.
# Void Affiliate Payout
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/void-payout
PATCH /stores/{storeId}/affiliates/{customerId}/payouts/{payoutId}
Marks the payout as voided, restores balances, and records the admin and reason.
Requires the `stores.affiliates.update` permission.
Voiding a recorded payout **restores the amount to the affiliate's balance**.
Use it when a payout was recorded in error or the transfer failed - not to
reverse money you actually sent.
# Create Product
Source: https://docs.komerza.com/api-reference/endpoint/products/create
POST /stores/{storeId}/products
Creates a new product in the store.
Requires the `stores.products.create` permission.
# Get Product
Source: https://docs.komerza.com/api-reference/endpoint/products/get
GET /stores/{storeId}/products/{productId}
Retrieves a product by its ID.
Requires the `stores.products.view` permission.
# Get All Products
Source: https://docs.komerza.com/api-reference/endpoint/products/get-all
GET /stores/{storeId}/products/all
Retrieves all products in the store as redacted references (no pagination).
Requires the `stores.products.view` permission.
# Get Products
Source: https://docs.komerza.com/api-reference/endpoint/products/get-list
GET /stores/{storeId}/products
Retrieves products in pages.
Requires the `stores.products.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------------------------------------ |
| `visibility` | integer | Filter by privacy level. See [Product Privacy](/api-reference/enums#product-privacy) |
| `isBestSeller` | boolean | Filter best-seller products |
| `shouldBlockVpns` | boolean | Filter products that block VPNs |
| `lowStock` | boolean | Filter products with low stock availability |
**Example:**
```bash theme={null}
# Public best-sellers with low stock
GET /stores/{storeId}/products?filters=visibility==0,isBestSeller==true,lowStock==true
# Products that don't block VPNs
GET /stores/{storeId}/products?filters=shouldBlockVpns==false
```
# Get Product by Slug
Source: https://docs.komerza.com/api-reference/endpoint/products/get-slug
GET /stores/{storeId}/products/slug/{slug}
Retrieves a product by its slug.
Requires the `stores.products.view` permission.
# Search Products
Source: https://docs.komerza.com/api-reference/endpoint/products/search
GET /stores/{storeId}/products/search/{query}
Searches products by query within the store.
Requires the `stores.products.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------------------------------------ |
| `visibility` | integer | Filter by privacy level. See [Product Privacy](/api-reference/enums#product-privacy) |
| `isBestSeller` | boolean | Filter best-seller products |
| `shouldBlockVpns` | boolean | Filter products that block VPNs |
| `lowStock` | boolean | Filter products with low stock availability |
**Example:**
```bash theme={null}
# Public best-sellers with low stock
GET /stores/{storeId}/products?filters=visibility==0,isBestSeller==true,lowStock==true
# Products that don't block VPNs
GET /stores/{storeId}/products?filters=shouldBlockVpns==false
```
# Add to Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/add-to-catalogue
PUT /stores/{storeId}/reseller/catalogue/{variantId}
Adds a variant to the catalogue or updates its terms. The variant does not have to be visible on
the storefront — offering resellers something ordinary customers never see is a supported use.
Requires the `stores.resellers.update` permission.
# Configure Programme
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/configure
PUT /stores/{storeId}/reseller
Sets whether the store supplies resellers, how they enrol, the discount they pay, and the
optional exposure and top-up limits.
Requires the `stores.resellers.update` permission.
# Create Application
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-application
POST /stores/{storeId}/reseller/applications
For a merchant hosting their own application form. Collect whatever fields suit your site, then
post them here with the applicant's email — answers may reference the questions configured on
this store, carry a `label` of your own, or mix the two.
The email is taken as given and not verified: the form is yours, on your domain, and you
review what it produces. The applicant proves the address the first time they sign in to
deposit or reach their reseller panel.
The application lands in the same review queue as every other, at
`GET /stores/{storeId}/reseller/application/submissions`.
Requires the `stores.resellers.update` permission.
# Create Basket Discount
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-basket-tier
POST /stores/{storeId}/reseller/basket-tiers
Measured at list price before anything comes off, so qualifying for a discount cannot lower a
basket's value and disqualify it again. Where several apply, the largest wins.
Requires the `stores.resellers.update` permission.
# Create Bundle
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-bundle
POST /stores/{storeId}/reseller/bundles
A bundle is a basket you have composed and priced as one thing. Set a discount to advertise a
figure, or leave it off and let your basket-size discounts decide.
Every product in it has to be in your reseller catalogue already, and it can never cost a
reseller more than buying the same lines separately would.
Requires the `stores.resellers.update` permission.
# Create Invitation
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-invite
POST /stores/{storeId}/reseller/invites
Returns the invite token once and only once — only its hash is stored, because the token grants
an already-approved reseller relationship and a leaked invite list must not be usable. Send it on
immediately; it cannot be read back.
Requires the `stores.resellers.update` permission.
# Create Question
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-question
POST /stores/{storeId}/reseller/application/questions
Questions can be added, reworded and reordered at any time, including while applications are
waiting: an answer records the wording it was given against, so editing the form never changes
what someone already in the queue appears to have been asked.
Requires the `stores.resellers.update` permission.
# Delete Basket Discount
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-basket-tier
DELETE /stores/{storeId}/reseller/basket-tiers/{basketTierId}
Requires the `stores.resellers.update` permission.
# Delete Bundle
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-bundle
DELETE /stores/{storeId}/reseller/bundles/{bundleId}
Takes it off sale. What resellers already bought through it, and what they paid, stays on record.
Requires the `stores.resellers.update` permission.
# Delete Invitation
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-invite
DELETE /stores/{storeId}/reseller/invites/{inviteId}
Requires the `stores.resellers.update` permission.
# Delete Question
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-question
DELETE /stores/{storeId}/reseller/application/questions/{questionId}
Archived rather than deleted. Every answer ever given to it still points at it, and tidying the
form should not orphan the applications that answered it.
Requires the `stores.resellers.update` permission.
# Get Programme
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get
GET /stores/{storeId}/reseller
Returns the store's reseller settings plus headline counts. A store that has never configured a
programme comes back disabled with defaults rather than a 404.
Requires the `stores.resellers.view` permission.
# Get Applications
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-applications
GET /stores/{storeId}/reseller/application/submissions
The review queue: who has applied, what they answered, and what they have deposited since. The
deposit is summed from the balance ledger rather than stored, so it always matches the money
that actually arrived.
Requires the `stores.resellers.view` permission.
# Get Basket Discounts
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-basket-tiers
GET /stores/{storeId}/reseller/basket-tiers
Volume terms measured on what a whole basket comes to rather than on how many of one product it
holds, so a reseller taking a spread of your catalogue is credited for the whole commitment.
Requires the `stores.resellers.view` permission.
# Get Bundles
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-bundles
GET /stores/{storeId}/reseller/bundles
Each comes back priced the way your resellers will see it: what it contains, what that comes to
at list price, the discount, and what they would pay. A bundle whose products have left your
reseller catalogue comes back unavailable, with the reason, rather than disappearing.
Requires the `stores.resellers.view` permission.
# Get Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-catalogue
GET /stores/{storeId}/reseller/catalogue
The variants resellers can draw, each with its list price and the price a reseller pays, so the
merchant can see the margin they are giving away.
Requires the `stores.resellers.view` permission.
# Get Invitations
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-invites
GET /stores/{storeId}/reseller/invites
Tokens are never included — they exist only in the response that created them.
Requires the `stores.resellers.view` permission.
# Get Leaderboard
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-leaderboard
GET /stores/{storeId}/reseller/leaderboard
Your resellers ranked by what they spent with you over a period, biggest first. Built for
publishing: every reseller is identified by the store they sell from — its name, description,
storefront and branding, all of which is public already — so nothing here has to be masked
before it goes on a page.
`from` is inclusive and `to` exclusive. Omit them for the last thirty days.
Spend is net of refunds, so a reseller who was refunded more than they spent in the period
does not appear.
A reseller who sells outside Komerza still ranks, with no store attached — leaving them out
would produce a leaderboard whose total disagreed with your own revenue.
Requires the `stores.resellers.view` permission.
# Get Questions
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-questions
GET /stores/{storeId}/reseller/application/questions
The store's reseller application form, in the order applicants are asked. Retired questions are
left out — they exist only so answers already given to them stay readable.
Requires the `stores.resellers.view` permission.
# Get Resellers
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-resellers
GET /stores/{storeId}/reseller/resellers
Who resells for this store, what they have spent, and what they can currently draw against.
Does not include the resellers' own customers — who they sell to is their business.
Requires the `stores.resellers.view` permission.
# Get Variant Stats
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-variant-stats
GET /stores/{storeId}/reseller/catalogue/{variantId}/stats
How many resellers list this variant and what they have drawn, so the reselling side of a product
is visible on the product page rather than only in the reseller section.
Requires the `stores.resellers.view` permission.
# Grant Samples
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/grant-samples
POST /stores/{storeId}/reseller/resellers/{resellerId}/samples
Gives a reseller free units so they can prove delivery works end to end before funding a
balance. Samples are spent before money on a matching draw.
Requires the `stores.resellers.update` permission.
# Remove from Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/remove-from-catalogue
DELETE /stores/{storeId}/reseller/catalogue/{variantId}
Removes it from the catalogue entirely, discarding its price overrides. Resellers already
listing it keep the listing, but their next draw fails. To pause it and keep the terms, set
`isEnabled` to false instead.
Requires the `stores.resellers.update` permission.
# Set Volume Tiers
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/set-volume-tiers
PUT /stores/{storeId}/reseller/catalogue/{variantId}/tiers
Applies only when a reseller buys stock up front, not to ordinary draws — drawing one unit at a
time commits them to nothing, so there is nothing for a discount to buy. Replaces the whole set,
so what you send is exactly what applies.
Requires the `stores.resellers.update` permission.
# Update Basket Discount
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-basket-tier
PUT /stores/{storeId}/reseller/basket-tiers/{basketTierId}
Requires the `stores.resellers.update` permission.
# Update Bundle
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-bundle
PUT /stores/{storeId}/reseller/bundles/{bundleId}
Replaces the bundle wholesale, lines and all. Anyone who already bought it keeps what they
bought at the price they paid.
Requires the `stores.resellers.update` permission.
# Update Question
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-question
PUT /stores/{storeId}/reseller/application/questions/{questionId}
Requires the `stores.resellers.update` permission.
# Update Status
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-status
PUT /stores/{storeId}/reseller/resellers/{resellerId}/status
Revoking stops further draws but leaves the reseller's balance alone — they funded it and are
still owed it.
Requires the `stores.resellers.update` permission.
# Accept Invitation
Source: https://docs.komerza.com/api-reference/endpoint/reselling/accept-invite
POST /stores/{storeId}/reselling/invites/accept
Redeems an invite on behalf of the store in the route, which `PlatformAuthorize` has already
proven you own. The relationship is approved outright — the supplier decided that when they sent
it — and a draft product is created for everything the invite named, so you land on a dashboard
with stock to sell rather than a setup guide.
Only redeemable by an account on the address the invite was sent to.
Requires the `stores.reselling.update` permission.
# Add Funds
Source: https://docs.komerza.com/api-reference/endpoint/reselling/add-funds
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/topup
Creates an order on the supplier's store that credits your balance with them once paid. Drawing
stock and buying it in bulk both spend that balance, so this is what makes either possible.
The account credited is the one this store's relationship is held under, not whoever is
signed in — a staff member topping up must not end up crediting themselves.
Requires the `stores.reselling.update` permission.
# Apply to Supplier
Source: https://docs.komerza.com/api-reference/endpoint/reselling/apply
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}
Asks to resell a store's stock. A customer account on that store is created from the caller's
platform email, so there is nothing to sign up for separately. Stores that enrol openly approve
immediately; stores that take applications leave this pending.
Requires the `stores.reselling.update` permission.
# Buy Basket
Source: https://docs.komerza.com/api-reference/endpoint/reselling/buy-basket
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/basket
The basket is priced once and each line is then bought at its share of that price, landing on
your own products as ordinary stock exactly as a single-variant purchase would. Short deliveries
are refunded the same way.
Supply an idempotency key. Lines carry it with their own index appended, so a retried basket
replays line for line rather than buying twice.
Requires the `stores.reselling.update` permission.
# Buy Stock
Source: https://docs.komerza.com/api-reference/endpoint/reselling/buy-stock
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/stock
Buys units up front, at whatever volume price the supplier offers for that quantity, and puts
them on your own product as ordinary stock. Nothing is held or pending: the units are yours, they
sell through your normal delivery, and nothing the supplier does afterwards takes them away.
If the supplier delivers fewer than were bought, the difference is refunded to your balance
rather than quietly kept.
Requires the `stores.reselling.update` permission.
# Create Listing
Source: https://docs.komerza.com/api-reference/endpoint/reselling/create-listing
PUT /stores/{storeId}/reselling/suppliers/{supplierStoreId}/listings/{sourceVariantId}
Points one of this store's variants at a supplier's variant. Selling it then draws a unit from
the supplier automatically — no delivery configuration of any kind is needed on this side.
Requires the `stores.reselling.update` permission.
# Delete Listing
Source: https://docs.komerza.com/api-reference/endpoint/reselling/delete-listing
DELETE /stores/{storeId}/reselling/suppliers/{supplierStoreId}/listings/{sourceVariantId}
The store's own product keeps existing and keeps selling; it just no longer draws from the
supplier, which for a purely resold product means it can no longer be delivered. Worth
unpublishing it at the same time.
Requires the `stores.reselling.update` permission.
# Find Supplier
Source: https://docs.komerza.com/api-reference/endpoint/reselling/find-supplier
GET /stores/{storeId}/reselling/suppliers/lookup
There is no supplier directory, so this is how you reach a store you already know of: paste its
address.
Only stores currently taking applications are found. One running an invite-only programme, or
none at all, comes back as not found — which store has a reseller programme is not something
this should let anyone map out.
Requires the `stores.reselling.view` permission.
# Get Bundles
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-bundles
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/bundles
Baskets the supplier composed and priced as one thing, each quoted for this store: what is in
it, what that comes to at their list prices, the discount, and what you would pay. Bundles aimed
at other resellers are not listed, and cannot be bought by guessing an id.
Requires the `stores.reselling.view` permission.
# Get Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-catalogue
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/catalogue
Priced as it will actually cost this store, with whichever of its own products is already
sourced from each.
Requires the `stores.reselling.view` permission.
# Get Draws
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-draws
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/draws
One entry per draw, newest first, each quoting the supplier's own order id so it can be chased
upstream.
Requires the `stores.reselling.view` permission.
# Get Linkable Accounts
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-linkable
GET /stores/{storeId}/reselling/linkable
Relationships created by signing up on a supplier's own storefront, before this store existed or
before you thought to use it. They have no store attached, so none of the reselling here applies
to them and the supplier's panel keeps pointing you back at a dashboard that cannot see them.
Matched on your confirmed email address.
Requires the `stores.reselling.view` permission.
# Get Payment Methods
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-payment-methods
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/payment-methods
The supplier's own payment methods, since topping up is paid to them rather than to Komerza.
Only readable by a store that actually holds a relationship with them — a merchant's enabled
gateways are not public information.
Requires the `stores.reselling.view` permission.
# Get Resold Products
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-products
GET /stores/{storeId}/reselling/products
Every product of this store drawn from a supplier, with its cost, margin, and whether it would
currently sell. A resold product can stop being deliverable without its owner touching anything —
the supplier withdraws it, revokes them, raises the price past their ceiling, or runs out — so
each carries the reason it is blocked, if it is.
Requires the `stores.reselling.view` permission.
# Get Suppliers
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-suppliers
GET /stores/{storeId}/reselling/suppliers
Every store this one draws stock from, with what it can currently spend at each.
Requires the `stores.reselling.view` permission.
# Link Account
Source: https://docs.komerza.com/api-reference/endpoint/reselling/link-account
POST /stores/{storeId}/reselling/linkable/{resellerId}
Attaches an off-platform reseller relationship to this store. From then on its draws are served
in-process — stock lands on your own variants, there is no delivery URL to hold, and the balance
already held with that supplier comes across untouched.
Requires the `stores.reselling.update` permission.
# Quote Basket
Source: https://docs.komerza.com/api-reference/endpoint/reselling/quote-basket
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/basket/quote
Give it a bundle id, or lines of your own, and it comes back with the same arithmetic the
purchase will use — including what those lines would have cost bought separately, so a discount
you already have is not counted twice in what you tell yourself you saved.
Requires the `stores.reselling.view` permission.
# Create Store
Source: https://docs.komerza.com/api-reference/endpoint/stores/create
POST /stores
Creates a new store for the current user.
Requires the `stores.create` permission.
# Get Store
Source: https://docs.komerza.com/api-reference/endpoint/stores/get
GET /stores/{storeId}
Retrieves the specified store's reference information.
Requires the `stores.view` permission.
# Get Permissions
Source: https://docs.komerza.com/api-reference/endpoint/stores/get-permissions
GET /stores/{storeId}/permissions
Gets the permissions that the currently authenticated user has against the store, along with the payment methods
enabled by the store owner
Requires the `stores.view` permission.
This method can return permissions (otherwise known as scopes), you can learn
more about scopes by clicking here.
# Update Store
Source: https://docs.komerza.com/api-reference/endpoint/stores/update
PUT /stores/{storeId}
Replaces the specified store's data with the information in the form.
Requires the `stores.update` permission.
# Maintenance Mode
Source: https://docs.komerza.com/api-reference/endpoint/stores/update-maintenance
PUT /stores/{storeId}/maintenance
Enables or disables maintenance mode for the specified store.
Requires the `stores.update` permission.
**This takes your storefront offline for shoppers.** They see your message
instead of the store and cannot buy anything until it is switched back off.
## The reason is the switch
There is no `enabled` flag - maintenance is on or off depending on whether `reason` has a value.
| Send | Result |
| --------------------------------- | ---------------------------------------- |
| `{ "reason": "Back at 3pm UTC" }` | Maintenance **on**, showing that message |
| `{ "reason": null }` | Maintenance **off**, store live again |
```bash theme={null}
curl -X PUT "https://api.komerza.com/stores/{storeId}/maintenance" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
-H "Content-Type: application/json" \
-d '{"reason": "Restocking, back at 3pm UTC"}'
```
## Message rules
Up to **1024 characters**, and only letters, digits, spaces and `. , ! ? ' -`.
Colons and slashes are rejected, so **a URL will fail validation** - as will
emoji, `@`, `#`, `&` and brackets.
The message is public, so write it for shoppers.
## Reading the current state
The store object returns `maintenanceReason`; `null` means live.
If the storefront is dark while `maintenanceReason` is `null`, this endpoint
won't fix it - that's an expired plan, an unpaid Buyer Protection invoice, or
a suspended account.
# Get User
Source: https://docs.komerza.com/api-reference/endpoint/users/get
GET /user
Returns the currently authenticated user’s profile data.
Requires the `user.view` permission.
# Create Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/create
POST /webhooks
Creates a webhook that POSTs the events you subscribe to, for the stores you choose, as signed
JSON. The signing secret is returned once here, so save it to verify the X-Signature header.
Requires the `user.webhooks.create` permission.
**The signing secret is returned once, here, and never again.** There is no
endpoint or dashboard screen that will show it later. Store it when you
receive it; if you lose it, your only option is to delete the webhook and
create a new one.
You can hold up to **25 webhooks per user**, each scoped to one or more of
your stores. URLs must be HTTP or HTTPS, and any that resolve to private or
reserved addresses are rejected.
# Delete Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/delete
DELETE /webhooks/{webhookId}
Removes the webhook and all associated logs from the user account.
Requires the `user.webhooks.delete` permission.
**Deleting a webhook deletes all of its execution logs**, so any delivery
history you would need for debugging goes with it. Export anything you want to
keep first.
# Get Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/get
GET /webhooks/{webhookId}
Retrieves the webhook if it belongs to the currently authenticated user.
Requires the `user.webhooks.view` permission.
# Get Webhooks
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/get-list
GET /webhooks
Returns a list of all account-wide webhooks configured by the user.
Requires the `user.webhooks.view` permission.
# Get Webhook Logs
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/get-logs
GET /webhooks/{webhookId}/logs
Returns a webhook's delivery history: one entry per attempt, with the response code and, on
failures, the reason.
Supports filtering by response code and success/failure status via query parameters.
Requires the `user.webhooks.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| -------------- | ------- | --------------------------------------------------------------------------------- |
| `success` | boolean | Whether the delivery succeeded (a 2xx response) |
| `responseCode` | integer | The HTTP status your endpoint returned. `-1` means paused after repeated failures |
**Example:**
```bash theme={null}
# Failed deliveries only
GET /webhooks/{webhookId}/logs?filters=success==false
# Deliveries your endpoint rejected as unauthorised
GET /webhooks/{webhookId}/logs?filters=responseCode==401
```
# Resend Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/resend
POST /webhooks/{webhookId}/logs/{executionLogId}/resend
Re-queues the Hangfire job for a particular webhook execution log. If the job no longer exists or the user
does not own the log, it fails. This is a single execution log re-send.
Requires the `user.webhooks.update` permission.
# Search Webhook Logs
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/search-logs
GET /webhooks/{webhookId}/logs/search/{query}
Searches logs by log ID, order ID (if the query is a valid GUID), or by URL fragment.
All pagination and date/status filters from the listing endpoint are also supported.
Requires the `user.webhooks.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| -------------- | ------- | --------------------------------------------------------------------------------- |
| `success` | boolean | Whether the delivery succeeded (a 2xx response) |
| `responseCode` | integer | The HTTP status your endpoint returned. `-1` means paused after repeated failures |
**Example:**
```bash theme={null}
# Failed deliveries only
GET /webhooks/{webhookId}/logs?filters=success==false
# Deliveries your endpoint rejected as unauthorised
GET /webhooks/{webhookId}/logs?filters=responseCode==401
```
# Update Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/update
PUT /webhooks/{webhookId}
Overwrites the URL, events, and store IDs.
Requires the `user.webhooks.update` permission.
# Enums & Constants
Source: https://docs.komerza.com/api-reference/enums
Every enum value the API accepts and returns
Most enums are **integers** - send and expect the numeric value, not the name. The names below are for readability only; they never appear in a payload. String enums are marked as such.
Order statuses have their own page: [Order Status](/api-reference/payment-status).
## Products
### Privacy
Controls where a product appears and whether it can be bought.
| Value | Name | Meaning |
| ----- | -------- | ------------------------------------------------------ |
| `0` | Public | Visible on the store page and purchasable |
| `1` | Unlisted | Hidden from the store page, purchasable via direct URL |
| `2` | Private | Hidden entirely, not purchasable |
| `3` | OnHold | Visible but blocked from purchase |
### Product type
| Value | Name | Meaning |
| ----- | ------------ | --------------------------------------------- |
| `0` | OneTime | A single purchase |
| `1` | Subscription | Creates or extends a subscription on delivery |
### Billing interval
Applies to subscription variants.
| Value | Name |
| ----- | ----------------------------- |
| `0` | Weekly |
| `1` | Monthly |
| `2` | Quarterly |
| `3` | Yearly |
| `4` | Custom (interval set in days) |
### Stock calculation mode
| Value | Name | Meaning |
| ----- | ---------- | --------------------------------------------------------------- |
| `0` | Calculated | Stock equals the number of delivery items loaded on the variant |
| `1` | Ignored | Stock is not tracked; never out of stock |
| `2` | Fixed | A number you set and maintain yourself |
### Volume discount type
| Value | Name | Meaning |
| ----- | ----------- | ------------------------ |
| `0` | Percentage | A percentage off |
| `1` | FixedAmount | An amount off per unit |
| `2` | FixedPrice | A replacement unit price |
### Affiliate discount override
Set per product to override the store's default affiliate discount.
| Value | Name | Meaning |
| ----- | ---------- | ---------------------------------------------------------- |
| `0` | UseDefault | Use the store's default discount |
| `1` | Disabled | No customer discount; the affiliate still earns commission |
| `2` | Custom | Use the percentage set on the product |
### Delivery types
**String values**, supplied as an array on a variant. A variant can combine several.
| Value | Delivers |
| --------------- | ---------------------------------------------------------------------- |
| `license_keys` | Keys drawn from the variant's inventory |
| `files` | The uploaded file, via a time-limited download link |
| `dynamic` | Whatever your endpoint returns at delivery time |
| `instructions` | A static message included in the delivery email |
| `custom_fields` | Not a delivery mechanism - collects answers from the buyer at checkout |
`keyauth` still appears in some request descriptions. It was removed from the
product and no longer works - do not send it.
## Orders
### Refund reason
| Value | Name |
| ----- | ------------------- |
| `0` | Fraudulent |
| `1` | RequestedByCustomer |
| `2` | Duplicate |
| `3` | Other |
### Refund destination
| Value | Name | Meaning |
| ----- | --------------- | ----------------------------------- |
| `0` | OriginalMethod | Reverse through the payment gateway |
| `1` | CustomerBalance | Issue store credit instead |
Several payment methods only support `1` - see [Managing Orders](/guides/orders#refunds).
### Custom payment method type
| Value | Name | Meaning |
| ----- | ------------ | -------------------------------------------------- |
| `0` | Instructions | Show the buyer instructions to pay outside Komerza |
| `1` | Redirect | Send the buyer to a URL you specify |
## Subscriptions
### Subscription status
| Value | Name | Meaning |
| ----- | -------- | ------------------------------------------------------ |
| `0` | Active | Paid and current |
| `1` | PastDue | An automatic charge failed; retries are scheduled |
| `2` | Canceled | Ended by the merchant or the customer |
| `3` | Expired | Retries exhausted, or the period ended without renewal |
| `4` | Paused | Resumes automatically at its resume date |
### Cancel mode
| Value | Name | Meaning |
| ----- | ----------- | -------------------------------------------- |
| `0` | AtPeriodEnd | Access continues until the period ends |
| `1` | Immediate | Access is revoked at once, with no proration |
## Discord
### Join mode
| Value | Name | Meaning |
| ----- | -------------------------- | ------------------------------------------------------ |
| `0` | AddAndGrantAfterPayment | Added to the server and granted the role after payment |
| `1` | AddOnLinkGrantAfterPayment | Added when they link Discord; role waits for payment |
### Requirement
| Value | Name | Meaning |
| ----- | -------- | ---------------------------------------- |
| `0` | Required | Cannot check out without linking Discord |
| `1` | Optional | Offered but not enforced |
| `2` | Disabled | Not shown at checkout |
### Revocation behavior
Applied when an order is refunded or charged back.
| Value | Name | Meaning |
| ----- | --------------- | -------------------------------- |
| `0` | RevokeAndRemove | Remove the role and the member |
| `1` | RevokeRoleOnly | Remove the role, keep the member |
| `2` | DoNothing | Leave access in place |
## Customers & support
### Ticket status
| Value | Name | Meaning |
| ----- | ----------------- | ------------------------------------- |
| `0` | Opened | Awaiting the seller |
| `1` | Resolved | Closed, issue sorted. Final |
| `2` | Closed | Closed without resolution. Final |
| `3` | Responded | Seller replied; awaiting the customer |
| `4` | CustomerResponded | Customer replied; awaiting the seller |
The values are not in flow order: a ticket moves `0` → `3` ⇄ `4` → `1` or `2`.
### Customer segment
Used to target email marketing recipients.
| Value | Name |
| ----- | ---------- |
| `0` | All |
| `1` | New |
| `2` | Active |
| `3` | Regulars |
| `4` | Vip |
| `5` | Affiliates |
### Blacklist type
The kind of value a blacklist rule matches on.
| Value | Name |
| ----- | ----------------------- |
| `0` | EmailAddress |
| `1` | EmailDomain |
| `2` | IpAddress |
| `3` | IpCidr |
| `4` | AutonomousSystemNumber |
| `5` | InternetServiceProvider |
| `6` | CountryCode |
| `7` | City |
## Email marketing
### Campaign status
| Value | Name | Meaning |
| ----- | ------------ | --------------------------------------------------- |
| `0` | Draft | Editable, not queued |
| `1` | Scheduled | Queued for a future time; still cancellable |
| `2` | Sending | In progress; cannot be cancelled from the dashboard |
| `3` | Sent | Finished |
| `4` | Cancelled | Cancelled before sending |
| `5` | Failed | The reason is not exposed; contact support |
| `6` | Under review | Held by automated screening before sending |
### Recipient send status
Per-recipient outcome within a campaign.
| Value | Name |
| ----- | ---------- |
| `0` | Queued |
| `1` | Sent |
| `2` | Delivered |
| `3` | Bounced |
| `4` | Complained |
| `5` | Failed |
### Suppression type
| Value | Name | Meaning |
| ----- | ----------- | ------------------------- |
| `0` | Unsubscribe | Removable by the merchant |
| `1` | HardBounce | Permanent |
| `2` | Complaint | Permanent |
## Storefront
### Discount dialog trigger
| Value | Name | Meaning |
| ----- | ---------- | ------------------------------------- |
| `0` | Entrance | Shown when the visitor arrives |
| `1` | ExitIntent | Shown when the visitor moves to leave |
Enums used only by dashboard-internal endpoints - platform billing, passkey
registration, account standing - are deliberately not listed. They are not
part of the public API and can change without notice.
# Error Codes
Source: https://docs.komerza.com/api-reference/error-codes
Complete reference for all API error codes and how to handle them
Every failed request carries a `code`: a stable, machine-readable identifier for what went wrong. This page lists them all.
```json theme={null}
{
"success": false,
"message": "The request contains invalid parameters",
"code": "ValidationError",
"data": null
}
```
For the response envelope itself, validation errors and HTTP status codes, see
[Responses & Errors](/api-reference/responses).
## Error Codes Reference
### Authentication & Authorization
**HTTP Status:** 403 Forbidden
**Description:** You don't have permission to access this resource or perform this action.
**Common causes:**
* API key lacks required scope
* Trying to access another user's resources
* Account restrictions or suspensions
* Store-level permissions insufficient
**How to fix:**
* Check your API key has the necessary [scopes](/api-reference/scopes)
* Verify you're accessing resources you own
* Check account status in dashboard
* Request additional permissions if needed
**Example:**
```json theme={null}
{
"success": false,
"message": "Your API key does not have permission to update products",
"code": "AccessDenied",
"data": null
}
```
**HTTP Status:** 401 Unauthorized
**Description:** The provided authentication token is invalid, expired, or malformed.
**Common causes:**
* Expired API key
* Revoked API key
* Malformed Authorization header
* API key not found
**How to fix:**
* Generate a new API key from dashboard
* Verify Authorization header format: `Bearer YOUR_API_KEY`
* Check for whitespace or special characters in token
* Ensure API key hasn't been revoked
**Example:**
```json theme={null}
{
"success": false,
"message": "The provided API key is invalid or has expired",
"code": "BadToken",
"data": null
}
```
**HTTP Status:** 403 Forbidden
**Description:** Two-factor authentication is required for this action.
**Common causes:**
* Sensitive operation requires 2FA
* Account security policy enforced
* Administrative action attempted
**How to fix:**
* Complete 2FA challenge
* Enable 2FA on your account
* Use session-based authentication for sensitive operations
**Example:**
```json theme={null}
{
"success": false,
"message": "This operation requires two-factor authentication",
"code": "TwoFactorRequired",
"data": null
}
```
**HTTP Status:** 401 Unauthorized
**Description:** OAuth authentication is required for this endpoint.
**Common causes:**
* Endpoint requires user session, not API key
* OAuth flow not completed
* Session expired
**How to fix:**
* Complete OAuth authentication flow
* Use correct authentication method for endpoint
* Check if endpoint supports API key authentication
**Example:**
```json theme={null}
{
"success": false,
"message": "This endpoint requires OAuth authentication",
"code": "OAuthLoginRequired",
"data": null
}
```
### Validation & Input Errors
**HTTP Status:** 400 Bad Request
**Description:** The request contains invalid or missing parameters.
**Common causes:**
* Missing required fields
* Invalid data types
* Values outside allowed ranges
* Invalid format (email, URL, UUID, etc.)
* Business rule violations
**How to fix:**
* Check request body against API documentation
* Validate data types and formats
* Ensure all required fields are present
* Review error details for specific field errors
**Example:**
```json theme={null}
{
"success": false,
"message": "Price must be greater than 0",
"code": "ValidationError",
"data": null
}
```
**HTTP Status:** 400 Bad Request
**Description:** CAPTCHA verification failed.
**Common causes:**
* Invalid CAPTCHA response
* Expired CAPTCHA token
* CAPTCHA not solved
* Bot detection triggered
**How to fix:**
* Request new CAPTCHA challenge
* Ensure user completes CAPTCHA
* Check CAPTCHA token hasn't expired
* Verify CAPTCHA integration is correct
**Example:**
```json theme={null}
{
"success": false,
"message": "CAPTCHA verification failed. Please try again.",
"code": "BadCaptcha",
"data": null
}
```
### Resource Errors
**HTTP Status:** 404 Not Found
**Description:** The requested resource does not exist.
**Common causes:**
* Invalid resource ID
* Resource was deleted
* Typo in endpoint URL
* Resource belongs to different store
**How to fix:**
* Verify resource ID is correct
* Check resource hasn't been deleted
* Ensure you're querying the right store
* Validate endpoint URL
**Example:**
```json theme={null}
{
"success": false,
"message": "Product not found",
"code": "NotFound",
"data": null
}
```
**HTTP Status:** 409 Conflict
**Description:** The operation conflicts with an existing resource.
**Common causes:**
* Duplicate unique field (email, slug, etc.)
* Resource already exists
* Concurrent modification conflict
* Business rule prevents operation
**How to fix:**
* Use unique values for unique fields
* Check if resource already exists
* Implement optimistic locking for concurrent updates
* Review business rules
**Example:**
```json theme={null}
{
"success": false,
"message": "A product with this slug already exists",
"code": "ObjectConflict",
"data": null
}
```
### Rate Limiting
**HTTP Status:** 429 Too Many Requests
**Description:** You have exceeded the API rate limit.
**Common causes:**
* Too many requests in short time period
* Burst limit exceeded
* Account-level rate limit reached
**How to fix:**
* Implement exponential backoff
* Respect `Retry-After` header
* Cache frequently accessed data
* Optimize API calls to reduce frequency
* Contact support for higher limits
**Response headers:**
```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1638360000
Retry-After: 60
```
**Example:**
```json theme={null}
{
"success": false,
"message": "Rate limit exceeded. Please retry after 60 seconds.",
"code": "RateLimited",
"data": null
}
```
### Business Logic Errors
**HTTP Status:** 400 Bad Request
**Description:** The requested product is not available for purchase.
**Common causes:**
* Product out of stock
* Product is private or unlisted
* Product deleted or disabled
* Product not available in customer's region
* Purchase limits exceeded
**How to fix:**
* Check product availability
* Verify product stock levels
* Ensure product is public
* Check regional restrictions
**Example:**
```json theme={null}
{
"success": false,
"message": "This product is currently out of stock",
"code": "ProductUnavailable",
"data": null
}
```
**HTTP Status:** 400 Bad Request
**Description:** Insufficient funds in customer balance.
**Common causes:**
* Customer balance too low
* Attempting to use balance payment method
* Withdrawal amount exceeds available balance
**How to fix:**
* Check customer balance
* Request customer add funds
* Use alternative payment method
* Reduce order amount
**Example:**
```json theme={null}
{
"success": false,
"message": "Insufficient customer balance",
"code": "NotEnoughFunds",
"data": null
}
```
**HTTP Status:** 403 Forbidden
**Description:** Account verification is required before proceeding.
**Common causes:**
* Email not verified
* Identity verification pending
* Payment method requires verification
* Account limits require verification
**How to fix:**
* Complete email verification
* Submit required verification documents
* Verify payment methods
* Contact support for verification status
**Example:**
```json theme={null}
{
"success": false,
"message": "Email verification required",
"code": "VerificationRequired",
"data": null
}
```
### Feature & Upgrade Errors
**HTTP Status:** 403 Forbidden
**Description:** This feature is not available for your account.
**Common causes:**
* Feature disabled for your plan
* Beta feature not enabled
* Regional restrictions
* Feature requires specific integration
**How to fix:**
* Check feature availability for your plan
* Contact support to enable beta features
* Verify regional availability
* Review feature requirements
**Example:**
```json theme={null}
{
"success": false,
"message": "Advanced analytics is not available on your current plan",
"code": "FeatureUnavailable",
"data": null
}
```
**HTTP Status:** 402 Payment Required
**Description:** Your current plan does not support this operation.
**Common causes:**
* Plan limits exceeded
* Feature requires higher plan tier
* Usage quota exhausted
**How to fix:**
* Upgrade to higher plan tier
* Review current plan limits
* Reduce usage to stay within limits
* Contact sales for enterprise options
**Example:**
```json theme={null}
{
"success": false,
"message": "You have reached the maximum number of products for your plan",
"code": "UpgradeRequired",
"data": null
}
```
**HTTP Status:** 400 Bad Request
**Description:** The requested operation is not supported.
**Common causes:**
* Deprecated API version
* Invalid operation combination
* Unsupported payment method
* Unsupported currency
**How to fix:**
* Check API documentation for supported operations
* Use current API version
* Verify operation compatibility
* Check supported payment methods/currencies
**Example:**
```json theme={null}
{
"success": false,
"message": "This payment method is not supported for subscriptions",
"code": "UnsupportedRequest",
"data": null
}
```
### File & Upload Errors
**HTTP Status:** 400 Bad Request
**Description:** File upload failed.
**Common causes:**
* File too large
* Invalid file type
* Corrupted file
* Network interruption
* Storage quota exceeded
**How to fix:**
* Check file size limits
* Verify file type is supported
* Ensure file is not corrupted
* Retry upload with stable connection
* Check storage quota
**Example:**
```json theme={null}
{
"success": false,
"message": "File size exceeds maximum allowed size",
"code": "UploadFailed",
"data": null
}
```
### Server Errors
**HTTP Status:** 500 Internal Server Error
**Description:** An unexpected error occurred on the server.
**Common causes:**
* Server-side bug
* Database connection issue
* External service failure
* Unexpected condition
**How to fix:**
* Retry the request after a short delay
* If problem persists, contact support
* Check [status page](https://status.komerza.com) for incidents
* Include error ID when contacting support
**Example:**
```json theme={null}
{
"success": false,
"message": "An unexpected error occurred",
"code": "InternalServerError",
"data": null
}
```
Always include the `errorId` when reporting issues to support for faster resolution.
## HTTP Status Code Summary
| Status Code | Error Codes | Description |
| ----------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| 400 | ValidationError, BadCaptcha, ProductUnavailable, NotEnoughFunds, UnsupportedRequest, UploadFailed | Bad Request - Invalid input |
| 401 | BadToken, OAuthLoginRequired | Unauthorized - Invalid or missing authentication |
| 402 | UpgradeRequired | Payment Required - Plan upgrade needed |
| 403 | AccessDenied, TwoFactorRequired, VerificationRequired, FeatureUnavailable | Forbidden - Insufficient permissions |
| 404 | NotFound | Not Found - Resource doesn't exist |
| 409 | ObjectConflict | Conflict - Resource conflict |
| 429 | RateLimited | Too Many Requests - Rate limit exceeded |
| 500 | InternalServerError | Internal Server Error - Server-side issue |
## Support Resources
Check API and service status
Browse error troubleshooting guides
Get help from the community
Report issues with error IDs
# Filtering & Sorting
Source: https://docs.komerza.com/api-reference/filtering
Narrow down, order and page through list endpoints
List endpoints return everything by default. Four query parameters let you narrow that down:
| Parameter | Does |
| ---------- | ---------------------------- |
| `filters` | Which items come back |
| `sorts` | What order they come back in |
| `page` | Which page you want |
| `pageSize` | How many per page |
You can use any of them on their own. Endpoints that support them show a **Filterable Fields** table on their reference page - that table is the list of field names you're allowed to use.
## Start with one filter
A filter is three parts stuck together: **field**, **operator**, **value**.
```bash theme={null}
GET /stores/{storeId}/orders?filters=status==pending
```
That's `status` (field) `==` (equals) `pending` (value). Add more by separating with commas - every condition has to match:
```bash theme={null}
# Pending orders, paid by Stripe
GET /stores/{storeId}/orders?filters=status==pending,gateway==stripe
```
Then order them, newest first, with `-` meaning descending:
```bash theme={null}
GET /stores/{storeId}/orders?filters=status==pending&sorts=-dateFrom
```
And take them 50 at a time:
```bash theme={null}
GET /stores/{storeId}/orders?filters=status==pending&sorts=-dateFrom&page=1&pageSize=50
```
That's the whole idea. The rest of this page is the detail.
**A field name that isn't on the endpoint's list is ignored, not rejected.**
You get a `200` with unfiltered results rather than an error. If a filter
seems to do nothing, check the spelling against that endpoint's Filterable
Fields table before looking anywhere else.
## Operators
**Comparing numbers and dates:**
| Operator | Meaning | Example |
| -------- | ------------ | ---------------------- |
| `==` | Equals | `status==pending` |
| `!=` | Not equals | `gateway!=stripe` |
| `>` | Greater than | `rating>3` |
| `<` | Less than | `rating<3` |
| `>=` | At least | `dateFrom>=2026-01-01` |
| `<=` | At most | `dateTo<=2026-06-30` |
**Matching text:**
| Operator | Meaning | Example |
| -------- | ------------------- | ------------------ |
| `@=` | Contains | `email@=gmail` |
| `_=` | Starts with | `subject_=Refund` |
| `_-=` | Ends with | `email_-=.com` |
| `!@=` | Does not contain | `email!@=test` |
| `!_=` | Does not start with | `subject!_=[spam]` |
| `!_-=` | Does not end with | `email!_-=.ru` |
Text matching is case-sensitive. Add a `*` to the end of any operator to ignore case:
```bash theme={null}
?filters=subject@=Premium # matches "Premium", not "premium"
?filters=subject@=*premium # matches both
```
Field names are always case-insensitive, so `dateFrom` and `datefrom` both work.
## Matching several values
Separate values with `|` to match any of them:
```bash theme={null}
# Orders paid by Stripe or PayPal
?filters=gateway==stripe|paypal
```
Put several **fields** in brackets to run the same test against each, matching if any one hits:
```bash theme={null}
# "urgent" in either the subject or the customer's email
?filters=(subject|customerEmail)@=urgent
```
## Values that need care
Enum fields take their numeric value: `visibility==0`, **not**
`visibility==Public`. Passing the name matches nothing and returns an empty
list. Every value is in [Enums & Constants](/api-reference/enums).
Order status and gateway are the exceptions - they take the string name, as
their field tables show.
`dateFrom>=2026-01-01`, or with a time `dateFrom>=2026-01-01T09:30:00Z`.
`isBestSeller==true`. Anything that isn't recognisably a boolean counts as
`false`.
These mean something to the parser, so escape them with a backslash:
| To match | Write |
| --------------- | -------------------- |
| `some,value` | `field@=some\,value` |
| `some\|value` | `field@=some\|value` |
| the text "null" | `field@=\null` |
Unescaped, `field@=some,value` is read as two separate filters.
`!=` leaves out rows where the field is empty. `gateway!=stripe` returns
orders paid another way, but not orders with no gateway at all.
## Two shortcut filters
These aren't real fields, they're prepared questions. Pass `true`; passing `false` is the same as leaving them out.
| Field | On | Matches |
| ---------- | -------- | ----------------------------------------------------------- |
| `lowStock` | Products | Has a license-key variant and fewer than 5 in stock overall |
| `hasReply` | Reviews | Already has a merchant reply |
## Sorting
One field, or several separated by commas. The first is the main order, the rest break ties. `-` means descending.
```bash theme={null}
?sorts=-dateFrom # newest first
?sorts=status,-dateFrom # by status, newest first inside each status
```
When paging through results, sort by something nearly unique. If lots of rows
share the same value, their order can shift between requests, so you may see
the same row twice or miss one entirely.
## Paging
`page` starts at 1. `pageSize` defaults to **20** and is capped at **100** - ask for 500 and you get 100, with no error.
Responses tell you how many pages there are:
```json theme={null}
{
"success": true,
"pages": 5,
"data": [
// items
]
}
```
There's no total item count, so keep requesting pages until you reach `pages`.
A few endpoints use an older style, with the page in the path and a `limit` or
`count` parameter:
```
GET /stores/{storeId}/products/{productId}/variants/{variantId}/items/2?count=50
```
Their reference pages show what they accept.
## Recipes
Real requests you can paste and edit. `curl -G` with `--data-urlencode` matters here: filter values are full of `=`, `>`, `|` and `*`, and letting curl encode them avoids the request being mangled on the way out.
**Last 30 days of pending orders, newest first**
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/orders" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=status==pending,dateFrom>=2026-01-02" \
--data-urlencode "sorts=-dateFrom"
```
**Unanswered 1-2 star reviews**
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/reviews" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=rating<3,hasReply==false" \
--data-urlencode "sorts=-dateRangeFrom"
```
**Open tickets from one customer, oldest first**
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/tickets" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=status==0,customerEmail@=*buyer@example.com" \
--data-urlencode "sorts=dateCreated" \
--data-urlencode "pageSize=50"
```
**Public best-sellers running low on stock**
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/products" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=visibility==0,isBestSeller==true,lowStock==true"
```
## When it doesn't work
| What you see | What's wrong |
| --------------------------------- | ---------------------------------------------------------------------------------------------- |
| The filter changes nothing | The field isn't on that endpoint's list, or is misspelled. Unknown fields are ignored silently |
| Empty list when filtering an enum | You passed the name instead of the number - use `visibility==0` |
| One value became two filters | It contains a comma; escape it as `\,` |
| `pageSize=500` returned 100 items | 100 is the maximum, and larger values are quietly clamped |
| Results shuffle between pages | Equal sort values have no guaranteed order - add a tie-breaker field |
# Introduction
Source: https://docs.komerza.com/api-reference/introduction
The Komerza REST API: what it covers and where to start
The Komerza API is a REST interface to everything in your store. Anything you can do in the dashboard, you can automate: manage your catalogue, read and fulfil orders, issue refunds, look after customers, run coupons and affiliates, and receive events on your own server.
```
https://api.komerza.com
```
} href="/api-reference/quickstart">
Create a key, find your store ID and get a real response back, in three steps.
## What you can build
}>
Sync products, variants, pricing and stock from wherever you already manage
them.
}>
Read orders, deliver them, issue refunds, and push status into your own
systems.
}>
Drive checkout from a site you build yourself, with your own design.
}>
Feed orders and customers into accounting, CRM, fulfilment or analytics.
## Start here
} href="/api-reference/authentication">
API keys, required headers and scopes.
} href="/api-reference/responses">
The response envelope, list responses and how failures look.
} href="/api-reference/conventions">
IDs, dates, enums, money and store scoping.
} href="/api-reference/filtering">
Narrow, order and page through list endpoints.
## Related
} href="/api-reference/sdks">
Official libraries for .NET, JavaScript, Python, Go and PHP.
} href="/guides/webhooks">
Order events pushed to your server, instead of polling for them.
} href="/storefront/introduction">
Cart and checkout on a site you host yourself.
} href="https://status.komerza.com">
Live API availability.
# Exchange Code
Source: https://docs.komerza.com/api-reference/oauth2/exchange
GET /oauth2/{appId}/exchange/{code}
## Overview
Exchange an authorization code for a JWT API key. This is the final step in the OAuth2 flow, converting the temporary code into a permanent access token.
This endpoint requires the `X-OAuth2-Client-Secret` header for authentication instead
of the standard `Authorization` header.
## Path Parameters
| Parameter | Type | Description |
| --------- | ------ | ------------------------------------------------- |
| `appId` | string | Your application's Client ID |
| `code` | string | The authorization code received from the callback |
## Headers
| Header | Required | Description |
| ------------------------ | -------- | --------------------------------------------- |
| `X-OAuth2-Client-Secret` | Yes | Your application's 72-character client secret |
| `User-Agent` | Yes | Your application identifier |
**Server-Side Only**: This request must be made from your backend server,
never from client-side code. The client secret must remain confidential.
## Response
```json theme={null}
{
"success": true,
"data": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```
The `data` field contains a JWT API key that you can use to make authenticated API requests.
## Using the API Key
Include the JWT as a Bearer token in subsequent API requests:
```bash theme={null}
curl -X GET https://api.komerza.com/stores/STORE_ID \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "User-Agent: MyApp/1.0"
```
## Token Properties
The JWT API key:
* **Grants access** only to the stores the user authorized
* **Has permissions** limited to the scopes you requested and the user approved
* **Does not expire** until the user revokes authorization
* **Is tied** to the specific user and application
## Error Responses
| Error | Description |
| ----------------------- | ----------------------------------------------------------------- |
| `invalid_code` | The authorization code is invalid or has already been used |
| `code_expired` | The authorization code has expired (codes are valid for 1 minute) |
| `invalid_client_secret` | The client secret is incorrect |
| `app_not_found` | The application ID is invalid |
## Complete Flow Example
```javascript theme={null}
// 1. User clicks "Connect with Komerza" in your app
const authUrl = `https://dashboard.komerza.com/auth/authorize?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&state=${randomState}`;
window.location.href = authUrl;
// 2. User authorizes on Komerza, redirected back to your app
// URL: https://myapp.com/callback?code=ENCRYPTED_CODE&state=randomState
// 3. Your backend exchanges the code for an API key
const response = await fetch(
`https://api.komerza.com/oauth2/${CLIENT_ID}/exchange/${code}`,
{
headers: {
"X-OAuth2-Client-Secret": process.env.CLIENT_SECRET,
"User-Agent": "MyApp/1.0",
},
}
);
const { data: apiKey } = await response.json();
// 4. Store the API key securely and use it for API calls
const stores = await fetch("https://api.komerza.com/stores", {
headers: {
Authorization: `Bearer ${apiKey}`,
"User-Agent": "MyApp/1.0",
},
});
```
# OAuth2 Integration
Source: https://docs.komerza.com/api-reference/oauth2/introduction
Allow third-party applications to access user accounts with their consent
## Overview
Komerza's OAuth2 system enables third-party developers to create applications that can securely access user accounts with explicit user consent. This allows you to build integrations, plugins, and services that interact with Komerza stores on behalf of users.
Register your OAuth2 application in the Komerza Dashboard
## How It Works
Register your application with Komerza, specifying the permissions you need
and your redirect URLs.
Redirect users to Komerza's authorization page where they can review and
approve your app's access.
After approval, users are redirected back to your app with a temporary
authorization code.
Exchange the authorization code for a JWT API key that grants access to the
authorized stores.
## Authorization URL
Send users to the following URL to initiate the OAuth2 flow:
```
https://dashboard.komerza.com/auth/authorize
```
### Query Parameters
| Parameter | Required | Description |
| -------------- | -------- | ------------------------------------------------------ |
| `client_id` | Yes | Your application's Client ID |
| `redirect_uri` | Yes | One of your registered redirect URLs |
| `state` | No | Optional value passed back to your app in the callback |
### Example Authorization URL
```
https://dashboard.komerza.com/auth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=https://myapp.com/callback&state=random123
```
## OAuth2 Flow Diagram
```
┌─────────────┐ ┌─────────────┐
│ Your App │ │ Komerza │
└──────┬──────┘ └──────┬──────┘
│ │
│ 1. Redirect user to authorization URL │
│ ─────────────────────────────────────────> │
│ │
│ 2. User reviews & approves │
│ │
│ 3. Redirect back with authorization code │
│ <───────────────────────────────────────── │
│ │
│ 4. Exchange code for API key │
│ ─────────────────────────────────────────> │
│ │
│ 5. Return JWT API key │
│ <───────────────────────────────────────── │
│ │
│ 6. Make API calls with Bearer token │
│ ─────────────────────────────────────────> │
│ │
```
## Permissions & Scopes
When creating your application, you must specify which permissions (scopes) your app requires. Users will see these permissions during the authorization flow and can choose which stores to grant access to.
Complete reference for all available permission scopes
## Security Best Practices
**Client Secret Security**: Your client secret is like a password. Never
expose it in client-side code, public repositories, or logs.
All redirect URLs should use HTTPS in production environments.
Always exchange authorization codes from your backend server, never from
client-side code.
Store client secrets and API keys securely using environment variables or
secret management systems.
Only request the permissions your application actually needs.
## Token Characteristics
| Property | Value |
| ------------------------------- | --------------------- |
| **Token Type** | JWT (JSON Web Token) |
| **Authorization Code Validity** | 1 minute (single-use) |
| **API Key Validity** | Until revoked |
| **Max Redirect URLs** | 10 per application |
## Quick Links
Exchange authorization code for API key
# Payment Methods
Source: https://docs.komerza.com/api-reference/payment-methods
Complete reference for all supported payment methods and cryptocurrencies
## Overview
Komerza supports a wide range of payment methods including traditional payment gateways and cryptocurrencies. Each payment method has a unique identifier used throughout the API.
## Payment Gateways
### Traditional Processors
**Stripe** - Industry-leading payment processor supporting cards, wallets, and
local payment methods
**SumUp** - European payment gateway for card processing
**Square** - Payment processor with integrated point-of-sale solutions
**Cash App** - Mobile payment service for instant transfers
**Authorize.Net** - Long-established payment gateway for online transactions
**NMI** - Payment gateway similar to Authorize.Net
**Mollie** - European payment service provider supporting cards, iDEAL,
Bancontact, and 20+ local payment methods
**VenPayr** - Card payment processor
**moneymotion** - Card payment processor
**Pandabase** - Card payment processor
**Storrik** - Card payment processor
**Shopify** - Card payments through Shopify's hosted checkout
### PayPal
**PayPal Business** - PayPal business accounts with Instant Payment
Notifications (IPNs)
**PayPal Friends & Family** - PayPal personal payment method
### Customer Balance
**Customer Balance** - Payment using customer's account balance
## Cryptocurrencies
### Major Cryptocurrencies
**Bitcoin** - The original and most widely recognized cryptocurrency
* Network: Bitcoin
* Symbol: BTC
* Type: Native coin
**Ethereum** - Leading smart contract platform - Network: Ethereum - Symbol:
ETH - Type: Native coin
**Litecoin** - Faster alternative to Bitcoin - Network: Litecoin - Symbol: LTC
* Type: Native coin
**Bitcoin Cash** - Bitcoin fork with larger block sizes - Network: Bitcoin
Cash - Symbol: BCH - Type: Native coin
**Monero** - Privacy-focused cryptocurrency - Network: Monero - Symbol: XMR -
Type: Native coin
**Dash** - Digital cash with instant transactions - Network: Dash - Symbol:
DASH - Type: Native coin
**Dogecoin** - Popular meme-based cryptocurrency
* Network: Dogecoin
* Symbol: DOGE
* Type: Native coin
### Smart Contract Platforms
**Solana** - High-performance blockchain platform
* Network: Solana
* Symbol: SOL
* Type: Native coin
**Cardano** - Proof-of-stake blockchain platform - Network: Cardano - Symbol:
ADA - Type: Native coin
**TRON** - Decentralized entertainment ecosystem - Network: TRON - Symbol: TRX
* Type: Native coin
**Nano** - Zero-fee instant transactions
* Network: Nano
* Symbol: NANO
* Type: Native coin
### Stablecoins
Stablecoins are cryptocurrencies designed to maintain a stable value, typically pegged to fiat currencies like USD.
**USDT** - Most widely used stablecoin (1 USDT ≈ 1 USD)
Available on multiple networks:
* `usdt` - USD Tether (primary)
* `usdterc20` - USD Tether on Ethereum (ERC-20)
* `usdtbsc` - USD Tether on Binance Smart Chain (BEP-20)
* `usdtsol` - USD Tether on Solana (SPL)
**USDC** - Regulated stablecoin (1 USDC ≈ 1 USD) Available on multiple
networks: - `usdc` - USD Coin (primary) - `usdcbsc` - USD Coin on Binance
Smart Chain (BEP-20) - `usdcsol` - USD Coin on Solana (SPL)
**BUSD** - Binance's regulated stablecoin
* Network: Multiple
* Symbol: BUSD
* Peg: 1 BUSD ≈ 1 USD
### Exchange Tokens
**BNB** - Binance Smart Chain native token - Network: Binance Smart Chain -
Symbol: BNB - Type: Exchange utility token
### Meme Tokens
**SHIB** - Popular meme token - Network: Ethereum - Symbol: SHIB - Type:
ERC-20 token
## Related Resources
Process payments and create orders
Track payment statuses
# Order Status
Source: https://docs.komerza.com/api-reference/payment-status
Reference for all order statuses and their meanings
The order has not received payment and is considered incomplete. This is the
initial status for all new orders.
The order has received payment and the funds are pending or being verified by
the payment gateway.
The funds have been received but are withheld for verification. Commonly used
for cryptocurrency payments awaiting blockchain confirmations or fraud checks.
The order is being delivered. This is a guard status to prevent multiple
deliveries from happening simultaneously.
The order has been verified, paid for, and the product has been delivered to
the customer.
The order has been verified and paid for, but issues were encountered during
delivery that prevented fulfillment. This is typically due to missing stock
(e.g., no license keys available).
The order has been partially paid. The customer sent less than the required
amount.
The order was cancelled by the customer or the store before completion.
The store refunded the amount or the customer disputed the payment and won.
The order has been disputed by the customer, usually through a chargeback with
their payment gateway.
# Quick Start
Source: https://docs.komerza.com/api-reference/quickstart
Make your first Komerza API call
Three steps: create a key, call `/user` to find your store, then call something with that store ID. You need a Komerza account and dashboard access.
## 1. Create an API key
1. Open **[Account Settings → API Keys](https://dashboard.komerza.com/account?api)** in the dashboard
2. Click **Create New API Key** and give it a name you'll recognise later
3. Select the scopes it needs - `user.view` and `stores.view` are enough for this guide
4. Copy the key
The key is shown once. Store it in an environment variable or a secrets
manager, never in source control, and give each integration its own key so you
can revoke one without breaking the rest.
Every request needs two headers:
| Header | Value |
| --------------- | ---------------------------------------------------- |
| `Authorization` | `Bearer YOUR_API_KEY` |
| `User-Agent` | Something that identifies your app, e.g. `MyApp/1.0` |
## 2. Find your store ID
Almost every endpoint is scoped to a store. `GET /user` returns your account, with the stores on it:
```bash theme={null}
curl "https://api.komerza.com/user" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0"
```
```json theme={null}
{
"success": true,
"message": null,
"code": null,
"data": {
"id": "…",
"email": "you@example.com",
"plan": 1,
"stores": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "My Store",
"url": "mystore",
"currencyCode": "USD"
}
]
}
}
```
The `id` of the store you want is what every `{storeId}` in these docs refers to.
You can also copy it from the dashboard: **Online Store → Settings →
General**, where the **Store ID** field has a copy button next to it.
## 3. Call an endpoint
Fetch the store you just found:
```bash theme={null}
curl "https://api.komerza.com/stores/{storeId}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0"
```
```json theme={null}
{
"success": true,
"message": null,
"code": null,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"dateCreated": "2026-01-15T10:30:00Z",
"name": "My Store",
"description": "Digital goods, delivered instantly",
"url": "mystore",
"customDomain": "shop.example.com",
"currencyCode": "USD",
"rating": 4.8,
"isTicketingEnabled": true,
"isCustomerBalanceEnabled": false
}
}
```
That's it - you're authenticated and reading real data.
## The response envelope
Every response has the same four fields:
| Field | What it is |
| --------- | ------------------------------------------------------ |
| `success` | Whether the request worked |
| `message` | Human-readable detail, often `null` on success |
| `code` | Error code when something went wrong, otherwise `null` |
| `data` | The payload - an object, an array, or `null` |
Errors keep the same shape:
```json theme={null}
{
"success": false,
"message": "Access denied."
}
```
Check `success` rather than assuming a `200` means you got what you asked for.
## When it doesn't work
The key is missing, malformed or revoked. Check you sent `Authorization:
Bearer YOUR_KEY` including the `Bearer ` prefix, and that the key still
exists in the dashboard.
The key is valid but lacks the scope for that endpoint. Scopes follow the
resource: reading orders needs `stores.orders.view`, refunding them needs
`stores.orders.refund`. The full list is in [Scopes](/api-reference/scopes).
Scopes are fixed when the key is created, so create a new key with the ones
you need.
Usually a store ID that doesn't exist, belongs to another account, or picked
up a stray character. Confirm it against `GET /user`. Check the path too:
the base URL is `https://api.komerza.com` with **no** `/api` prefix.
You're sending requests faster than allowed. Back off and retry with
increasing delays rather than immediately.
## Next steps
} href="/api-reference/filtering">
Narrow, order and page through list endpoints.
} href="/api-reference/scopes">
Every permission a key can carry.
} href="/api-reference/endpoint/products/get-list">
Read and manage your catalogue.
} href="/api-reference/endpoint/orders/get-list">
Query orders, deliver and refund them.
} href="/guides/webhooks">
Get order events pushed to your server instead of polling.
} href="/api-reference/sdks">
Official libraries for .NET, JavaScript, Python, Go and PHP.
# Scopes
Source: https://docs.komerza.com/api-reference/scopes
Complete reference for all available API key permission scopes
## Overview
API keys use a permission-based scope system that controls access to specific endpoints and operations. Each API key can be configured with one or more scopes to limit access according to your integration needs.
API keys can be generated and scopes can be configured from your [Komerza
Dashboard](https://dashboard.komerza.com/account?api).
## Scope Categories
### Store Management
**Permission to view stores** Read access to store information, settings,
and configuration. Required for most store-related read operations.
**Permission to update stores** Modify store configuration, settings,
branding, and operational parameters.
**Permission to create new stores** Create new store instances. Typically used
for multi-store applications or store management platforms.
**Permission to view store analytics** Access store performance metrics,
sales data, traffic statistics, and analytical insights.
### Categories
**Permission to view categories in stores** Read access to product
categories, category hierarchy, and organization structure.
**Permission to create new categories in stores** Add new product categories
to organize your store catalog.
**Permission to update categories in stores** Modify category information,
reorganize hierarchy, and update category settings.
**Permission to delete categories in stores** Remove categories from the
store. Ensure products are reassigned before deleting categories.
### Products & Inventory
**Permission to view products in stores** Read access to product catalog,
variants, pricing, and inventory information.
**Permission to create new products in stores** Add new products to the store
catalog, including variants and pricing.
**Permission to update products in stores** Modify existing products, update
pricing, inventory levels, and product details.
**Permission to delete products in stores** Remove products from the store
catalog. Use with caution as this action may affect order history.
### Customer Management
**Permission to view customers in stores** Access customer profiles, contact
information, and purchase history.
**Permission to create new customers in stores** Add new customer profiles to
the store database.
**Permission to update customers in stores** Modify customer information,
update balances, and manage customer accounts.
### Order Processing
**Permission to view orders in stores** Access order information, payment
status, and order history.
**Permission to create orders** Process new orders, including payment
processing and order creation.
**Permission to deliver orders in stores** Mark orders as delivered, update
tracking information, and manage fulfillment.
**Permission to refund orders** Process refunds, partial refunds, and handle
order cancellations.
### Marketing & Promotions
**Permission to view coupons in stores** Access coupon information, usage
statistics, and discount configurations.
**Permission to create new coupons in stores** Create discount codes,
promotional campaigns, and coupon configurations.
**Permission to update coupons in stores** Modify existing coupons, update
discount amounts, and change validity periods.
**Permission to delete coupons in stores** Remove coupons from the system.
Active coupons should be disabled rather than deleted.
### Affiliate Program
**Permission to view affiliates in stores** Access affiliate information,
commission data, and performance statistics.
**Permission to update affiliates in stores** Modify affiliate settings,
commission rates, and payout configurations.
### Support & Communication
**Permission to view tickets in stores** Access support tickets, customer
inquiries, and communication history.
**Permission to update tickets in stores** Respond to tickets, update ticket
status, and manage customer support workflows.
**Permission to delete tickets in stores** Remove tickets from the system.
Consider archiving instead of deleting for audit purposes.
**Permission to flag reviews in stores** Mark reviews as inappropriate,
spam, or requiring moderation.
### User Account & Platform
**Permission to view user profile information** Access user account details,
preferences, and profile information.
**Permission to view enabled cryptocurrencies for a user** Access
cryptocurrency payment settings and enabled payment methods.
This scope is deprecated and will be removed in future versions. Please avoid
using it.
### Webhooks & Integrations
**Permission to view webhooks** Access webhook configurations, event
subscriptions, and delivery logs.
**Permission to manage webhooks: create** Set up new webhook endpoints and
event subscriptions.
**Permission to manage webhooks: update** Modify existing webhook
configurations and event subscriptions.
**Permission to manage webhooks: delete** Remove webhook endpoints and
cancel event subscriptions.
## Best Practices
### Principle of Least Privilege
Only request the minimum scopes necessary for your application. This reduces security risks and makes it easier to audit API access.
### Scope Planning
Plan your required scopes during the design phase of your integration. Consider:
* Which resources you need to read vs. modify
* Whether you need create/delete permissions
* If your application handles sensitive customer data
# SDKs
Source: https://docs.komerza.com/api-reference/sdks
Official SDKs for the Komerza API - coming soon
## Overview
We're building official SDKs to make integrating with the Komerza API even easier. These SDKs will provide idiomatic interfaces, automatic error handling, and built-in best practices for each programming language.
All SDKs are currently in development. While you wait, you can use the API
directly via HTTP requests as shown in our [Quick Start
Guide](/api-reference/quickstart).
## Planned SDKs
**Coming Soon** Full-featured SDK for .NET applications with strong typing
and async support.
**Coming Soon** Modern SDK with TypeScript definitions for Node.js and browser
environments.
**Coming Soon** Pythonic interface with support for async/await and popular
frameworks.
**Coming Soon** Modern PHP SDK with Composer support and PSR-compliant design.
**Coming Soon** Idiomatic Go SDK with proper error handling and context
support.
## SDK Previews
Here's what the SDK interfaces will look like:
```csharp .NET theme={null}
using Komerza.Api;
var client = new KomerzaClient("your-api-key");
// Get store information
var store = await client.Stores.GetAsync(storeId);
// List products with pagination
var products = await client.Products.ListAsync(storeId, new ProductListOptions
{
Page = 1,
PageSize = 20
});
// Create a new product
var newProduct = await client.Products.CreateAsync(storeId, new CreateProductRequest
{
Name = "New Product",
Price = 29.99m,
Currency = "USD"
});
```
```javascript JavaScript/TypeScript theme={null}
import { KomerzaClient } from "@komerza/api";
const client = new KomerzaClient("your-api-key");
// Get store information
const store = await client.stores.get(storeId);
// List products with pagination
const products = await client.products.list(storeId, {
page: 1,
pageSize: 20,
});
// Create a new product
const newProduct = await client.products.create(storeId, {
name: "New Product",
price: 29.99,
currency: "USD",
});
```
```python Python theme={null}
from komerza import KomerzaClient
client = KomerzaClient('your-api-key')
# Get store information
store = await client.stores.get(store_id)
# List products with pagination
products = await client.products.list(store_id, page=1, page_size=20)
# Create a new product
new_product = await client.products.create(store_id, {
'name': 'New Product',
'price': 29.99,
'currency': 'USD'
})
```
```php PHP theme={null}
stores->get($storeId);
// List products with pagination
$products = $client->products->list($storeId, [
'page' => 1,
'pageSize' => 20
]);
// Create a new product
$newProduct = $client->products->create($storeId, [
'name' => 'New Product',
'price' => 29.99,
'currency' => 'USD'
]);
```
```go Go theme={null}
package main
import (
"context"
"github.com/komerza/go-sdk"
)
func main() {
client := komerza.NewClient("your-api-key")
ctx := context.Background()
// Get store information
store, err := client.Stores.Get(ctx, storeID)
if err != nil {
// Handle error
}
// List products with pagination
products, err := client.Products.List(ctx, storeID, &komerza.ProductListOptions{
Page: 1,
PageSize: 20,
})
// Create a new product
newProduct, err := client.Products.Create(ctx, storeID, &komerza.CreateProductRequest{
Name: "New Product",
Price: 29.99,
Currency: "USD",
})
}
```
## Get Notified
Want to be the first to know when an SDK is released?
Get updates in our Telegram developer community
Watch our GitHub organization for new SDK releases
## In the Meantime
While waiting for the official SDKs, you can:
### Use the REST API Directly
Follow our [Quick Start Guide](/api-reference/quickstart) to start building with HTTP requests right away.
### Build Your Own Wrapper
Create a simple wrapper around the API for your specific use case. Our [API Reference](/api-reference/introduction) provides all the details you need.
### Community SDKs
Check if there are any community-built SDKs available. We'll showcase quality community contributions here.
## Contributing
Interested in contributing to an SDK or building one for another language?
Get in touch about SDK development opportunities
We welcome contributions and are happy to work with the community to build SDKs for additional programming languages and frameworks.
## Stay Updated
Check our help center for SDK documentation as it becomes available
Learn the API while waiting for your preferred SDK
# Blacklist Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/blacklist
POST /stores/{storeId}/affiliates/{customerId}/blacklist
Disables the affiliate, blocks the customer on the store, and adds their email to the store's blacklist.
Requires the `stores.affiliates.update` permission.
Blacklisting stops the affiliate earning on new referrals. It does not claw
back commission already accrued, and it does not refund orders they referred.
# Configure Affiliate Program
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/configure
PUT /stores/{storeId}/affiliates/configure
Updates the configuration options for the affiliate program of the specified store.
Requires the `stores.affiliates.update` permission.
# Create Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/create
POST /stores/{storeId}/affiliates/{customerId}
Creates a new affiliate record for the given customer in this store.
Requires the `stores.affiliates.update` permission.
# Delete Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/delete
DELETE /stores/{storeId}/affiliates/{customerId}
Permanently removes an affiliate and all their payout records from the store.
Requires the `stores.affiliates.update` permission.
# Get Affiliates
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/get-list
GET /stores/{storeId}/affiliates
Retrieves affiliates for the specified store using query-based filter options.
Supports filtering, sorting, and pagination via query parameters.
Requires the `stores.affiliates.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ---------------------- | ------- | ----------------------------------------------------------- |
| `isEnabled` | boolean | Filter by affiliate enabled status |
| `email` | string | Filter by email address (contains) |
| `link` | string | Filter by affiliate link/code (contains) |
| `balanceFrom` | number | Minimum affiliate balance (inclusive) |
| `balanceTo` | number | Maximum affiliate balance (inclusive) |
| `returnPercentageFrom` | integer | Minimum return percentage (inclusive) |
| `returnPercentageTo` | integer | Maximum return percentage (inclusive) |
| `percentageOffFrom` | integer | Minimum discount percentage (inclusive) |
| `percentageOffTo` | integer | Maximum discount percentage (inclusive) |
| `referredOrdersFrom` | integer | Minimum referred delivered orders (inclusive) |
| `referredOrdersTo` | integer | Maximum referred delivered orders (inclusive) |
| `allTimeEarnedFrom` | number | Minimum all-time earned amount (inclusive) |
| `allTimeEarnedTo` | number | Maximum all-time earned amount (inclusive) |
| `payoutCountFrom` | integer | Minimum payout count (inclusive) |
| `payoutCountTo` | integer | Maximum payout count (inclusive) |
| `dateFrom` | string | Filter affiliates created from this date/time (inclusive) |
| `dateTo` | string | Filter affiliates created before this date/time (inclusive) |
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | -------- | ----------------------------------------------------- |
| `isEnabled` | boolean | Filter by whether the affiliate profile is enabled |
| `email` | string | Filter by affiliate email (case-insensitive contains) |
| `link` | string | Filter by referral link or code |
| `balance` | number | Current unpaid balance |
| `allTimeEarned` | number | Total commission earned across all time |
| `referredOrders` | integer | Number of orders attributed to this affiliate |
| `payoutCount` | integer | Number of payouts recorded against this affiliate |
| `returnPercentage` | number | The affiliate's commission rate |
| `percentageOff` | number | The customer discount applied to their referrals |
| `dateCreated` | datetime | When the affiliate profile was created |
Numeric and date fields take ranges through the comparison operators rather than separate from/to parameters.
**Sortable:** `isEnabled`, `email`, `link`, `referredOrders`, `allTimeEarned`, `payoutCount`, `dateCreated`.
**Example:**
```bash theme={null}
# Enabled affiliates who have earned over 100, biggest earners first
GET /stores/{storeId}/affiliates?filters=isEnabled==true,allTimeEarned>=100&sorts=-allTimeEarned
# Affiliates with an outstanding balance and at least one referred order
GET /stores/{storeId}/affiliates?filters=balance>0,referredOrders>=1
```
# List Affiliate Orders
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/get-orders
GET /stores/{storeId}/affiliates/{customerId}/orders/{page}
Retrieves a paginated list of orders attributed to a specific affiliate (by customerId) in the store.
Requires the `stores.affiliates.view` permission.
# List Affiliate Payouts
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/get-payouts
GET /stores/{storeId}/affiliates/{customerId}/payouts/{page}
Requires the `stores.affiliates.view` permission.
# Invite Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/invite
POST /stores/{storeId}/affiliates/invite
Invites an affiliate, this will create a customer profile if one does not exist and takes in a customer email
address instead.
Requires the `stores.affiliates.update` permission.
# Record Affiliate Payout
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/record-payout
POST /stores/{storeId}/affiliates/{customerId}/payouts
Requires the `stores.affiliates.update` permission.
**This records a payout, it does not send money.** Komerza reduces the
affiliate's balance by the amount; the actual transfer happens between you and
your affiliate outside Komerza.
Record it only once you have actually paid, or your balances will disagree with
reality.
# Search Affiliates
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/search
GET /stores/{storeId}/affiliates/search
Searches affiliates by the `query` parameter and applies filtering/sorting/pagination.
The query is matched against affiliate email, affiliate link/code, affiliate ID, and customer ID.
Requires the `stores.affiliates.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | -------- | ----------------------------------------------------- |
| `isEnabled` | boolean | Filter by whether the affiliate profile is enabled |
| `email` | string | Filter by affiliate email (case-insensitive contains) |
| `link` | string | Filter by referral link or code |
| `balance` | number | Current unpaid balance |
| `allTimeEarned` | number | Total commission earned across all time |
| `referredOrders` | integer | Number of orders attributed to this affiliate |
| `payoutCount` | integer | Number of payouts recorded against this affiliate |
| `returnPercentage` | number | The affiliate's commission rate |
| `percentageOff` | number | The customer discount applied to their referrals |
| `dateCreated` | datetime | When the affiliate profile was created |
Numeric and date fields take ranges through the comparison operators rather than separate from/to parameters.
**Sortable:** `isEnabled`, `email`, `link`, `referredOrders`, `allTimeEarned`, `payoutCount`, `dateCreated`.
**Example:**
```bash theme={null}
# Enabled affiliates who have earned over 100, biggest earners first
GET /stores/{storeId}/affiliates?filters=isEnabled==true,allTimeEarned>=100&sorts=-allTimeEarned
# Affiliates with an outstanding balance and at least one referred order
GET /stores/{storeId}/affiliates?filters=balance>0,referredOrders>=1
```
# Get Affiliate Statistics
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/stats
GET /stores/{storeId}/affiliates/stats
Retrieves the affiliate statistics associated with a specific store.
Requires the `stores.affiliates.view` permission.
# Update Affiliate
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/update
PUT /stores/{storeId}/affiliates/{customerId}
Updates affiliate settings for the given customer in this store.
Requires the `stores.affiliates.update` permission.
# Void Affiliate Payout
Source: https://docs.komerza.com/api-reference/endpoint/affiliates/void-payout
PATCH /stores/{storeId}/affiliates/{customerId}/payouts/{payoutId}
Marks the payout as voided, restores balances, and records the admin and reason.
Requires the `stores.affiliates.update` permission.
Voiding a recorded payout **restores the amount to the affiliate's balance**.
Use it when a payout was recorded in error or the transfer failed - not to
reverse money you actually sent.
# Create Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/create
POST /stores/{storeId}/categories
Creates a new category in the store.
Requires the `stores.categories.create` permission.
# Delete Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/delete
DELETE /stores/{storeId}/categories/{categoryId}
Deletes an existing category from the store.
Requires the `stores.categories.delete` permission.
# Get Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/get
GET /stores/{storeId}/categories/{categoryId}
Retrieves a category by its ID with full product details.
Requires the `stores.categories.view` permission.
# Get All Categories
Source: https://docs.komerza.com/api-reference/endpoint/categories/get-all
GET /stores/{storeId}/categories/all
Retrieves all categories in the store as references (no pagination).
Requires the `stores.categories.view` permission.
# Get Categories
Source: https://docs.komerza.com/api-reference/endpoint/categories/get-list
GET /stores/{storeId}/categories
Gets the categories in the store with pagination and filtering.
Requires the `stores.categories.view` permission.
# Order Categories
Source: https://docs.komerza.com/api-reference/endpoint/categories/order
PATCH /stores/{storeId}/categories/order
Updates the display order of categories.
Requires the `stores.categories.update` permission.
# Order Products in Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/order-products
PATCH /stores/{storeId}/categories/{categoryId}/products/order
Updates the display order of products within a category.
Requires the `stores.categories.update` permission.
# Update Category
Source: https://docs.komerza.com/api-reference/endpoint/categories/update
PUT /stores/{storeId}/categories/{categoryId}
Updates an existing category in the store.
Requires the `stores.categories.update` permission.
# Create Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/create
POST /stores/{storeId}/coupons
Creates a new coupon in the store.
Requires the `stores.coupons.create` permission.
# Delete Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/delete
DELETE /stores/{storeId}/coupons/{couponId}
Deletes an existing coupon from the store.
Requires the `stores.coupons.delete` permission.
# Get Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/get
GET /stores/{storeId}/coupons/{couponId}
Gets a coupon by ID.
Requires the `stores.coupons.view` permission.
# Get All Coupons
Source: https://docs.komerza.com/api-reference/endpoint/coupons/get-all
GET /stores/{storeId}/coupons/all
Gets the coupons in the store as references.
Requires the `stores.coupons.view` permission.
# Get Coupons
Source: https://docs.komerza.com/api-reference/endpoint/coupons/get-list
GET /stores/{storeId}/coupons
Gets the coupons in the store.
Requires the `stores.coupons.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------ |
| `usesFrom` | integer | Filter by minimum number of times coupon has been used |
| `usesTo` | integer | Filter by maximum number of times coupon has been used |
| `maximumUsesFrom` | integer | Filter by minimum maximum uses allowed |
| `maximumUsesTo` | integer | Filter by maximum maximum uses allowed |
| `percentageFrom` | decimal | Filter by minimum percentage discount |
| `percentageTo` | decimal | Filter by maximum percentage discount |
| `amountFrom` | decimal | Filter by minimum amount discount |
| `amountTo` | decimal | Filter by maximum amount discount |
| `isEnabled` | boolean | Filter by whether coupon is enabled |
**Example:**
```bash theme={null}
# Active coupons with 20-50% discount
GET /stores/{storeId}/coupons?filters=isEnabled==true,percentageFrom>=20,percentageTo<=50
# Heavily used coupons
GET /stores/{storeId}/coupons?filters=usesFrom>=100&sorts=-uses
```
# Search Coupons
Source: https://docs.komerza.com/api-reference/endpoint/coupons/search
GET /stores/{storeId}/coupons/search/{query}
Searches the coupons in the store by the given query.
Requires the `stores.coupons.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------ |
| `usesFrom` | integer | Filter by minimum number of times coupon has been used |
| `usesTo` | integer | Filter by maximum number of times coupon has been used |
| `maximumUsesFrom` | integer | Filter by minimum maximum uses allowed |
| `maximumUsesTo` | integer | Filter by maximum maximum uses allowed |
| `percentageFrom` | decimal | Filter by minimum percentage discount |
| `percentageTo` | decimal | Filter by maximum percentage discount |
| `amountFrom` | decimal | Filter by minimum amount discount |
| `amountTo` | decimal | Filter by maximum amount discount |
| `isEnabled` | boolean | Filter by whether coupon is enabled |
**Example:**
```bash theme={null}
# Active coupons with 20-50% discount
GET /stores/{storeId}/coupons?filters=isEnabled==true,percentageFrom>=20,percentageTo<=50
# Heavily used coupons
GET /stores/{storeId}/coupons?filters=usesFrom>=100&sorts=-uses
```
# Update Coupon
Source: https://docs.komerza.com/api-reference/endpoint/coupons/update
PUT /stores/{storeId}/coupons/{couponId}
Requires the `stores.coupons.update` permission.
# Create Customer
Source: https://docs.komerza.com/api-reference/endpoint/customers/create
POST /stores/{storeId}/customers
Registers a customer for the store using their email.
If the customer already exists and is linked to the store, a 409 conflict is returned.
Requires the `stores.customers.create` permission.
# Get Customer
Source: https://docs.komerza.com/api-reference/endpoint/customers/get
GET /stores/{storeId}/customers/{customerId}
Retrieves detailed information for a single customer in the specified store.
Requires the `stores.customers.view` permission.
# Get Customer by Email
Source: https://docs.komerza.com/api-reference/endpoint/customers/get-by-email
GET /stores/{storeId}/customers/{customerEmail}/email
Retrieves detailed information for a single customer in the specified store.
Requires the `stores.customers.view` permission.
# Get Customers
Source: https://docs.komerza.com/api-reference/endpoint/customers/get-list
GET /stores/{storeId}/customers
Retrieves a paginated list of customers for the specified store.
Requires the `stores.customers.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | ------- | ---------------------------------------------------- |
| `email` | string | Filter by email address (case-insensitive contains) |
| `ipAddress` | string | Filter by IP address (case-insensitive contains) |
| `balanceFrom` | decimal | Filter by minimum balance (inclusive) |
| `balanceTo` | decimal | Filter by maximum balance (inclusive) |
| `stripeCustomerId` | string | Filter by Stripe customer ID (exact match) |
| `affiliateCode` | string | Filter by affiliate code (case-insensitive contains) |
**Example:**
```bash theme={null}
# Customers with balance between $10 and $100
GET /stores/{storeId}/customers?filters=balanceFrom>=10,balanceTo<=100
# Customers by affiliate code
GET /stores/{storeId}/customers?filters=affiliateCode@=*PROMO
```
# Search Customers
Source: https://docs.komerza.com/api-reference/endpoint/customers/search
GET /stores/{storeId}/customers/search/{query}
Retrieves a paginated list of customers for the specified store, matching the provided query.
Requires the `stores.customers.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ------------------ | ------- | ---------------------------------------------------- |
| `email` | string | Filter by email address (case-insensitive contains) |
| `ipAddress` | string | Filter by IP address (case-insensitive contains) |
| `balanceFrom` | decimal | Filter by minimum balance (inclusive) |
| `balanceTo` | decimal | Filter by maximum balance (inclusive) |
| `stripeCustomerId` | string | Filter by Stripe customer ID (exact match) |
| `affiliateCode` | string | Filter by affiliate code (case-insensitive contains) |
**Example:**
```bash theme={null}
# Customers with balance between $10 and $100
GET /stores/{storeId}/customers?filters=balanceFrom>=10,balanceTo<=100
# Customers by affiliate code
GET /stores/{storeId}/customers?filters=affiliateCode@=*PROMO
```
# Update Customer Balance
Source: https://docs.komerza.com/api-reference/endpoint/customers/update-balance
PATCH /stores/{storeId}/customers/{customerId}/balance
Updates the store-specific balance for a customer.
Requires the `stores.customers.update` permission.
**Store credit is real spendable value.** The balance applies immediately at
your checkout and never expires, so treat this like issuing money.
It is not cashable - customers cannot withdraw it - and a top-up order can
never be refunded to balance, since that would credit them twice.
# Approve Custom Payment
Source: https://docs.komerza.com/api-reference/endpoint/orders/approve-custom-payment
POST /stores/{storeId}/paymentMethods/custom/orders/{orderId}/approve
Approves a pending custom payment order, marking it as delivered.
Requires the `stores.orders.deliver` permission.
# Create Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/create
POST /stores/{storeId}/orders
Creates a new order for a customer in the admin dashboard.
Requires the `stores.orders.create` permission.
## Payment Methods
When creating an order, specify the `paymentMethod` field using one of the supported payment method codes. See the [Payment Methods reference](/api-reference/payment-methods) for the complete list of available options.
## Redirecting to Checkout
After successfully creating an order, redirect the customer to the hosted checkout page to complete payment:
```
https://checkout.komerza.com/checkout/{orderId}
```
### Example
```javascript theme={null}
const response = await fetch(`/stores/${storeId}/orders`, {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}` },
body: JSON.stringify(orderData),
});
const result = await response.json();
if (result.success) {
const orderId = result.data.id;
window.location.href = `https://checkout.komerza.com/checkout/${orderId}`;
}
```
The checkout page handles payment processing, order confirmation, and receipt
generation automatically.
# Deliver Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/deliver
PUT /stores/{storeId}/orders/{orderId}/deliver
Forces delivery of an existing order as if purchased normally.
No action is taken if the order is already delivered.
Requires the `stores.orders.deliver` permission.
**This does not verify that you were paid.** It runs the full delivery flow as
though payment had arrived: stock is consumed, license keys leave your
inventory, the receipt email is sent, `order.completed` fires and Discord roles
are granted.
Confirm the payment first. For custom payment methods, use [Approve Custom
Payment](/api-reference/endpoint/orders/approve-custom-payment) instead, which
is the reviewed path.
Manually delivered orders can only ever be refunded to **customer balance**,
never back to the gateway.
# Get Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/get
GET /stores/{storeId}/orders/{orderId}
Retrieve order details by ID in the given store.
Requires the `stores.orders.view` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
# Get Orders
Source: https://docs.komerza.com/api-reference/endpoint/orders/get-list
GET /stores/{storeId}/orders
Gets orders in the specified store and optionally filters by the given criteria.
Requires the `stores.orders.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ---------- | -------- | -------------------------------------------------------------------------------- |
| `dateFrom` | datetime | Filter orders created on or after this date |
| `dateTo` | datetime | Filter orders created on or before this date |
| `status` | string | Filter by order status. See [Order Statuses](/api-reference/payment-status) |
| `gateway` | string | Filter by payment gateway. See [Payment Methods](/api-reference/payment-methods) |
| `email` | string | Filter by customer email address |
**Example:**
```bash theme={null}
# Orders from last 30 days with "pending" status
GET /stores/{storeId}/orders?filters=dateFrom>=2025-10-28,status==pending
# Orders paid via Stripe, sorted by newest first
GET /stores/{storeId}/orders?filters=gateway==stripe&sorts=-dateFrom
```
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
# Get Proof of Payment
Source: https://docs.komerza.com/api-reference/endpoint/orders/get-payment-proof
GET /stores/{storeId}/paymentMethods/custom/orders/{orderId}/proof
Gets the proof of payment submitted by a customer for an order.
Requires the `stores.orders.view` permission.
# Re-deliver Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/redeliver
PUT /stores/{storeId}/orders/{orderId}/redeliver
Forces redelivery of an already delivered order as if purchased normally.
Requires the `stores.orders.deliver` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
**This reruns the entire delivery, exactly like a fresh purchase.** It takes
**new license keys** from your inventory, re-fires dynamic delivery webhooks,
and emails the customer again.
Call it once, deliberately. Repeated calls consume stock every time, and there
is no way to return issued keys to inventory.
# Refund Order
Source: https://docs.komerza.com/api-reference/endpoint/orders/refund
DELETE /stores/{storeId}/orders/{orderId}/refund
Refunds a delivered order for a customer in the admin dashboard.
Requires the `stores.orders.refund` permission.
**Refunds move real money and cannot be undone.** Only **Delivered** orders can
be refunded, always for the **full amount** - there are no partial refunds.
A successful refund also reverses any affiliate commission and the Buyer
Protection accrual on that order.
For cryptocurrency, custom payment methods, Cash App and PayPal Friends &
Family, marking an order refunded **moves no money** - you must return the
funds yourself. Those methods can only refund to customer balance.
# Reject Custom Payment
Source: https://docs.komerza.com/api-reference/endpoint/orders/reject-custom-payment
POST /stores/{storeId}/paymentMethods/custom/orders/{orderId}/reject
Rejects a pending custom payment order, cancelling it.
Requires the `stores.orders.deliver` permission.
# Resend Receipt
Source: https://docs.komerza.com/api-reference/endpoint/orders/resend-receipt
PUT /stores/{storeId}/orders/{orderId}/resend
Resends the delivery receipt to the customer for a previously delivered order.
Rate limited due to sensitivity.
Requires the `stores.orders.deliver` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
# Search Orders
Source: https://docs.komerza.com/api-reference/endpoint/orders/search
GET /stores/{storeId}/orders/search/{query}
Search orders by ID or customer email in the specified store.
Requires the `stores.orders.view` permission.
View all possible order status values and their meanings
See supported payment gateways and cryptocurrencies
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| ---------- | -------- | -------------------------------------------------------------------------------- |
| `dateFrom` | datetime | Filter orders created on or after this date |
| `dateTo` | datetime | Filter orders created on or before this date |
| `status` | string | Filter by order status. See [Order Statuses](/api-reference/payment-status) |
| `gateway` | string | Filter by payment gateway. See [Payment Methods](/api-reference/payment-methods) |
| `email` | string | Filter by customer email address |
**Example:**
```bash theme={null}
# Orders from last 30 days with "pending" status
GET /stores/{storeId}/orders?filters=dateFrom>=2025-10-28,status==pending
# Orders paid via Stripe, sorted by newest first
GET /stores/{storeId}/orders?filters=gateway==stripe&sorts=-dateFrom
```
# Delete Product
Source: https://docs.komerza.com/api-reference/endpoint/products/delete
DELETE /stores/{storeId}/products/{productId}
Deletes a product from the store.
Requires the `stores.products.delete` permission.
**Deleting a product takes its reviews with it.** Variants, custom fields,
volume discounts and images go too, and none of it can be restored.
Files already delivered to customers are kept, so past buyers keep their
downloads. If you only want a product off the storefront, set its privacy to
Private or On Hold instead of deleting it.
# Bulk Delete Products
Source: https://docs.komerza.com/api-reference/endpoint/products/delete-bulk
DELETE /stores/{storeId}/products
Deletes multiple products in one request. This will delete all variants, files, images, and reviews
associated with each product. Products that don't exist are silently skipped.
Requires the `stores.products.delete` permission.
**Irreversible, across every product in the request.** Each one loses its
variants, custom fields, volume discounts, images and reviews.
Files already delivered to customers are kept. To hide products rather than
destroy them, bulk-update their privacy instead.
# Duplicate Product
Source: https://docs.komerza.com/api-reference/endpoint/products/duplicate
POST /stores/{storeId}/products/{productId}/duplicate
Creates a copy of an existing product.
Requires the `stores.products.create` permission.
# Order Products
Source: https://docs.komerza.com/api-reference/endpoint/products/order
PATCH /stores/{storeId}/products/order
Updates the display order of products.
Requires the `stores.products.update` permission.
# Order Product Images
Source: https://docs.komerza.com/api-reference/endpoint/products/order-images
PATCH /stores/{storeId}/products/{productId}/images/order
Updates the display order of images for a product.
Requires the `stores.products.update` permission.
# Update Product
Source: https://docs.komerza.com/api-reference/endpoint/products/update
PUT /stores/{storeId}/products/{productId}
Updates basic information of a product.
Requires the `stores.products.update` permission.
# Update Affiliate Discount
Source: https://docs.komerza.com/api-reference/endpoint/products/update-affiliate-discount
PUT /stores/{storeId}/products/{productId}/affiliateDiscount
Updates affiliate discount configuration of a product.
Requires the `stores.products.update` permission.
# Update Product Badges
Source: https://docs.komerza.com/api-reference/endpoint/products/update-badges
PUT /stores/{storeId}/products/{productId}/badges
Replaces every badge on the product at once (bulk set). An empty list clears all badges.
Requires the `stores.products.update` permission.
# Bulk Update Products
Source: https://docs.komerza.com/api-reference/endpoint/products/update-bulk
PUT /stores/{storeId}/products
Updates multiple products in one request.
Requires the `stores.products.update` permission.
# Update Payment Settings
Source: https://docs.komerza.com/api-reference/endpoint/products/update-payment
PUT /stores/{storeId}/products/{productId}/payment
Sets a custom fee structure for a product.
Requires the `stores.products.update` permission.
# Update Product Security
Source: https://docs.komerza.com/api-reference/endpoint/products/update-security
PUT /stores/{storeId}/products/{productId}/security
Updates security information of a product.
Requires the `stores.products.update` permission.
# Add to Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/add-to-catalogue
PUT /stores/{storeId}/reseller/catalogue/{variantId}
Adds a variant to the catalogue or updates its terms. The variant does not have to be visible on
the storefront — offering resellers something ordinary customers never see is a supported use.
Requires the `stores.resellers.update` permission.
# Configure Programme
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/configure
PUT /stores/{storeId}/reseller
Sets whether the store supplies resellers, how they enrol, the discount they pay, and the
optional exposure and top-up limits.
Requires the `stores.resellers.update` permission.
# Create Invitation
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-invite
POST /stores/{storeId}/reseller/invites
Returns the invite token once and only once — only its hash is stored, because the token grants
an already-approved reseller relationship and a leaked invite list must not be usable. Send it on
immediately; it cannot be read back.
Requires the `stores.resellers.update` permission.
# Get Programme
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get
GET /stores/{storeId}/reseller
Returns the store's reseller settings plus headline counts. A store that has never configured a
programme comes back disabled with defaults rather than a 404.
Requires the `stores.resellers.view` permission.
# Get Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-catalogue
GET /stores/{storeId}/reseller/catalogue
The variants resellers can draw, each with its list price and the price a reseller pays, so the
merchant can see the margin they are giving away.
Requires the `stores.resellers.view` permission.
# Get Invitations
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-invites
GET /stores/{storeId}/reseller/invites
Tokens are never included — they exist only in the response that created them.
Requires the `stores.resellers.view` permission.
# Get Resellers
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-resellers
GET /stores/{storeId}/reseller/resellers
Who resells for this store, what they have spent, and what they can currently draw against.
Does not include the resellers' own customers — who they sell to is their business.
Requires the `stores.resellers.view` permission.
# Get Variant Stats
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-variant-stats
GET /stores/{storeId}/reseller/catalogue/{variantId}/stats
How many resellers list this variant and what they have drawn, so the reselling side of a product
is visible on the product page rather than only in the reseller section.
Requires the `stores.resellers.view` permission.
# Grant Samples
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/grant-samples
POST /stores/{storeId}/reseller/resellers/{resellerId}/samples
Gives a reseller free units so they can prove delivery works end to end before funding a
balance. Samples are spent before money on a matching draw.
Requires the `stores.resellers.update` permission.
# Remove from Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/remove-from-catalogue
DELETE /stores/{storeId}/reseller/catalogue/{variantId}
Removes it from the catalogue entirely, discarding its price overrides. Resellers already
listing it keep the listing, but their next draw fails. To pause it and keep the terms, set
`isEnabled` to false instead.
Requires the `stores.resellers.update` permission.
# Set Volume Tiers
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/set-volume-tiers
PUT /stores/{storeId}/reseller/catalogue/{variantId}/tiers
Applies only when a reseller buys stock up front, not to ordinary draws — drawing one unit at a
time commits them to nothing, so there is nothing for a discount to buy. Replaces the whole set,
so what you send is exactly what applies.
Requires the `stores.resellers.update` permission.
# Update Status
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-status
PUT /stores/{storeId}/reseller/resellers/{resellerId}/status
Revoking stops further draws but leaves the reseller's balance alone — they funded it and are
still owed it.
Requires the `stores.resellers.update` permission.
# Get Review Analytics
Source: https://docs.komerza.com/api-reference/endpoint/reviews/analytics
GET /stores/{storeId}/reviews/analytics
Gets the review analytics for a store.
Requires the `stores.view` permission.
# Get Reviews
Source: https://docs.komerza.com/api-reference/endpoint/reviews/get-list
GET /stores/{storeId}/reviews
Gets the current and previous reviews for a store.
Requires the `stores.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| --------------- | -------- | ---------------------------------------------------- |
| `rating` | integer | Filter by star rating (1-5) |
| `emailAddress` | string | Filter by customer email (case-insensitive contains) |
| `hasReply` | boolean | Filter reviews that have a merchant reply |
| `dateRangeFrom` | datetime | Filter reviews created on or after this date |
| `dateRangeTo` | datetime | Filter reviews created on or before this date |
**Example:**
```bash theme={null}
# 5-star reviews from last 30 days
GET /stores/{storeId}/reviews?filters=rating==5,dateRangeFrom>=2025-10-28
# Reviews without replies, sorted by newest first
GET /stores/{storeId}/reviews?filters=hasReply==false&sorts=-dateRangeFrom
```
# Reply to Review
Source: https://docs.komerza.com/api-reference/endpoint/reviews/reply
PUT /stores/{storeId}/reviews/{reviewId}/reply
Requires the `stores.update` permission.
Replies are **public**. Whatever you send here appears under the review on
your storefront, next to the customer's original words.
## How it works
A review holds **one** reply, stored on the review itself rather than as a thread. That shapes the whole endpoint:
* **`PUT` overwrites.** Call it again on the same review and you replace the previous reply - this is also how you edit one. There is no separate update endpoint.
* **There is no delete.** `reply` is required and must be at least 8 characters, so you cannot blank it out to remove a reply. Replace it with better wording instead.
## Request
```json theme={null}
{
"reply": "Sorry this landed late - your key was reissued the same day and we've since fixed the delay."
}
```
| Field | Rules |
| ------- | ------------------------------------------- |
| `reply` | Required. Between **8 and 4096** characters |
## Permission
Requires **`stores.update`**, not a review-specific scope. A key that can reply to reviews can also change other store settings, so scope keys with that in mind.
## Finding what to reply to
Use [Get Reviews](/api-reference/endpoint/reviews/get-list) with `hasReply==false` to fetch the ones still waiting, worst first:
```bash theme={null}
curl -G "https://api.komerza.com/stores/{storeId}/reviews" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "User-Agent: MyApp/1.0" \
--data-urlencode "filters=hasReply==false,rating<3" \
--data-urlencode "sorts=-dateRangeFrom"
```
A reply is read by far more future buyers than by the reviewer. Answer the
question the next shopper is silently asking - what went wrong, and what you
did about it.
# Add Ticket Response
Source: https://docs.komerza.com/api-reference/endpoint/tickets/add
PUT /stores/{storeId}/tickets/{ticketId}
Adds a response/reply to the given ticket.
Requires the `stores.tickets.update` permission.
# Close Ticket
Source: https://docs.komerza.com/api-reference/endpoint/tickets/delete
DELETE /stores/{storeId}/tickets/{ticketId}
Closes the given ticket.
Requires the `stores.tickets.delete` permission.
**Closed and Resolved are final states.** Neither you nor the customer can
reopen a closed ticket - they have to open a new one, losing the thread's
context. Reply instead if the conversation is still live.
# Get Ticket
Source: https://docs.komerza.com/api-reference/endpoint/tickets/get
GET /stores/{storeId}/tickets/{ticketId}
Gets a ticket by the ID in the given store and returns it.
Requires the `stores.tickets.view` permission.
# Get Tickets
Source: https://docs.komerza.com/api-reference/endpoint/tickets/get-list
GET /stores/{storeId}/tickets
Gets the tickets in the store.
Requires the `stores.tickets.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| --------------- | -------- | -------------------------------------------------------------------------------- |
| `subject` | string | Search ticket subject (case-insensitive contains) |
| `status` | integer | Filter by ticket status. See [Ticket Status](/api-reference/enums#ticket-status) |
| `customerEmail` | string | Filter by customer email (case-insensitive contains) |
| `dateFrom` | datetime | Filter tickets created on or after this date |
| `dateTo` | datetime | Filter tickets created on or before this date |
**Example:**
```bash theme={null}
# Open tickets from last 7 days
GET /stores/{storeId}/tickets?filters=status==0,dateFrom>=2025-11-20
# Tickets by customer email
GET /stores/{storeId}/tickets?filters=customerEmail@=*user@example.com
```
# Add License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/add-items
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/items
Appends delivery items to a variant.
Requires the `stores.products.update` permission.
On a variant using **Calculated** stock, the number of keys loaded here *is*
the stock level - adding keys is how you restock, and an Undeliverable order
can be retried with [Deliver](/api-reference/endpoint/orders/deliver) once
stock is back.
# Clear License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/clear-items
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/items/clear
Removes all items from a variant.
Requires the `stores.products.update` permission.
**This wipes every license key on the variant, permanently.** There is no undo
and no way to recover the keys afterwards - keep your own copy before calling
it.
On a variant using Calculated stock, this also drops its stock to zero, so the
product stops selling immediately.
# Create Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/create
POST /stores/{storeId}/products/{productId}/variants
Adds a new variant to the specified product.
Requires the `stores.products.update` permission.
# Delete Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/delete
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}
Removes the specified variant.
Requires the `stores.products.update` permission.
**Deletes the variant's license keys, custom fields and volume discounts with
it,** and cannot be undone. Any unsold keys loaded on it are gone.
# Remove File from Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/delete-file
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/file/{fileId}
Deletes the delivery file and its link.
Requires the `stores.products.update` permission.
# Delete License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/delete-items
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/items
Deletes specified items from a variant.
Requires the `stores.products.update` permission.
**Removed keys are gone for good** - they leave your inventory and cannot be
restored. On Calculated stock, removing keys lowers the sellable stock
straight away.
# Create Volume Discount
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/create
POST /stores/{storeId}/products/{productId}/variants/{variantId}/discounts
Adds a new volume discount tier to the specified variant. Each tier defines a minimum quantity
and the discount to apply when that quantity is reached.
Requires the `stores.products.update` permission.
# Delete Volume Discount
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/delete
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/discounts/{discountId}
Removes the specified volume discount tier from the variant.
Requires the `stores.products.update` permission.
# Get Volume Discounts
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/get
GET /stores/{storeId}/products/{productId}/variants/{variantId}/discounts
Retrieves all volume discount tiers configured for the specified variant, ordered by quantity threshold.
Requires the `stores.products.update` permission.
# Replace Volume Discounts
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/replace
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/discounts
Replaces all existing volume discount tiers for the variant with a new set.
This is useful for bulk updates where you want to set all tiers at once.
Requires the `stores.products.update` permission.
# Update Volume Discount
Source: https://docs.komerza.com/api-reference/endpoint/variants/discounts/update
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/discounts/{discountId}
Updates the quantity threshold, discount type, or discount value of an existing volume discount tier.
Requires the `stores.products.update` permission.
# Duplicate Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/duplicate
POST /stores/{storeId}/products/{productId}/variants/{variantId}/duplicate
Creates a copy of an existing variant in the same product.
Requires the `stores.products.update` permission.
# Create Field
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/create
POST /stores/{storeId}/products/{productId}/variants/{variantId}/fields
Adds a new custom field to the specified variant. Options for select/radio fields are managed separately.
Requires the `stores.products.update` permission.
# Delete Field
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/delete
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}
Removes the specified custom field and all its options from the variant.
Requires the `stores.products.update` permission.
# Create Field Option
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/options/create
POST /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}/options
Adds a selectable option under the given custom field.
Requires the `stores.products.update` permission.
# Delete Field Option
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/options/delete
DELETE /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}/options/{optionId}
Removes the specified option from its parent custom field.
Requires the `stores.products.update` permission.
# Update Field Option
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/options/update
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}/options/{optionId}
Updates the label, value, or display order of an existing option.
Requires the `stores.products.update` permission.
# Update Field
Source: https://docs.komerza.com/api-reference/endpoint/variants/fields/update
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/fields/{fieldId}
Updates the label, placeholder, type, requirement, or hint of an existing custom field.
Requires the `stores.products.update` permission.
# Get Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/get
GET /stores/{storeId}/products/{productId}/variants/{variantId}
Retrieves a single variant by ID.
Requires the `stores.products.view` permission.
# Get License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/get-items
GET /stores/{storeId}/products/{productId}/variants/{variantId}/items/{page}
Retrieves files/items delivered by a variant in pages.
Requires the `stores.products.view` permission.
# Order Variants
Source: https://docs.komerza.com/api-reference/endpoint/variants/order
PATCH /stores/{storeId}/products/{productId}/variants/order
Updates the display order of variants.
Requires the `stores.products.update` permission.
# Order Variant Images
Source: https://docs.komerza.com/api-reference/endpoint/variants/order-images
PATCH /stores/{storeId}/products/{productId}/variants/{variantId}/images/order
Sets the display order of images.
Requires the `stores.products.update` permission.
# Search License Keys
Source: https://docs.komerza.com/api-reference/endpoint/variants/search-items
GET /stores/{storeId}/products/{productId}/variants/{variantId}/items/search/{query}
Searches for license keys in the specified store using fuzzy matching.
Results are ordered by relevance score, with exact matches appearing first,
followed by prefix matches, substring matches, and then similar keys.
Requires the `stores.products.view` permission.
# Update Variant
Source: https://docs.komerza.com/api-reference/endpoint/variants/update
PUT /stores/{storeId}/products/{productId}/variants/{variantId}
Modifies a variant’s label, SKU, price, etc.
Requires the `stores.products.update` permission.
# Update Delivery Settings
Source: https://docs.komerza.com/api-reference/endpoint/variants/update-delivery
PUT /stores/{storeId}/products/{productId}/variants/{variantId}/delivery
Configures how the variant is delivered (e.g., file, custom script).
Requires the `stores.products.update` permission.
# Create Application
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-application
POST /stores/{storeId}/reseller/applications
For a merchant hosting their own application form. Collect whatever fields suit your site, then
post them here with the applicant's email — answers may reference the questions configured on
this store, carry a `label` of your own, or mix the two.
The email is taken as given and not verified: the form is yours, on your domain, and you
review what it produces. The applicant proves the address the first time they sign in to
deposit or reach their reseller panel.
The application lands in the same review queue as every other, at
`GET /stores/{storeId}/reseller/application/submissions`.
Requires the `stores.resellers.update` permission.
# Create Basket Discount
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-basket-tier
POST /stores/{storeId}/reseller/basket-tiers
Measured at list price before anything comes off, so qualifying for a discount cannot lower a
basket's value and disqualify it again. Where several apply, the largest wins.
Requires the `stores.resellers.update` permission.
# Create Bundle
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-bundle
POST /stores/{storeId}/reseller/bundles
A bundle is a basket you have composed and priced as one thing. Set a discount to advertise a
figure, or leave it off and let your basket-size discounts decide.
Every product in it has to be in your reseller catalogue already, and it can never cost a
reseller more than buying the same lines separately would.
Requires the `stores.resellers.update` permission.
# Create Question
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/create-question
POST /stores/{storeId}/reseller/application/questions
Questions can be added, reworded and reordered at any time, including while applications are
waiting: an answer records the wording it was given against, so editing the form never changes
what someone already in the queue appears to have been asked.
Requires the `stores.resellers.update` permission.
# Delete Basket Discount
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-basket-tier
DELETE /stores/{storeId}/reseller/basket-tiers/{basketTierId}
Requires the `stores.resellers.update` permission.
# Delete Bundle
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-bundle
DELETE /stores/{storeId}/reseller/bundles/{bundleId}
Takes it off sale. What resellers already bought through it, and what they paid, stays on record.
Requires the `stores.resellers.update` permission.
# Delete Invitation
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-invite
DELETE /stores/{storeId}/reseller/invites/{inviteId}
Requires the `stores.resellers.update` permission.
# Delete Question
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/delete-question
DELETE /stores/{storeId}/reseller/application/questions/{questionId}
Archived rather than deleted. Every answer ever given to it still points at it, and tidying the
form should not orphan the applications that answered it.
Requires the `stores.resellers.update` permission.
# Get Applications
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-applications
GET /stores/{storeId}/reseller/application/submissions
The review queue: who has applied, what they answered, and what they have deposited since. The
deposit is summed from the balance ledger rather than stored, so it always matches the money
that actually arrived.
Requires the `stores.resellers.view` permission.
# Get Basket Discounts
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-basket-tiers
GET /stores/{storeId}/reseller/basket-tiers
Volume terms measured on what a whole basket comes to rather than on how many of one product it
holds, so a reseller taking a spread of your catalogue is credited for the whole commitment.
Requires the `stores.resellers.view` permission.
# Get Bundles
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-bundles
GET /stores/{storeId}/reseller/bundles
Each comes back priced the way your resellers will see it: what it contains, what that comes to
at list price, the discount, and what they would pay. A bundle whose products have left your
reseller catalogue comes back unavailable, with the reason, rather than disappearing.
Requires the `stores.resellers.view` permission.
# Get Leaderboard
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-leaderboard
GET /stores/{storeId}/reseller/leaderboard
Your resellers ranked by what they spent with you over a period, biggest first. Built for
publishing: every reseller is identified by the store they sell from — its name, description,
storefront and branding, all of which is public already — so nothing here has to be masked
before it goes on a page.
`from` is inclusive and `to` exclusive. Omit them for the last thirty days.
Spend is net of refunds, so a reseller who was refunded more than they spent in the period
does not appear.
A reseller who sells outside Komerza still ranks, with no store attached — leaving them out
would produce a leaderboard whose total disagreed with your own revenue.
Requires the `stores.resellers.view` permission.
# Get Questions
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/get-questions
GET /stores/{storeId}/reseller/application/questions
The store's reseller application form, in the order applicants are asked. Retired questions are
left out — they exist only so answers already given to them stay readable.
Requires the `stores.resellers.view` permission.
# Update Basket Discount
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-basket-tier
PUT /stores/{storeId}/reseller/basket-tiers/{basketTierId}
Requires the `stores.resellers.update` permission.
# Update Bundle
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-bundle
PUT /stores/{storeId}/reseller/bundles/{bundleId}
Replaces the bundle wholesale, lines and all. Anyone who already bought it keeps what they
bought at the price they paid.
Requires the `stores.resellers.update` permission.
# Update Question
Source: https://docs.komerza.com/api-reference/endpoint/reseller-programme/update-question
PUT /stores/{storeId}/reseller/application/questions/{questionId}
Requires the `stores.resellers.update` permission.
# Accept Invitation
Source: https://docs.komerza.com/api-reference/endpoint/reselling/accept-invite
POST /stores/{storeId}/reselling/invites/accept
Redeems an invite on behalf of the store in the route, which `PlatformAuthorize` has already
proven you own. The relationship is approved outright — the supplier decided that when they sent
it — and a draft product is created for everything the invite named, so you land on a dashboard
with stock to sell rather than a setup guide.
Only redeemable by an account on the address the invite was sent to.
Requires the `stores.reselling.update` permission.
# Add Funds
Source: https://docs.komerza.com/api-reference/endpoint/reselling/add-funds
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/topup
Creates an order on the supplier's store that credits your balance with them once paid. Drawing
stock and buying it in bulk both spend that balance, so this is what makes either possible.
The account credited is the one this store's relationship is held under, not whoever is
signed in — a staff member topping up must not end up crediting themselves.
Requires the `stores.reselling.update` permission.
# Apply to Supplier
Source: https://docs.komerza.com/api-reference/endpoint/reselling/apply
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}
Asks to resell a store's stock. A customer account on that store is created from the caller's
platform email, so there is nothing to sign up for separately. Stores that enrol openly approve
immediately; stores that take applications leave this pending.
Requires the `stores.reselling.update` permission.
# Buy Basket
Source: https://docs.komerza.com/api-reference/endpoint/reselling/buy-basket
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/basket
The basket is priced once and each line is then bought at its share of that price, landing on
your own products as ordinary stock exactly as a single-variant purchase would. Short deliveries
are refunded the same way.
Supply an idempotency key. Lines carry it with their own index appended, so a retried basket
replays line for line rather than buying twice.
Requires the `stores.reselling.update` permission.
# Buy Stock
Source: https://docs.komerza.com/api-reference/endpoint/reselling/buy-stock
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/stock
Buys units up front, at whatever volume price the supplier offers for that quantity, and puts
them on your own product as ordinary stock. Nothing is held or pending: the units are yours, they
sell through your normal delivery, and nothing the supplier does afterwards takes them away.
If the supplier delivers fewer than were bought, the difference is refunded to your balance
rather than quietly kept.
Requires the `stores.reselling.update` permission.
# Create Listing
Source: https://docs.komerza.com/api-reference/endpoint/reselling/create-listing
PUT /stores/{storeId}/reselling/suppliers/{supplierStoreId}/listings/{sourceVariantId}
Points one of this store's variants at a supplier's variant. Selling it then draws a unit from
the supplier automatically — no delivery configuration of any kind is needed on this side.
Requires the `stores.reselling.update` permission.
# Delete Listing
Source: https://docs.komerza.com/api-reference/endpoint/reselling/delete-listing
DELETE /stores/{storeId}/reselling/suppliers/{supplierStoreId}/listings/{sourceVariantId}
The store's own product keeps existing and keeps selling; it just no longer draws from the
supplier, which for a purely resold product means it can no longer be delivered. Worth
unpublishing it at the same time.
Requires the `stores.reselling.update` permission.
# Find Supplier
Source: https://docs.komerza.com/api-reference/endpoint/reselling/find-supplier
GET /stores/{storeId}/reselling/suppliers/lookup
There is no supplier directory, so this is how you reach a store you already know of: paste its
address.
Only stores currently taking applications are found. One running an invite-only programme, or
none at all, comes back as not found — which store has a reseller programme is not something
this should let anyone map out.
Requires the `stores.reselling.view` permission.
# Get Bundles
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-bundles
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/bundles
Baskets the supplier composed and priced as one thing, each quoted for this store: what is in
it, what that comes to at their list prices, the discount, and what you would pay. Bundles aimed
at other resellers are not listed, and cannot be bought by guessing an id.
Requires the `stores.reselling.view` permission.
# Get Catalogue
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-catalogue
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/catalogue
Priced as it will actually cost this store, with whichever of its own products is already
sourced from each.
Requires the `stores.reselling.view` permission.
# Get Draws
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-draws
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/draws
One entry per draw, newest first, each quoting the supplier's own order id so it can be chased
upstream.
Requires the `stores.reselling.view` permission.
# Get Linkable Accounts
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-linkable
GET /stores/{storeId}/reselling/linkable
Relationships created by signing up on a supplier's own storefront, before this store existed or
before you thought to use it. They have no store attached, so none of the reselling here applies
to them and the supplier's panel keeps pointing you back at a dashboard that cannot see them.
Matched on your confirmed email address.
Requires the `stores.reselling.view` permission.
# Get Payment Methods
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-payment-methods
GET /stores/{storeId}/reselling/suppliers/{supplierStoreId}/payment-methods
The supplier's own payment methods, since topping up is paid to them rather than to Komerza.
Only readable by a store that actually holds a relationship with them — a merchant's enabled
gateways are not public information.
Requires the `stores.reselling.view` permission.
# Get Resold Products
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-products
GET /stores/{storeId}/reselling/products
Every product of this store drawn from a supplier, with its cost, margin, and whether it would
currently sell. A resold product can stop being deliverable without its owner touching anything —
the supplier withdraws it, revokes them, raises the price past their ceiling, or runs out — so
each carries the reason it is blocked, if it is.
Requires the `stores.reselling.view` permission.
# Get Suppliers
Source: https://docs.komerza.com/api-reference/endpoint/reselling/get-suppliers
GET /stores/{storeId}/reselling/suppliers
Every store this one draws stock from, with what it can currently spend at each.
Requires the `stores.reselling.view` permission.
# Link Account
Source: https://docs.komerza.com/api-reference/endpoint/reselling/link-account
POST /stores/{storeId}/reselling/linkable/{resellerId}
Attaches an off-platform reseller relationship to this store. From then on its draws are served
in-process — stock lands on your own variants, there is no delivery URL to hold, and the balance
already held with that supplier comes across untouched.
Requires the `stores.reselling.update` permission.
# Quote Basket
Source: https://docs.komerza.com/api-reference/endpoint/reselling/quote-basket
POST /stores/{storeId}/reselling/suppliers/{supplierStoreId}/basket/quote
Give it a bundle id, or lines of your own, and it comes back with the same arithmetic the
purchase will use — including what those lines would have cost bought separately, so a discount
you already have is not counted twice in what you tell yourself you saved.
Requires the `stores.reselling.view` permission.
# Create Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/create
POST /webhooks
Creates a webhook that POSTs the events you subscribe to, for the stores you choose, as signed
JSON. The signing secret is returned once here, so save it to verify the X-Signature header.
Requires the `user.webhooks.create` permission.
**The signing secret is returned once, here, and never again.** There is no
endpoint or dashboard screen that will show it later. Store it when you
receive it; if you lose it, your only option is to delete the webhook and
create a new one.
You can hold up to **25 webhooks per user**, each scoped to one or more of
your stores. URLs must be HTTP or HTTPS, and any that resolve to private or
reserved addresses are rejected.
# Delete Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/delete
DELETE /webhooks/{webhookId}
Removes the webhook and all associated logs from the user account.
Requires the `user.webhooks.delete` permission.
**Deleting a webhook deletes all of its execution logs**, so any delivery
history you would need for debugging goes with it. Export anything you want to
keep first.
# Get Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/get
GET /webhooks/{webhookId}
Retrieves the webhook if it belongs to the currently authenticated user.
Requires the `user.webhooks.view` permission.
# Get Webhooks
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/get-list
GET /webhooks
Returns a list of all account-wide webhooks configured by the user.
Requires the `user.webhooks.view` permission.
# Get Webhook Logs
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/get-logs
GET /webhooks/{webhookId}/logs
Returns a webhook's delivery history: one entry per attempt, with the response code and, on
failures, the reason.
Supports filtering by response code and success/failure status via query parameters.
Requires the `user.webhooks.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| -------------- | ------- | --------------------------------------------------------------------------------- |
| `success` | boolean | Whether the delivery succeeded (a 2xx response) |
| `responseCode` | integer | The HTTP status your endpoint returned. `-1` means paused after repeated failures |
**Example:**
```bash theme={null}
# Failed deliveries only
GET /webhooks/{webhookId}/logs?filters=success==false
# Deliveries your endpoint rejected as unauthorised
GET /webhooks/{webhookId}/logs?filters=responseCode==401
```
# Resend Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/resend
POST /webhooks/{webhookId}/logs/{executionLogId}/resend
Re-queues the Hangfire job for a particular webhook execution log. If the job no longer exists or the user
does not own the log, it fails. This is a single execution log re-send.
Requires the `user.webhooks.update` permission.
# Search Webhook Logs
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/search-logs
GET /webhooks/{webhookId}/logs/search/{query}
Searches logs by log ID, order ID (if the query is a valid GUID), or by URL fragment.
All pagination and date/status filters from the listing endpoint are also supported.
Requires the `user.webhooks.view` permission.
## Filterable Fields
This endpoint supports filtering and sorting. See the [Filtering & Sorting Guide](/api-reference/filtering) for syntax details.
| Field | Type | Description |
| -------------- | ------- | --------------------------------------------------------------------------------- |
| `success` | boolean | Whether the delivery succeeded (a 2xx response) |
| `responseCode` | integer | The HTTP status your endpoint returned. `-1` means paused after repeated failures |
**Example:**
```bash theme={null}
# Failed deliveries only
GET /webhooks/{webhookId}/logs?filters=success==false
# Deliveries your endpoint rejected as unauthorised
GET /webhooks/{webhookId}/logs?filters=responseCode==401
```
# Update Webhook
Source: https://docs.komerza.com/api-reference/endpoint/webhooks/update
PUT /webhooks/{webhookId}
Overwrites the URL, events, and store IDs.
Requires the `user.webhooks.update` permission.