POST /v1/actions/subscription-product/create-product
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
storeId | string | Yes | Store ID (STO_xxx format) |
name | string | Yes | Product name (max 64 chars) |
billingPeriod | string | Yes | weekly, monthly, quarterly, or yearly |
prices | object | Yes | Multi-currency pricing (see Price Format) |
description | string | null | No | Product description (supports Markdown; pass null or "" to clear) |
media | array | No | Product images/videos (see Media Format) |
successUrl | string | null | No | Redirect URL after successful subscription (max 512 chars, must be a valid http(s) URL; pass null or "" to clear) |
metadata | object | No | Custom key-value data. May include trialDays (integer, 1-365) to offer a free trial period |
Example Request
import { WaffoPancake, BillingPeriod, 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.subscriptionProducts.create({
storeId: "STO_2D5F8G3H1K4M6N9P",
name: "Pro Plan",
billingPeriod: BillingPeriod.Monthly,
prices: {
USD: { amount: "29.00", taxIncluded: false, taxCategory: TaxCategory.SaaS },
EUR: { amount: "27.00", taxIncluded: false, taxCategory: TaxCategory.SaaS },
},
description: "Full access to all Pro features.",
successUrl: "https://example.com/welcome",
metadata: { trialDays: 14 },
});
curl -X POST https://api.waffo.ai/v1/actions/subscription-product/create-product \
-H "Content-Type: application/json" \
-H "X-Merchant-Id: MER_7K3M9P2R5T8W1Y4Z" \
-H "X-Timestamp: 2026-03-30T10:00:00Z" \
-H "X-Signature: BASE64_RSA_SIGNATURE" \
-d '{
"storeId": "STO_2D5F8G3H1K4M6N9P",
"name": "Pro Plan",
"billingPeriod": "monthly",
"prices": {
"USD": { "amount": "29.00", "taxIncluded": false, "taxCategory": "saas" },
"EUR": { "amount": "27.00", "taxIncluded": false, "taxCategory": "saas" }
},
"description": "Full access to all Pro features.",
"successUrl": "https://example.com/welcome",
"metadata": { "trialDays": 14 }
}'
import requests
# Assumes you have a sign_request() helper for RSA-SHA256 signing
headers = sign_request("POST", "/v1/actions/subscription-product/create-product", body)
body = {
"storeId": "STO_2D5F8G3H1K4M6N9P",
"name": "Pro Plan",
"billingPeriod": "monthly",
"prices": {
"USD": {"amount": "29.00", "taxIncluded": False, "taxCategory": "saas"},
"EUR": {"amount": "27.00", "taxIncluded": False, "taxCategory": "saas"},
},
"description": "Full access to all Pro features.",
"successUrl": "https://example.com/welcome",
"metadata": {"trialDays": 14},
}
response = requests.post(
"https://api.waffo.ai/v1/actions/subscription-product/create-product",
json=body,
headers=headers,
)
data = response.json()
Success Response (200)
{
"data": {
"product": {
"id": "PROD_3F7H2J5L8N1Q4S6U",
"storeId": "STO_2D5F8G3H1K4M6N9P",
"name": "Pro Plan",
"description": "Full access to all Pro features.",
"billingPeriod": "monthly",
"prices": {
"USD": { "amount": "29.00", "taxCategory": "saas" },
"EUR": { "amount": "27.00", "taxCategory": "saas" }
},
"media": [],
"successUrl": "https://example.com/welcome",
"metadata": { "trialDays": 14 },
"status": "active",
"createdAt": "2026-03-30T10:30:00.000Z",
"updatedAt": "2026-03-30T10:30:00.000Z"
}
}
}
Response Fields
The response is wrapped indata.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) |
billingPeriod | string | Billing period: weekly, monthly, quarterly, or yearly |
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, may include trialDays) |
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) |
| Field | Type | Description |
|---|---|---|
amount | string | Price as a display format string (e.g., “29.00”) |
taxCategory | string | Tax category for tax calculation |
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 header: x-context-merchant-id | The merchant context header was not forwarded | Fix your SDK configuration |
| 400 | Missing or invalid header: x-context-environment | Environment header is missing or not test / prod | Set X-Environment to test or prod |
| 400 | Missing required field: storeId / Missing required field: name / Missing required field: prices (must have at least one currency) | A required body field was omitted | Add the missing field, resubmit |
| 400 | Expected format: STO_xxx, got "X" | storeId is not a valid Short ID | Use a STO_ prefixed Short ID |
| 400 | Invalid or missing billingPeriod | billingPeriod is not one of weekly / monthly / quarterly / yearly | Use one of the four allowed values |
| 400 | Invalid currency code: "X". Must be 3 uppercase letters (e.g., "USD", "EUR", "JPY") | A key in prices is not a valid ISO 4217 code | Use 3 uppercase letters |
| 400 | Invalid amount for X: "Y". Must be a positive number string (e.g., "9.99", "1000") | Amount string couldn’t be parsed | Use a positive decimal string |
| 401 | Unauthorized | Authentication failed | Verify API key, timestamp, and signature |
| 404 | Store not found | The decoded storeId does not exist (foreign key violation) | Verify the store belongs to your merchant account |
| 500 | Internal server error | Transient downstream failure | Retry with exponential backoff (start 5s, max 3 attempts) |