Based in Johannesburg·Shipping nationwide across South Africa
Developer

API Documentation

Integration reference for connecting your Commerce Manager to the Qustom Collectibles storefront. All endpoints require API key authentication and accept JSON request bodies.

Authentication

Every request must include an API key in the X-API-Key header. The key is validated against the store's STORE_API_KEY secret.

X-API-Key: your_store_api_key_here

If you don't have the API key, request it from the store administrator. Requests without a valid key receive a 401 Unauthorized response.

Products API

Full product management — push upserts, list inventory, unpublish, or permanently delete products from your Commerce Manager.

POST/api/functions/externalProductsApi

action: "list"

Returns up to 500 products, newest first. Optionally filter by tcg, product_type, status, pokemon_set, or sku. Use this to sync stock levels and product details back to your Commerce Manager.

Request Body
{
  "action": "list",
  "tcg": "pokemon",
  "product_type": "sealed",
  "status": "publish",
  "limit": 500
}
Response (200)
{
  "products": [
    {
      "id": "abc123...",
      "sku": "PRC-SV-001",
      "name": "Scarlet & Violet Booster Pack",
      "regular_price": "79.99",
      "sale_price": "",
      "stock_quantity": 48,
      "stock_status": "instock",
      "tcg": "pokemon",
      "product_type": "sealed",
      "singles_subcategory": "auto",
      "pokemon_set": "Scarlet & Violet",
      "status": "publish",
      "allowed_payment_methods": [
        "eft"
      ],
      "updated_date": "2026-08-01T12:00:00Z"
    }
  ]
}

action: "upsert"

Create or update a product. Matches existing records by SKU — if a product with the same SKU exists, it is updated; otherwise a new record is created. The pokemon_set field is manually allocated per product (it is not inferred from the product name or categories), so pass it explicitly to assign the product to a set. Use the taxonomy or listSets action to retrieve the list of valid set names. For singles, pass singles_subcategory to assign the card to a subcategory (SIRs & IRs, Full Arts, Promos, EX Cards, Uncategorized) — use "auto" to let the store detect it from the card name. Pass allowed_payment_methods to restrict the product to specific checkout methods (yoco, payfast, eft) — at checkout, only methods allowed by every item in the cart are shown; omit or pass an empty array to allow all methods.

Request Body
{
  "action": "upsert",
  "product": {
    "sku": "PRC-SV-001",
    "name": "Scarlet & Violet Booster Pack",
    "description": "1x Scarlet & Violet base set booster pack.",
    "regular_price": "79.99",
    "sale_price": "69.99",
    "stock_quantity": 48,
    "stock_status": "instock",
    "image_url": "https://cdn.example.com/sv-pack.jpg",
    "categories": [
      "Sealed",
      "Scarlet & Violet"
    ],
    "tags": [
      "pokemon",
      "booster"
    ],
    "pokemon_set": "Scarlet & Violet",
    "tcg": "pokemon",
    "product_type": "sealed",
    "singles_subcategory": "auto",
    "allowed_payment_methods": [
      "eft"
    ],
    "status": "publish"
  }
}
Response (200)
{
  "product": {
    "id": "abc123..."
  }
}

action: "unpublish"

Marks a product as draft (hidden from the storefront). Provide either the internal product id or the SKU.

Request Body
{
  "action": "unpublish",
  "sku": "PRC-SV-001"
}
Response (200)
{
  "success": true
}

action: "delete"

Permanently deletes a product from the store. Provide either the internal product id or the SKU. This action cannot be undone.

Request Body
{
  "action": "delete",
  "sku": "PRC-SV-001"
}
Response (200)
{
  "success": true
}

action: "taxonomy"

Returns the store's full TCG taxonomy — supported games, product types, and Pokémon sets grouped by era. Set data is sourced dynamically from the PokemonSet entity, which is synced nightly from Scrydex (scrydex.com/pokemon/expansions) and covers all eras from Base Set through the current Mega Evolution era. Use this to populate dropdowns in external platforms so their category structure matches this store exactly.

