Create a new one-time purchase product with multi-currency pricing.
POST /v1/actions/onetime-product/create-product
Authentication: API Key
Request Body
| Field | Type | Required | Description |
|---|
storeId | string | Yes | Store ID (Short ID format STO_xxx) |
name | string | Yes | Product name (max 64 chars) |
description | string | null | No | Product description (supports Markdown; pass null or "" to clear) |
prices | object | Yes | Multi-currency pricing map (see below) |
media | array | No | Product images/videos (see below) |
successUrl | string | null | No | Redirect URL after successful purchase (max 512 chars, must be a valid http(s) URL; pass null or "" to clear) |
metadata | object | No | Custom key-value data (max 50 keys) |
Prices are a map of ISO 4217 currency codes to price configuration objects. At least one currency is required.
{
"USD": {
"amount": "29.00",
"taxIncluded": false,
"taxCategory": "saas"
},
"EUR": {
"amount": "27.00",
"taxIncluded": true,
"taxCategory": "saas"
}
}
| Field | Type | Description |
|---|
amount | string | Price as a display format string (e.g., “29.00” = $29.00) |
taxIncluded | boolean | Whether the amount already includes tax |
taxCategory | string | Tax category for tax calculation |
Supported taxCategory values:
| Value | Description |
|---|
digital_goods | General digital goods |
saas | Software as a Service |
software | Downloadable software |
ebook | Electronic books |
online_course | Online courses and education |
consulting | Consulting services |
professional_service | Professional services |
{
"type": "image",
"url": "https://example.com/product.png",
"alt": "Product screenshot",
"thumbnail": "https://example.com/product-thumb.png"
}
| Field | Type | Description |
|---|
type | string | image or video |
url | string | Media URL |
alt | string | Alt text for accessibility |
thumbnail | string | Thumbnail URL (optional for images, recommended for videos) |
Example Request
import { WaffoPancake, TaxCategory } from "@waffo/pancake-ts";
const client = new WaffoPancake({
merchantId: process.env.WAFFO_MERCHANT_ID!,
privateKey: process.env.WAFFO_PRIVATE_KEY!,
});
const { product } = await client.onetimeProducts.create({
storeId: "STO_2aUyqjCzEIiEcYMKj7TZtw",
name: "Premium Template Pack",
description: "50 premium design templates for your next project.",
prices: {
USD: { amount: "49.00", taxIncluded: false, taxCategory: TaxCategory.DigitalGoods },
EUR: { amount: "45.00", taxIncluded: true, taxCategory: TaxCategory.DigitalGoods },
},
media: [
{ type: "image", url: "https://example.com/templates-preview.png", alt: "Template preview" },
],
successUrl: "https://example.com/thank-you",
metadata: { category: "design", fileCount: "50" },
});
Success Response (200)
{
"data": {
"product": {
"id": "PROD_3kF9mNpQrStUvWxYz1A2bC",
"storeId": "STO_2aUyqjCzEIiEcYMKj7TZtw",
"name": "Premium Template Pack",
"description": "50 premium design templates for your next project.",
"prices": {
"USD": { "amount": "49.00", "taxCategory": "digital_goods" },
"EUR": { "amount": "45.00", "taxCategory": "digital_goods" }
},
"media": [
{ "type": "image", "url": "https://example.com/templates-preview.png", "alt": "Template preview" }
],
"successUrl": "https://example.com/thank-you",
"metadata": { "category": "design", "fileCount": "50" },
"status": "active",
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-01-15T10:30:00.000Z"
}
}
}
Response Fields
The response is wrapped in data.product. The product object is a flattened detail view combining product and version fields.
| Field | Type | Description |
|---|
id | string | Product ID (PROD_xxx) |
storeId | string | Store ID (STO_xxx) |
name | string | Product name (from current version) |
description | string | null | Product description (from current version) |
prices | object | Multi-currency pricing map (see below) |
media | array | Media items (from current version) |
successUrl | string | null | Success redirect URL (from current version) |
metadata | object | Custom metadata (from current version) |
status | string | Status in the current environment: active or inactive |
createdAt | string | Product creation timestamp (ISO 8601) |
updatedAt | string | Product last update timestamp (ISO 8601) |
Response price object (per currency):
| Field | Type | Description |
|---|
amount | string | Price as a display format string (e.g., “49.00”) |
taxCategory | string | Tax category for tax calculation |
The status field reflects the environment you are operating in. When created in the test environment (default), status is active. When created in the production environment, the production status is returned.
Errors
Retry policy: Never retry 4xx — fix the request and resubmit. Retry 5xx with exponential backoff (start 5s, max 3 attempts).
| Status | errors[0].message | What it means | Recommended handling |
|---|
| 400 | Missing required field: storeId | storeId not provided | Fix the body, resubmit |
| 400 | Invalid ID format | The provided Short ID could not be decoded | Fix the storeId format, resubmit |
| 400 | Missing required field: name | name not provided | Fix the body, resubmit |
| 400 | Prices must have at least one currency | prices is missing or an empty object | Provide at least one currency entry, resubmit |
| 400 | Invalid currency code | Currency code must be exactly 3 uppercase letters (ISO 4217, e.g. USD, EUR, JPY) | Use a valid ISO 4217 code, resubmit |
| 400 | Invalid amount | Amount must be a positive number string (e.g. "9.99", "100") | Use a positive number string, resubmit |
| 404 | Store not found | Store does not exist or is not accessible | Verify the storeId belongs to your merchant account |
| 500 | Internal server error | Unexpected server-side failure | Retry with exponential backoff (start 5s, max 3 attempts) |