Build an alternate shop on Villa Market fulfilment — the same model Ocado uses with Waitrose. You own the customer experience. Villa remains the grocer: catalog identity, branch price, stock, basket, order, payment, and vans.
Public host: https://partner.villamarket.ai
You never call Villa’s internal website APIs. This host is the only commerce surface partners use.
| Path | Role |
|---|---|
GET / |
This guide |
POST /graphql |
GraphQL |
/mcp |
MCP (Streamable HTTP) for agents |
GET /schema.graphql |
Download the SDL |
GET /health |
Liveness |
Every GraphQL and MCP call (except public docs) needs:
X-Partner-Key: <key we issue you>
The key maps to your partnerId and registered orderSource (stamped on
quotes and orders).
Shopper-scoped operations (basket, quote, order, payment) also need the customer’s Cognito id token:
Authorization: Bearer <shopper idToken>
ownerId is always the bare Cognito sub from that token. Do not invent it.
curl -sS https://partner.villamarket.ai/graphql \
-H 'Content-Type: application/json' \
-H 'X-Partner-Key: YOUR_KEY' \
-d '{
"query": "query($cpr: ID!, $b: ID!) { product(cprcode: $cpr, branchCode: $b) { cprcode nameEn price { amount currency source } inventory { sellableQty listable } } }",
"variables": { "cpr": "141660", "b": "1000" }
}'
Nested price and inventory are the join win: one client query, branch-
correct numbers. Never display a total that did not come from quote.
Add to .cursor/mcp.json:
{
"mcpServers": {
"villa-partner": {
"url": "https://partner.villamarket.ai/mcp",
"headers": {
"X-Partner-Key": "YOUR_KEY"
}
}
}
}
Tools mirror GraphQL: search_products, get_product, get_inventory,
get_basket, add_basket_line, set_basket_quantity, empty_basket,
create_quote, apply_coupon, create_order, start_payment, get_order,
list_orders, resolve_fulfilment.
Resources: villa://schema, villa://docs.
quote.grandTotal is the bill. Never sum line prices in your app.orderCreate omits payment. Payment is a separate paymentStart that
returns a KBank widget / session payload. Card PAN never touches this API.grandTotal is missing.test in specialComment.Discount field contract (coupons / shipping):
https://knowledge.villamarket.ai/reference/discount-fields
| Key | Meaning |
|---|---|
cprcode |
Product identity everywhere |
branchCode |
Fulfilment branch — price authority |
basketId |
Server-side basket |
orderId |
Minted then created |
Order status is not a top-level enum. Read payment.isPaid and
shipping.schedules[].pickingStatus.
| Sandbox | Production | |
|---|---|---|
| Host | same (partner.villamarket.ai) |
same |
| Keys | sandbox partner key | production partner key |
| Orders | specialComment contains test |
never test in prod |
| Payment | KBank sandbox widget | production merchant via Villa |
Live money requires: partner key issued, Cognito app client for your redirect URIs, fulfilment branch resolve confirmed, payment + order webhooks registered.
Register a HTTPS endpoint. We POST HMAC-signed events when payment settles or order status moves (once Villa feeds us those events).
POST https://your.example/webhooks/villa-partner
X-Villa-Partner-Signature: sha256=<hex>
Content-Type: application/json
{
"type": "payment.paid | order.status",
"orderId": "…",
"partnerId": "…",
"payload": {},
"ts": 1710000000
}
Until Villa HMAC webhooks exist, we fan out from sanctioned order polls.
Inbound (Villa → us, for our adapter):
POST https://partner.villamarket.ai/webhooks/villa/payment
POST https://partner.villamarket.ai/webhooks/villa/order-status
orderSource registeredpaymentStart sandbox closedtest in specialCommentEngineering vault (public contracts): https://knowledge.villamarket.ai
Ask for a partner key: contact Villa Market AI / partner onboarding.