Request Body
{
  "action": "taxonomy"
}
Response (200)
{
  "tcg_games": [
    {
      "value": "pokemon",
      "label": "Pokémon"
    },
    {
      "value": "mtg",
      "label": "MTG"
    },
    {
      "value": "yugioh",
      "label": "Yu-Gi-Oh!"
    },
    {
      "value": "onepiece",
      "label": "One Piece"
    },
    {
      "value": "digimon",
      "label": "Digimon"
    },
    {
      "value": "other",
      "label": "Other"
    }
  ],
  "product_types": [
    {
      "value": "sealed",
      "label": "Sealed"
    },
    {
      "value": "singles",
      "label": "Singles"
    },
    {
      "value": "graded",
      "label": "Graded"
    },
    {
      "value": "figurines",
      "label": "Figurines"
    },
    {
      "value": "storage",
      "label": "Storage"
    },
    {
      "value": "custom_art",
      "label": "Custom Art"
    },
    {
      "value": "grading_service",
      "label": "Slabbing"
    }
  ],
  "singles_subcategories": [
    {
      "value": "auto",
      "label": "Auto-detect from name"
    },
    {
      "value": "sir_ir",
      "label": "SIRs & IRs"
    },
    {
      "value": "full_art",
      "label": "Full Arts"
    },
    {
      "value": "hyper_rare",
      "label": "Hyper Rares"
    },
    {
      "value": "tg_gg",
      "label": "TG & GG"
    },
    {
      "value": "promo",
      "label": "Promos"
    },
    {
      "value": "ex",
      "label": "EX Cards"
    },
    {
      "value": "uncategorized",
      "label": "Uncategorized"
    }
  ],
  "pokemon_eras": [
    {
      "name": "Mega Evolution Era",
      "range": "",
      "sets": [
        "Pitch Black",
        "Chaos Rising",
        "Perfect Order",
        "Ascended Heroes",
        "Phantasmal Flames"
      ]
    },
    {
      "name": "Scarlet & Violet Era",
      "range": "",
      "sets": [
        "Scarlet & Violet",
        "Paldea Evolved",
        "Obsidian Flames",
        "151",
        "Prismatic Evolutions"
      ]
    }
  ],
  "pokemon_sets": [
    "Pitch Black",
    "Chaos Rising",
    "Scarlet & Violet",
    "Paldea Evolved",
    "151"
  ],
  "pokemon_sets_detail": [
    {
      "name": "Pitch Black",
      "tcg_api_id": "me5",
      "era": "Mega Evolution Era",
      "series": "Mega Evolution",
      "release_date": "2026-07-17",
      "logo_url": "https://images.scrydex.com/pokemon/me5-logo/logo",
      "total_cards": 120,
      "status": "released"
    },
    {
      "name": "Scarlet & Violet",
      "tcg_api_id": "sv1",
      "era": "Scarlet & Violet Era",
      "series": "Scarlet & Violet",
      "release_date": "2023-03-31",
      "logo_url": "https://images.scrydex.com/pokemon/sv1-logo/logo",
      "total_cards": 198,
      "status": "released"
    }
  ]
}

action: "listSets"

Returns all Pokémon TCG sets with full metadata (era, series, release date, logo URL, card count, release status). Data is synced nightly from Scrydex and includes every expansion from Base Set through the latest Mega Evolution sets. Use this to build set pickers, browse-by-set UIs, or sync set imagery to external platforms.

Request Body
{
  "action": "listSets"
}
Response (200)
{
  "sets": [
    {
      "name": "Pitch Black",
      "tcg_api_id": "me5",
      "era": "Mega Evolution Era",
      "series": "Mega Evolution",
      "release_date": "2026-07-17",
      "logo_url": "https://images.scrydex.com/pokemon/me5-logo/logo",
      "total_cards": 120,
      "status": "released"
    },
    {
      "name": "Chaos Rising",
      "tcg_api_id": "me4",
      "era": "Mega Evolution Era",
      "series": "Mega Evolution",
      "release_date": "2026-05-22",
      "logo_url": "https://images.scrydex.com/pokemon/me4-logo/logo",
      "total_cards": 122,
      "status": "released"
    }
  ]
}
Field Reference
FieldTypeRequiredDescription
skustringYesUnique product identifier used for matching
namestringNoProduct display name
descriptionstringNoFull product description
regular_pricestring|numberNoStandard price in ZAR
sale_pricestring|numberNoSale price (if on sale)
stock_quantitynumberNoUnits in stock (defaults to 0)
stock_statusenumNoinstock | outofstock | onbackorder
image_urlstringNoSingle image URL (added to images array)
imagesstring[]NoArray of image URLs (used if image_url omitted)
categoriesstring[]NoCategory labels
tagsstring[]NoProduct tags
pokemon_setstringNoPokémon TCG set name (e.g. "Scarlet & Violet", "151"). Manually allocated — not auto-inferred from the product name. Use the taxonomy or listSets action to retrieve valid set names.
tcgenumNopokemon | mtg | yugioh | onepiece | digimon | other. Alias: tcg_game
product_typeenumNosealed | singles | graded | figurines | storage | custom_art | grading_service. Alias: tcg_product_type
singles_subcategoryenumNoauto | sir_ir | full_art | hyper_rare | tg_gg | promo | ex | uncategorized. Only meaningful when product_type is "singles". "auto" detects from the card name (Illustration Rare, Trainer/Galarian Gallery, Hyper Rare, Full Art, Promo, EX keywords); set explicitly to override. Preserved on re-sync if not provided.
featuredbooleanNoFeatured product flag
slugstringNoURL-friendly slug
statusenumNopublish | draft (defaults to publish)
allowed_payment_methodsstring[]NoRestrict checkout to specific methods — yoco | payfast | eft. Empty array (or omitted) allows all methods. At checkout, only methods allowed by every item in the cart are shown (most restrictive item wins). Preserved on re-sync if not provided.

