woocommerce
Skill708 starsChanged 5 months agoMITView on GitHub
WooCommerce REST API - products, orders, customers, webhooks
- Reads credentials
- Installs packages
6 other projects use a version of this.
Adopted by
Most recently taken up by
- turkeryildirim/ai-skillstheir own version, first seen Nov 1, 2026
- Mindrally/skillstheir own version, first seen Oct 8, 2026
- conjure-3301/skillstheir own version, first seen Aug 17, 2026
- apideck-libraries/api-skillstheir own version, first seen Aug 13, 2026
- alinaqi/maggyword for word, first seen Jun 12, 2026
- iwritec0de/wp-devtheir own version, first seen Apr 19, 2026
Other versions6 projects use a version of this.
Word for word · 1
- alinaqi/maggy★ 711100% same
Edited · 0
None found.
Wrote their own version · 5
- Mindrally/skills★ 275same name
- apideck-libraries/api-skills★ 4same name
- turkeryildirim/ai-skills★ 4same name
- iwritec0de/wp-dev★ 1same name
- conjure-3301/skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository75 other files it gives its agents.
- templates/AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- templates/CLAUDE.mdCLAUDE.md
- aeo-optimizationSkill
- agentic-developmentSkill
- agent-teamsSkill
- ai-modelsSkill
- android-javaSkill
- android-kotlinSkill
- autonomous-testingSkill
- aws-auroraSkill
- aws-dynamodbSkill
- azure-cosmosdbSkill
- baseSkill
- build-in-publicSkill
- cloudflare-d1Skill
- code-deduplicationSkill
- code-graphSkill
- code-reviewSkill
- codex-reviewSkill
- commit-hygieneSkill
- context-shuntSkill
- council-reviewSkill
- cpg-analysisSkill
- credentialsSkill
- cross-agent-delegationSkill
- database-schemaSkill
- dataforseoSkill
- demo-videoSkill
- existing-repoSkill
- external-model-delegationSkill
- firebaseSkill
- flutterSkill
- gemini-reviewSkill
- icpgSkill
- iterative-developmentSkill
- klaviyoSkill
- llm-patternsSkill
- maggySkill
- medusaSkill
- mnemosSkill
- model-routingSkill
- ms-teams-appsSkill
- nodejs-backendSkill
- playwright-testingSkill
- polyphonySkill
- posthog-analyticsSkill
- project-toolingSkill
- pwa-developmentSkill
- pythonSkill
- react-nativeSkill
- react-webSkill
- reddit-adsSkill
- reddit-apiSkill
- security-auditSkill
- securitySkill
- session-managementSkill
- shopify-appsSkill
- site-architectureSkill
- supabase-nextjsSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
4 rules. 0 are common, 4 are distinctive, and 0 are very specific to this project.
0
Common
Most of the 4 edited versions we read keep these.
4
Distinctive
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 776
HTTP in production: - Always use HTTPS
- Distinctive: line 778
Large single requests: - Use pagination for bulk operations
- Distinctive: line 779
Storing keys in code: - Use environment variables
- Distinctive: line 780
Skipping webhook verification: - Always verify signatures
“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
WooCommerce Development Skill
For integrating with WooCommerce stores via REST API - products, orders, customers, webhooks, and custom extensions.
Sources: WooCommerce REST API | Developer Docs
Prerequisites
Store Requirements
# WooCommerce store must have:
# 1. WordPress with WooCommerce plugin installed
# 2. HTTPS enabled (required for API auth)
# 3. Permalinks set to anything except "Plain"
# WordPress Admin → Settings → Permalinks → Post name (recommended)Generate API Keys
- Go to WooCommerce → Settings → Advanced → REST API
- Click Add key
- Set Description, User (admin), and Permissions (Read/Write)
- Click Generate API key
- Copy Consumer Key and Consumer Secret (shown only once)
API Basics
Base URL
https://your-store.com/wp-json/wc/v3/Authentication
// Node.js - Basic Auth (recommended)
const WooCommerceRestApi = require("@woocommerce/woocommerce-rest-api").default;
const api = new WooCommerceRestApi({
url: "https://your-store.com",
consumerKey: process.env.WC_CONSUMER_KEY,
consumerSecret: process.env.WC_CONSUMER_SECRET,
version: "wc/v3"
});# Python
from woocommerce import API
wcapi = API(
url="https://your-store.com",
consumer_key=os.environ["WC_CONSUMER_KEY"],
consumer_secret=os.environ["WC_CONSUMER_SECRET"],
version="wc/v3"
)Query String Auth (Fallback)
# Only use if Basic Auth fails (some hosting configurations)
curl https://your-store.com/wp-json/wc/v3/products \
?consumer_key=ck_xxx&consumer_secret=cs_xxxInstallation
Node.js
npm install @woocommerce/woocommerce-rest-api// lib/woocommerce.ts
import WooCommerceRestApi from "@woocommerce/woocommerce-rest-api";
const api = new WooCommerceRestApi({
url: process.env.WC_STORE_URL!,
consumerKey: process.env.WC_CONSUMER_KEY!,
consumerSecret: process.env.WC_CONSUMER_SECRET!,
version: "wc/v3",
queryStringAuth: false, // Set true for HTTP (dev only)
});
export default api;Python
pip install woocommerce# lib/woocommerce.py
import os
from woocommerce import API
wcapi = API(
url=os.environ["WC_STORE_URL"],
consumer_key=os.environ["WC_CONSUMER_KEY"],
consumer_secret=os.environ["WC_CONSUMER_SECRET"],
version="wc/v3",
timeout=30
)Products
List Products
// Node.js
async function getProducts(page = 1, perPage = 20) {
const response = await api.get("products", {
page,
per_page: perPage,
status: "publish",
});
return response.data;
}
// With filters
async function searchProducts(search: string, category?: number) {
const response = await api.get("products", {
search,
category: category || undefined,
orderby: "popularity",
order: "desc",
});
return response.data;
}# Python
def get_products(page=1, per_page=20):
response = wcapi.get("products", params={
"page": page,
"per_page": per_page,
"status": "publish"
})
return response.json()Get Single Product
async function getProduct(productId: number) {
const response = await api.get(`products/${productId}`);
return response.data;
}Create Product
async function createProduct(data: ProductInput) {
const response = await api.post("products", {
name: data.name,
type: "simple", // simple, variable, grouped, external
regular_price: data.price.toString(),
description: data.description,
short_description: data.shortDescription,
categories: data.categoryIds.map(id => ({ id })),
images: data.images.map(url => ({ src: url })),
manage_stock: true,
stock_quantity: data.stockQuantity,
status: "publish",
});
return response.data;
}Update Product
async function updateProduct(productId: number, data: Partial<ProductInput>) {
const response = await api.put(`products/${productId}`, data);
return response.data;
}
// Update stock only
async function updateStock(productId: number, quantity: number) {
const response = await api.put(`products/${productId}`, {
stock_quantity: quantity,
});
return response.data;
}Delete Product
async function deleteProduct(productId: number, force = false) {
// force: true = permanent delete, false = move to trash
const response = await api.delete(`products/${productId}`, {
force,
});
return response.data;
}Variable Products
// Create variable product
async function createVariableProduct(data: VariableProductInput) {
// 1. Create product with type "variable"
const product = await api.post("products", {
name: data.name,
type: "variable",
attributes: [
{
name: "Size",
visible: true,
variation: true,
options: ["Small", "Medium", "Large"],
},
{
name: "Color",
visible: true,
variation: true,
options: ["Red", "Blue"],
},
],
});
// 2. Create variations
for (const variant of data.variants) {
await api.post(`products/${product.data.id}/variations`, {
regular_price: variant.price.toString(),
stock_quantity: variant.stock,
attributes: [
{ name: "Size", option: variant.size },
{ name: "Color", option: variant.color },
],
});
}
return product.data;
}
// Get variations
async function getVariations(productId: number) {
const response = await api.get(`products/${productId}/variations`);
return response.data;
}Orders
List Orders
async function getOrders(params: OrderQueryParams = {}) {
const response = await api.get("orders", {
page: params.page || 1,
per_page: params.perPage || 20,
status: params.status || "any", // pending, processing, completed, etc.
after: params.after, // ISO date string
before: params.before,
});
return response.data;
}
// Get recent orders
async function getRecentOrders(days = 7) {
const after = new Date();
after.setDate(after.getDate() - days);
const response = await api.get("orders", {
after: after.toISOString(),
orderby: "date",
order: "desc",
});
return response.data;
}Get Single Order
async function getOrder(orderId: number) {
const response = await api.get(`orders/${orderId}`);
return response.data;
}Create Order
async function createOrder(data: OrderInput) {
const response = await api.post("orders", {
payment_method: "stripe",
payment_method_title: "Credit Card",
set_paid: false,
billing: {
first_name: data.customer.firstName,
last_name: data.customer.lastName,
email: data.customer.email,
phone: data.customer.phone,
address_1: data.billing.address1,
city: data.billing.city,
state: data.billing.state,
postcode: data.billing.postcode,
country: data.billing.country,
},
shipping: {
first_name: data.customer.firstName,
last_name: data.customer.lastName,
address_1: data.shipping.address1,
city: data.shipping.city,
state: data.shipping.state,
postcode: data.shipping.postcode,
country: data.shipping.country,
},
line_items: data.items.map(item => ({
product_id: item.productId,
variation_id: item.variationId,
quantity: item.quantity,
})),
shipping_lines: [
{
method_id: "flat_rate",
method_title: "Flat Rate",
total: data.shippingCost.toString(),
},
],
});
return response.data;
}Update Order Status
async function updateOrderStatus(orderId: number, status: OrderStatus) {
const response = await api.put(`orders/${orderId}`, {
status, // pending, processing, on-hold, completed, cancelled, refunded, failed
});
return response.data;
}
// Add order note
async function addOrderNote(orderId: number, note: string, customerNote = false) {
const response = await api.post(`orders/${orderId}/notes`, {
note,
customer_note: customerNote, // true = visible to customer
});
return response.data;
}Order Statuses
| Status | Description |
pending | Awaiting payment |
processing | Payment received, awaiting fulfillment |
on-hold | Awaiting action (stock, payment confirmation) |
completed | Order fulfilled |
cancelled | Cancelled by admin or customer |
refunded | Refunded |
failed | Payment failed |
Customers
List Customers
async function getCustomers(params: CustomerQueryParams = {}) {
const response = await api.get("customers", {
page: params.page || 1,
per_page: params.perPage || 20,
role: "customer",
orderby: "registered_date",
order: "desc",
});
return response.data;
}
// Search customers
async function searchCustomers(email: string) {
const response = await api.get("customers", {
email,
});
return response.data;
}Create Customer
async function createCustomer(data: CustomerInput) {
const response = await api.post("customers", {
email: data.email,
first_name: data.firstName,
last_name: data.lastName,
username: data.email.split("@")[0],
billing: {
first_name: data.firstName,
last_name: data.lastName,
email: data.email,
phone: data.phone,
address_1: data.address1,
city: data.city,
state: data.state,
postcode: data.postcode,
country: data.country,
},
shipping: {
// Same as billing or different
},
});
return response.data;
}Update Customer
async function updateCustomer(customerId: number, data: Partial<CustomerInput>) {
const response = await api.put(`customers/${customerId}`, data);
return response.data;
}Webhooks
Create Webhook
async function createWebhook(topic: string, deliveryUrl: string) {
const response = await api.post("webhooks", {
name: `Webhook for ${topic}`,
topic, // order.created, order.updated, product.created, etc.
delivery_url: deliveryUrl,
status: "active",
secret: process.env.WC_WEBHOOK_SECRET,
});
return response.data;
}Webhook Topics
| Topic | Trigger |
order.created | New order placed |
order.updated | Order status/details changed |
order.deleted | Order deleted |
product.created | New product created |
product.updated | Product updated |
product.deleted | Product deleted |
customer.created | New customer registered |
customer.updated | Customer updated |
coupon.created | New coupon created |
Verify Webhook Signature
// Express.js webhook handler
import crypto from "crypto";
function verifyWooCommerceWebhook(req: Request): boolean {
const signature = req.headers["x-wc-webhook-signature"] as string;
const payload = JSON.stringify(req.body);
const expectedSignature = crypto
.createHmac("sha256", process.env.WC_WEBHOOK_SECRET!)
.update(payload)
.digest("base64");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Route handler
app.post("/webhooks/woocommerce", (req, res) => {
if (!verifyWooCommerceWebhook(req)) {
return res.status(401).json({ error: "Invalid signature" });
}
const topic = req.headers["x-wc-webhook-topic"];
const payload = req.body;
switch (topic) {
case "order.created":
handleNewOrder(payload);
break;
case "order.updated":
handleOrderUpdate(payload);
break;
// ... other topics
}
res.status(200).json({ received: true });
});# Python/Flask webhook handler
import hmac
import hashlib
import base64
@app.route("/webhooks/woocommerce", methods=["POST"])
def woocommerce_webhook():
signature = request.headers.get("X-WC-Webhook-Signature")
payload = request.get_data()
expected = base64.b64encode(
hmac.new(
os.environ["WC_WEBHOOK_SECRET"].encode(),
payload,
hashlib.sha256
).digest()
).decode()
if not hmac.compare_digest(signature, expected):
return {"error": "Invalid signature"}, 401
topic = request.headers.get("X-WC-Webhook-Topic")
data = request.json
if topic == "order.created":
handle_new_order(data)
elif topic == "order.updated":
handle_order_update(data)
return {"received": True}, 200Categories & Tags
List Categories
async function getCategories() {
const response = await api.get("products/categories", {
per_page: 100,
orderby: "name",
});
return response.data;
}
// Create category
async function createCategory(name: string, parentId?: number) {
const response = await api.post("products/categories", {
name,
parent: parentId || 0,
});
return response.data;
}List Tags
async function getTags() {
const response = await api.get("products/tags", {
per_page: 100,
});
return response.data;
}Coupons
Create Coupon
async function createCoupon(data: CouponInput) {
const response = await api.post("coupons", {
code: data.code,
discount_type: data.type, // percent, fixed_cart, fixed_product
amount: data.amount.toString(),
individual_use: true,
exclude_sale_items: false,
minimum_amount: data.minimumAmount?.toString(),
maximum_amount: data.maximumAmount?.toString(),
usage_limit: data.usageLimit,
usage_limit_per_user: 1,
date_expires: data.expiresAt, // ISO date string
});
return response.data;
}Reports
Sales Report
async function getSalesReport(period = "month") {
const response = await api.get("reports/sales", {
period, // day, week, month, year
});
return response.data;
}
// Top sellers
async function getTopSellers(period = "month") {
const response = await api.get("reports/top_sellers", {
period,
});
return response.data;
}Pagination
Handle Large Datasets
async function getAllProducts() {
const allProducts = [];
let page = 1;
const perPage = 100;
while (true) {
const response = await api.get("products", {
page,
per_page: perPage,
});
allProducts.push(...response.data);
// Check headers for total pages
const totalPages = parseInt(response.headers["x-wp-totalpages"]);
if (page >= totalPages) break;
page++;
}
return allProducts;
}Pagination Headers
| Header | Description |
X-WP-Total | Total number of items |
X-WP-TotalPages | Total number of pages |
Error Handling
import WooCommerceRestApi from "@woocommerce/woocommerce-rest-api";
async function safeApiCall<T>(
operation: () => Promise<{ data: T }>
): Promise<T> {
try {
const response = await operation();
return response.data;
} catch (error: any) {
if (error.response) {
// API returned an error
const { status, data } = error.response;
switch (status) {
case 400:
throw new Error(`Bad request: ${data.message}`);
case 401:
throw new Error("Invalid API credentials");
case 404:
throw new Error("Resource not found");
case 429:
// Rate limited - wait and retry
await new Promise(r => setTimeout(r, 5000));
return safeApiCall(operation);
default:
throw new Error(`API error: ${data.message}`);
}
}
throw error;
}
}
// Usage
const products = await safeApiCall(() => api.get("products"));Environment Variables
# .env
WC_STORE_URL=https://your-store.com
WC_CONSUMER_KEY=ck_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
WC_CONSUMER_SECRET=cs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
WC_WEBHOOK_SECRET=your_webhook_secretAdd to credentials.md:
'WC_CONSUMER_KEY': r'ck_[a-f0-9]{40}',
'WC_CONSUMER_SECRET': r'cs_[a-f0-9]{40}',Checklist
Before Integration
- WooCommerce plugin installed and activated
- HTTPS enabled on store
- Permalinks set to non-Plain setting
- API keys generated with appropriate permissions
- Webhook secret configured
Security
- API keys stored in environment variables
- Webhook signatures verified
- HTTPS used for all API calls
- Rate limiting handled
Testing
- Test API connection
- Test product CRUD operations
- Test order creation/updates
- Test webhook delivery
- Test pagination for large datasets
Anti-Patterns
- Plain permalinks - API won't work without pretty permalinks
- HTTP in production - Always use HTTPS
- Ignoring rate limits - WooCommerce may throttle requests
- Large single requests - Use pagination for bulk operations
- Storing keys in code - Use environment variables
- Skipping webhook verification - Always verify signatures
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

