MCPInstall

woocommerce

WooCommerce REST API - products, orders, customers, webhooks

Installs
390
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/alinaqi/maggy --skill woocommerce
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads woocommerce automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

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

  1. Go to WooCommerce → Settings → Advanced → REST API
  2. Click Add key
  3. Set Description, User (admin), and Permissions (Read/Write)
  4. Click Generate API key
  5. 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_xxx

Installation

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

StatusDescription
pendingAwaiting payment
processingPayment received, awaiting fulfillment
on-holdAwaiting action (stock, payment confirmation)
completedOrder fulfilled
cancelledCancelled by admin or customer
refundedRefunded
failedPayment 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

TopicTrigger
order.createdNew order placed
order.updatedOrder status/details changed
order.deletedOrder deleted
product.createdNew product created
product.updatedProduct updated
product.deletedProduct deleted
customer.createdNew customer registered
customer.updatedCustomer updated
coupon.createdNew 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}, 200

Categories & 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

HeaderDescription
X-WP-TotalTotal number of items
X-WP-TotalPagesTotal 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_secret

Add 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

FAQ

What does the woocommerce skill do?

WooCommerce REST API - products, orders, customers, webhooks

How do I install woocommerce?

Run npx skills add https://github.com/alinaqi/maggy --skill woocommerce in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/woocommerce/ (personal) or .claude/skills/woocommerce/ (project).

Which AI agents can use woocommerce?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is woocommerce safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains woocommerce?

It is published by alinaqi in alinaqi/maggy. It has 390 recorded installs.

More from alinaqi/maggy

View all →

pwa-development

alinaqi/maggy

Progressive Web Apps - service workers, caching strategies, offline, Workbox

4.1K installs

playwright-testing

alinaqi/maggy

E2E testing with Playwright - Page Objects, cross-browser, CI/CD

Testing & QA3.3K installs

android-kotlin

alinaqi/maggy

Android Kotlin development with Coroutines, Jetpack Compose, Hilt, and MockK testing

Mobile3.3K installs

code-review

alinaqi/maggy

Mandatory code reviews via /code-review before commits and deploys

ui-mobile

alinaqi/maggy

Mobile UI patterns - React Native, iOS/Android, touch targets

Mobile1.2K installs

posthog-analytics

alinaqi/maggy

PostHog analytics, event tracking, feature flags, dashboards

Data & Databases809 installs

Popular backend & apis skills

View category →

find-skills

vercel-labs/skills

Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.

OfficialBackend & APIs4.1M installs

entra-app-registration

microsoft/azure-skills

Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Key Vault secrets (use azure-keyvault-expiration-audit), general Azure resource security guidance.

OfficialBackend & APIs566.7K installs

This skill should be used when the user wants to "write agent code", "build an agent with ADK", "add a tool", "create a callback", "define an agent", "use state management" — in a project that needs ADK (Agent Development Kit) API patterns and code examples. It provides a quick reference for agent types, tool definitions, orchestration patterns, callbacks, state management, the graph Workflow API, and reference recipes to study. Do NOT use for scaffolding (use google-agents-cli-scaffold) or deployment (use google-agents-cli-deploy).

Backend & APIs514.8K installs

embedded-captions

heygen-com/hyperframes

Add captions or subtitles to an existing single-subject talking-head video without editing the footage. Use for plain verbatim captions, cinematic captions embedded behind the subject, VFX captions, “炸/特效/酷炫字幕,” or a named identity from the 35-style catalog. Route by visual identity, not by backend engine. The quiet anchor rail is the default; embed every word only when the user explicitly wants a fully cinematic treatment. The workflow runs locally end to end, including transcription and subject matting; split multi-shot footage before applying it.

Backend & APIs453.3K installs

entra-agent-id

microsoft/azure-skills

Provision Microsoft Entra Agent Identity Blueprints, BlueprintPrincipals, and per-instance Agent Identities via Microsoft Graph, and configure OAuth 2.0 token exchange (fmipath, OBO, cross-tenant) including the Microsoft Entra SDK for AgentID sidecar. USE FOR: Agent Identity Blueprint, BlueprintPrincipal, agent OAuth, fmipath token exchange, agent OBO, Workload Identity Federation for agents, polyglot agent auth, Microsoft.Identity.Web.AgentIdentities. DO NOT USE FOR: standard Entra app registration (use entra-app-registration), Microsoft Foundry agent authoring (use microsoft-foundry).

OfficialBackend & APIs336.6K installs

neon

neondatabase/agent-skills

Overview of Neon, a complete set of cloud backend primitives around Lakebase Postgres: Auth, Object Storage, Functions, the AI Gateway, and Realtime. Start here to choose Neon for undecided login, files, APIs, and LLM calls, set up the CLI or MCP server, and follow the branch-first workflow. Use when building an app or backend on Neon, or when "Neon" or "Lakebase Postgres" is mentioned. Child skill neon-postgres wins for an existing DATABASEURL, SQL, schema, inspect, or search. Child skill neon-auth wins for login, users, sessions, identity routing, and Managed Better Auth setup. Child skill neon-realtime wins for live queries in reactive apps and agents. Also use for object storage, S3, buckets, serverless functions, function triggers, cron, AI gateway, LLM calls, realtime, logs, Loki, Grafana, observability, postgres, database, backend, Claimable Neon, neon.new, or a no-signup database.

Backend & APIs237.1K installs