Orders API

Full order management — list, retrieve, update, and delete orders from your Commerce Manager, matching the control level of the WooCommerce REST API.

POST/api/functions/externalOrdersApi

action: "list"

Returns up to 500 orders, newest first. Optionally filter by a since timestamp (ISO 8601) to only fetch orders created after that date.

Request Body
{
  "action": "list",
  "since": "2026-07-01T00:00:00Z"
}
Response (200)
{
  "orders": [
    {
      "id": "ord123...",
      "order_number": "QC-1001",
      "order_date": "2026-07-31T10:30:00Z",
      "customer_name": "Jane Doe",
      "customer_email": "jane@example.com",
      "phone": "+27821234567",
      "shipping_address": "123 Main St, Johannesburg",
      "shipping_method": "door",
      "payment_method": "yoco",
      "payment_status": "paid",
      "status": "processing",
      "subtotal": 159.98,
      "shipping_cost": 99,
      "total": 258.98,
      "items": [
        {
          "product_id": "prod123",
          "product_name": "Scarlet & Violet Booster Pack",
          "sku": "PRC-SV-001",
          "quantity": 2,
          "price": "79.99",
          "total": 159.98
        }
      ]
    }
  ]
}

action: "get"

Retrieve a single order by its internal id or order_number.

Request Body
{
  "action": "get",
  "order_number": "QC-1001"
}
Response (200)
{
  "order": {
    "id": "ord123...",
    "order_number": "QC-1001",
    "order_date": "2026-07-31T10:30:00Z",
    "customer_id": "usr123...",
    "customer_name": "Jane Doe",
    "customer_email": "jane@example.com",
    "status": "processing",
    "payment_status": "paid",
    "total": 258.98,
    "items": []
  }
}

action: "create"

Create a new order on the store, linked to a customer. Pass customer_id to map the order to an existing customer (retrieve it via the Customers API). If customer_id is omitted, the order is created with the provided customer_name and email as a guest-style record. An order_number is auto-generated if not supplied.

Request Body
{
  "action": "create",
  "customer_id": "usr123...",
  "customer_name": "Jane Doe",
  "email": "jane@example.com",
  "phone": "+27821234567",
  "shipping_address": "123 Main St, Johannesburg",
  "shipping_method": "door",
  "payment_method": "yoco",
  "payment_status": "paid",
  "status": "processing",
  "items": [
    {
      "product_id": "prod123",
      "name": "Scarlet & Violet Booster Pack",
      "price": "79.99",
      "quantity": 2
    }
  ],
  "subtotal": 159.98,
  "shipping_cost": 99,
  "total": 258.98
}
Response (200)
{
  "order": {
    "id": "ord123...",
    "order_number": "QC-1001",
    "customer_id": "usr123...",
    "status": "processing",
    "total": 258.98
  }
}

action: "update"

Update an order. Provide id or order_number to identify the order, and a fields object with the changes. Updatable fields: status, payment_status, payment_method, customer_name, email, phone, shipping_address, shipping_method, locker_details, subtotal, shipping_cost, total, items, order_number, banking_details.

Request Body
{
  "action": "update",
  "order_number": "QC-1001",
  "fields": {
    "status": "shipped",
    "payment_status": "paid"
  }
}
Response (200)
{
  "order": {
    "id": "ord123...",
    "order_number": "QC-1001",
    "status": "shipped",
    "payment_status": "paid"
  }
}

action: "delete"

Permanently deletes an order. Provide id or order_number. This action cannot be undone.

Request Body
{
  "action": "delete",
  "order_number": "QC-1001"
}
Response (200)
{
  "success": true
}
Field Reference
FieldTypeRequiredDescription
actionstringYeslist | get | create | update | delete
idstringNoInternal order id (for get/update/delete)
order_numberstringNoOrder number (alternative to id for get/update/delete)
sincestring (ISO 8601)Nolist only — only return orders created after this datetime
customer_idstringNocreate only — links the order to a customer (from Customers API)
fieldsobjectNoupdate only — object of fields to change (status, payment_status, etc.)

Customers API

Manage store customers (users) from your Commerce Manager — list, retrieve, invite new customers, update profiles, or remove access.

POST/api/functions/externalCustomersApi

action: "list"

Returns up to 500 customers, newest first. Optionally filter by role (user or admin).

Request Body
{
  "action": "list",
  "role": "user"
}
Response (200)
{
  "customers": [
    {
      "id": "usr123...",
      "email": "jane@example.com",
      "full_name": "Jane Doe",
      "role": "user",
      "created_date": "2026-07-15T09:00:00Z"
    }
  ]
}

action: "get"

Retrieve a single customer by internal id or email.

Request Body
{
  "action": "get",
  "email": "jane@example.com"
}
Response (200)
{
  "customer": {
    "id": "usr123...",
    "email": "jane@example.com",
    "full_name": "Jane Doe",
    "role": "user",
    "created_date": "2026-07-15T09:00:00Z"
  }
}

action: "invite"

Invites a new customer to the store by email. They receive an email to set up their account and password. Pass the customer profile details (first_name, last_name, phone, shipping_address, pudo_locker) to pre-populate their account, and role (defaults to "user").

Request Body
{
  "action": "invite",
  "email": "newcustomer@example.com",
  "first_name": "John",
  "last_name": "Smith",
  "phone": "+27821234567",
  "shipping_address": "123 Main St, Johannesburg",
  "pudo_locker": "",
  "role": "user"
}
Response (200)
{
  "customer": {
    "id": "usr456...",
    "email": "newcustomer@example.com",
    "first_name": "John",
    "last_name": "Smith",
    "phone": "+27821234567",
    "shipping_address": "123 Main St, Johannesburg",
    "pudo_locker": "",
    "role": "user"
  }
}

action: "update"

Update a customer profile. Provide id or email to identify the customer, and a fields object. Updatable fields: first_name, last_name, phone, shipping_address, pudo_locker, role.

Request Body
{
  "action": "update",
  "email": "jane@example.com",
  "fields": {
    "first_name": "Jane",
    "last_name": "Smith",
    "phone": "+27821234567",
    "shipping_address": "456 Oak Ave, Johannesburg",
    "pudo_locker": "JHB-001",
    "role": "user"
  }
}
Response (200)
{
  "customer": {
    "id": "usr123...",
    "email": "jane@example.com",
    "first_name": "Jane",
    "last_name": "Smith",
    "phone": "+27821234567",
    "shipping_address": "456 Oak Ave, Johannesburg",
    "pudo_locker": "JHB-001",
    "role": "user"
  }
}

action: "delete"

Permanently removes a customer from the store. Provide id or email. This action cannot be undone.

Request Body
{
  "action": "delete",
  "email": "jane@example.com"
}
Response (200)
{
  "success": true
}
Field Reference
FieldTypeRequiredDescription
actionstringYeslist | get | invite | update | delete
idstringNoInternal customer id (for get/update/delete)
emailstringNoCustomer email (alternative to id for get/update/delete; required for invite)
roleenumNouser | admin (list filter or invite role; defaults to user)
first_namestringNoinvite — customer first name
last_namestringNoinvite — customer surname
phonestringNoinvite — contact number
shipping_addressstringNoinvite — default shipping address
pudo_lockerstringNoinvite — preferred PUDO locker (optional)
fieldsobjectNoupdate only — object of fields to change (first_name, last_name, phone, shipping_address, pudo_locker, role)

Error Responses

StatusMeaning
401Missing or invalid X-API-Key header
400Missing required fields or unknown action
500Server error — see response body for details

Quick Start Example

A complete cURL example for upserting a product:

curl -X POST https://your-app-domain.base44.app/api/functions/externalProductsApi \
  -H "Content-Type: application/json"   -H "X-API-Key: your_store_api_key_here"   -d '{
    "action": "upsert",
    "product": {
      "sku": "PRC-SV-001",
      "name": "Scarlet & Violet Booster Pack",
      "regular_price": "79.99",
      "stock_quantity": 48,
      "stock_status": "instock",
      "pokemon_set": "Scarlet & Violet",
      "tcg": "pokemon",
      "product_type": "sealed"
    }
  }'