Skip to content

Instantly share code, notes, and snippets.

@AniketSaki
Created February 20, 2026 12:32
Show Gist options
  • Select an option

  • Save AniketSaki/9215ec6a08234d281217474ea80965ff to your computer and use it in GitHub Desktop.

Select an option

Save AniketSaki/9215ec6a08234d281217474ea80965ff to your computer and use it in GitHub Desktop.
Virtualisation Demo
{
"$schema": "http://json-schema.org/draft-07/schema#",
"version": "1.0.0",
"description": "Detailed BPP adapter transformation spec: maps Beckn v2 BAP action calls to proprietary seller API calls and defines how to build each on_* callback payload.",
"notation": {
"$in.<path>": "Field from the inbound Beckn action payload (after ACK is sent)",
"$step<N>.<path>": "Field from the response of the Nth seller API call in the current action",
"$state.<key>": "Value from adapter's per-transaction state store (keyed on context.transaction_id)",
"$config.<key>": "Static seller/adapter configuration value",
"$fn.now()": "Current ISO-8601 UTC timestamp",
"$fn.uuid()": "Newly generated UUID v4",
"$fn.map(array, template)": "Map an array to new objects using the given template",
"$fn.sum(array, field)": "Sum a numeric field across an array",
"$fn.statusMap(status, direction)": "Map status codes per the statusMappings table",
"$fn.parseJSONPath(expr, key)": "Extract a filter value from a JSONPath expression string",
"$fn.round(n)": "Round a number to nearest integer",
"$fn.buildTrackUrl(trackingId)": "Construct tracking URL from config.trackingBaseUrl and trackingId"
},
"idMappings": {
"transactionId": {
"beckn": "context.transaction_id",
"seller": "transactionId (on Cart and Order)",
"note": "Primary session continuity key across all adapter calls"
},
"itemId": {
"beckn": "beckn:Item['beckn:id']",
"seller": "Product.id",
"note": "Direct 1:1 mapping; adapter uses this as productId in all cart/order operations"
},
"orderId": {
"beckn": "beckn:Order['beckn:id']",
"seller": "Order.id",
"note": "Seller assigns Order.id at confirm; adapter stores as beckn:id in state and echoes in all subsequent callbacks"
},
"offerId": {
"beckn": "beckn:Offer['beckn:id']",
"seller": "Offer.id",
"note": "Direct mapping"
},
"providerId": {
"beckn": "beckn:Order['beckn:seller'] / beckn:Provider['beckn:id']",
"seller": "N/A (single seller)",
"note": "Always set to config.providerId; seller is a single entity"
}
},
"statusMappings": {
"orderStatus": {
"sellerToBeckn": {
"pending": "PENDING",
"confirmed": "CONFIRMED",
"shipped": "INPROGRESS",
"delivered": "COMPLETED",
"cancelled": "CANCELLED",
"returned": "RETURNED"
},
"becknToSeller": {
"PENDING": "pending",
"CONFIRMED": "confirmed",
"INPROGRESS": "shipped",
"COMPLETED": "delivered",
"CANCELLED": "cancelled",
"RETURNED": "returned"
}
},
"paymentStatus": {
"sellerToBeckn": {
"initiated": "INITIATED",
"authorized": "AUTHORIZED",
"captured": "CAPTURED",
"completed": "COMPLETED",
"failed": "FAILED",
"refunded": "REFUNDED"
}
},
"trackingStatus": {
"sellerToBeckn": {
"pending": "DISABLED",
"confirmed": "DISABLED",
"shipped": "ACTIVE",
"delivered": "COMPLETED",
"cancelled": "DISABLED",
"returned": "DISABLED"
}
}
},
"actions": {
"discover": {
"becknEndpoint": "GET /beckn/discover",
"callbackEndpoint": "POST {$in.context.bap_uri}/on_discover",
"phase": "Discovery",
"description": "Translates a Beckn discovery/search intent into seller product search and active offer retrieval. Builds a single-catalog Beckn response.",
"immediateAck": {
"transaction_id": "$in.context.transaction_id",
"timestamp": "$fn.now()",
"ack_status": "ACK"
},
"payloadExtraction": {
"transactionId": "$in.context.transaction_id",
"bapUri": "$in.context.bap_uri",
"messageId": "$in.context.message_id",
"textSearch": "$in.message.text_search",
"filtersExpression": "$in.message.filters.expression",
"extractedCategory": "$fn.parseJSONPath($in.message.filters.expression, 'category')",
"extractedMinPrice": "$fn.parseJSONPath($in.message.filters.expression, 'minPrice')",
"extractedMaxPrice": "$fn.parseJSONPath($in.message.filters.expression, 'maxPrice')",
"extractedBrand": "$fn.parseJSONPath($in.message.filters.expression, 'brand')"
},
"sellerApiCalls": [
{
"step": 1,
"name": "searchProducts",
"description": "Full-text + filter product search. If no text_search, falls back to GET /products with category filter.",
"method": "GET",
"path": "/search",
"condition": "$in.message.text_search OR $in.message.filters",
"fallback": {
"condition": "NOT $in.message.text_search",
"method": "GET",
"path": "/products",
"queryParams": {
"category": "$extractedCategory"
}
},
"queryParams": {
"q": "$textSearch",
"category": "$extractedCategory",
"minPrice": "$extractedMinPrice",
"maxPrice": "$extractedMaxPrice"
},
"responseRef": "$step1",
"responseShape": {
"results": "Product[]",
"total": "number",
"query": "string"
},
"errorHandling": {
"4xx": "Return on_discover with empty catalog",
"5xx": "Retry once; if still failing, send NACK callback"
}
},
{
"step": 2,
"name": "getActiveOffers",
"description": "Fetch all active offer schemes (optionally filtered by category if extracted).",
"method": "GET",
"path": "/offers",
"queryParams": {
"active": true,
"category": "$extractedCategory"
},
"responseRef": "$step2",
"responseShape": {
"offers": "Offer[]",
"total": "number"
}
}
],
"callbackPayload": {
"method": "POST",
"url": "$in.context.bap_uri + '/on_discover'",
"body": {
"context": {
"version": "$in.context.version",
"action": "on_discover",
"timestamp": "$fn.now()",
"message_id": "$fn.uuid()",
"transaction_id": "$in.context.transaction_id",
"bpp_id": "$config.bppId",
"bpp_uri": "$config.bppUri",
"ttl": "$in.context.ttl"
},
"message": {
"catalogs": [
{
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:Catalog",
"beckn:id": "catalog-$config.bppId",
"beckn:bppId": "$config.bppId",
"beckn:bppUri": "$config.bppUri",
"beckn:descriptor": {
"@type": "beckn:Descriptor",
"schema:name": "$config.sellerName"
},
"beckn:items": {
"_transform": "$fn.map($step1.results, item => ({",
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:Item",
"beckn:id": "item.id",
"beckn:descriptor": {
"@type": "beckn:Descriptor",
"schema:name": "item.name",
"beckn:shortDesc": "item.description",
"schema:image": "item.images"
},
"beckn:category": {
"@type": "schema:CategoryCode",
"schema:codeValue": "item.category",
"schema:name": "item.category"
},
"beckn:rateable": true,
"beckn:rating": {
"@type": "beckn:Rating",
"beckn:ratingValue": "item.rating",
"beckn:ratingCount": "item.reviewCount"
},
"beckn:provider": {
"beckn:id": "$config.providerId",
"beckn:descriptor": {
"@type": "beckn:Descriptor",
"schema:name": "$config.sellerName"
}
},
"beckn:itemAttributes": {
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:ProductAttributes",
"beckn:price": {
"currency": "item.currency",
"value": "item.price"
},
"beckn:stock": "item.stock",
"beckn:brand": "item.brand",
"beckn:customAttributes": "item.attributes"
}
},
"beckn:offers": {
"_transform": "$fn.map($step2.offers, offer => ({",
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:Offer",
"beckn:id": "offer.id",
"beckn:descriptor": {
"@type": "beckn:Descriptor",
"schema:name": "offer.name",
"beckn:shortDesc": "offer.description"
},
"beckn:provider": "$config.providerId",
"beckn:items": "offer.applicableProductIds",
"beckn:isActive": "offer.active",
"beckn:validity": {
"@type": "beckn:TimePeriod",
"schema:startDate": "offer.startDate",
"schema:endDate": "offer.endDate"
},
"beckn:price": {
"currency": "$config.defaultCurrency",
"value": 0,
"components": [
{
"type": "DISCOUNT",
"value": "offer.value",
"description": "offer.type === 'percentage' ? offer.value + '% off' : '$' + offer.value + ' off'"
}
]
}
}
}
]
}
}
}
},
"select": {
"becknEndpoint": "POST /beckn/select",
"callbackEndpoint": "POST {$in.context.bap_uri}/on_select",
"phase": "Ordering",
"description": "Syncs the Beckn order intent with the seller cart. Reconciles additions, updates, and removals. Returns a priced quote.",
"immediateAck": {
"transaction_id": "$in.context.transaction_id",
"timestamp": "$fn.now()",
"ack_status": "ACK"
},
"payloadExtraction": {
"transactionId": "$in.context.transaction_id",
"bapUri": "$in.context.bap_uri",
"orderItems": "$in.message.order['beckn:orderItems']",
"seller": "$in.message.order['beckn:seller']"
},
"sellerApiCalls": [
{
"step": 1,
"name": "getCurrentCart",
"description": "Fetch the existing cart for this transaction to diff against incoming selection.",
"method": "GET",
"path": "/cart",
"queryParams": {
"transactionId": "$transactionId"
},
"responseRef": "$step1",
"responseShape": {
"id": "string",
"transactionId": "string",
"items": "CartItem[]",
"total": "number"
}
},
{
"step": "2a",
"name": "validateProducts",
"description": "For EACH item in orderItems: fetch live product to confirm existence and current price.",
"iterateOver": "$orderItems",
"iterationVar": "orderItem",
"method": "GET",
"path": "/products/{orderItem['beckn:orderedItem']}",
"responseRef": "$step2a[orderItem['beckn:orderedItem']]",
"responseShape": {
"id": "string",
"name": "string",
"price": "number",
"currency": "string",
"stock": "integer"
},
"errorHandling": {
"404": "Abort; send on_select with error: product not found",
"stockCheck": "If product.stock < orderItem['beckn:quantity'].unitQuantity → error: insufficient stock"
}
},
{
"step": "2b",
"name": "getProductOffers",
"description": "For EACH item in orderItems: fetch product-specific active offers.",
"iterateOver": "$orderItems",
"iterationVar": "orderItem",
"method": "GET",
"path": "/products/{orderItem['beckn:orderedItem']}/offers",
"responseRef": "$step2b[orderItem['beckn:orderedItem']]",
"responseShape": {
"offers": "Offer[]",
"total": "number",
"averageRating": "number"
}
},
{
"step": "2c",
"name": "addNewCartItems",
"description": "POST to cart for each item in orderItems that is NOT currently in $step1.items.",
"condition": "item IN $orderItems AND item NOT IN $step1.items",
"iterateOver": "newItems (difference of orderItems and step1.items)",
"iterationVar": "newItem",
"method": "POST",
"path": "/cart",
"requestBody": {
"transactionId": "$transactionId",
"productId": "$newItem['beckn:orderedItem']",
"quantity": "$newItem['beckn:quantity'].unitQuantity",
"price": "$step2a[$newItem['beckn:orderedItem']].price"
},
"responseRef": "$step2c"
},
{
"step": "2d",
"name": "updateExistingCartItems",
"description": "PUT to cart for each item that IS in both orderItems and step1.items but with a different quantity.",
"condition": "item IN $orderItems AND item IN $step1.items AND quantity differs",
"iterateOver": "updatedItems",
"iterationVar": "updItem",
"method": "PUT",
"path": "/cart",
"requestBody": {
"transactionId": "$transactionId",
"productId": "$updItem['beckn:orderedItem']",
"quantity": "$updItem['beckn:quantity'].unitQuantity"
},
"responseRef": "$step2d"
},
{
"step": "2e",
"name": "removeStaleCartItems",
"description": "DELETE from cart for each item in step1.items that is NOT in incoming orderItems.",
"condition": "item IN $step1.items AND item NOT IN $orderItems",
"iterateOver": "removedItems (in step1.items but not in orderItems)",
"iterationVar": "rmItem",
"method": "DELETE",
"path": "/cart",
"requestBody": {
"transactionId": "$transactionId",
"productId": "$rmItem.productId"
}
},
{
"step": 3,
"name": "getFinalCart",
"description": "Fetch the updated cart to get the authoritative total for the quote.",
"method": "GET",
"path": "/cart",
"queryParams": {
"transactionId": "$transactionId"
},
"responseRef": "$step3",
"responseShape": {
"id": "string",
"items": "CartItem[]",
"total": "number"
}
}
],
"stateUpdates": {
"priceSnapshot": "$fn.map($step3.items, i => ({ productId: i.productId, price: i.price, quantity: i.quantity }))",
"note": "Save price snapshot to detect changes at init time"
},
"callbackPayload": {
"method": "POST",
"url": "$in.context.bap_uri + '/on_select'",
"body": {
"context": {
"version": "$in.context.version",
"action": "on_select",
"timestamp": "$fn.now()",
"message_id": "$fn.uuid()",
"transaction_id": "$in.context.transaction_id",
"bpp_id": "$config.bppId",
"bpp_uri": "$config.bppUri",
"ttl": "$in.context.ttl"
},
"message": {
"order": {
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:Order",
"beckn:orderStatus": "CREATED",
"beckn:seller": "$config.providerId",
"beckn:buyer": "$in.message.order['beckn:buyer']",
"beckn:orderItems": {
"_transform": "$fn.map($step3.items, cartItem => ({",
"beckn:orderedItem": "cartItem.productId",
"beckn:quantity": {
"unitQuantity": "cartItem.quantity",
"unitCode": "UNIT"
},
"beckn:price": {
"currency": "$step2a[cartItem.productId].currency",
"value": "cartItem.price * cartItem.quantity",
"components": [
{
"type": "UNIT",
"value": "cartItem.price",
"description": "Unit price per item"
}
]
},
"beckn:acceptedOffer": {
"_note": "Populate if a matching offer exists in $step2b[cartItem.productId].offers",
"_condition": "$step2b[cartItem.productId].offers.length > 0",
"beckn:id": "$step2b[cartItem.productId].offers[0].id"
}
},
"beckn:orderValue": {
"currency": "$config.defaultCurrency",
"value": "$step3.total",
"components": {
"_transform": "$fn.map($step3.items, cartItem => ({",
"type": "UNIT",
"value": "cartItem.price * cartItem.quantity",
"description": "cartItem.productId + ' x ' + cartItem.quantity"
}
},
"beckn:payment": {
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:Payment",
"beckn:paymentStatus": "INITIATED",
"beckn:acceptedPaymentMethod": "$config.acceptedPaymentMethods"
}
}
}
}
}
},
"init": {
"becknEndpoint": "POST /beckn/init",
"callbackEndpoint": "POST {$in.context.bap_uri}/on_init",
"phase": "Ordering",
"description": "Validates prices and inventory against the current cart. Stores buyer and fulfillment details. Returns payment terms.",
"immediateAck": {
"transaction_id": "$in.context.transaction_id",
"timestamp": "$fn.now()",
"ack_status": "ACK"
},
"payloadExtraction": {
"transactionId": "$in.context.transaction_id",
"bapUri": "$in.context.bap_uri",
"buyer": "$in.message.order['beckn:buyer']",
"fulfillment": "$in.message.order['beckn:fulfillment']",
"paymentIntent": "$in.message.order['beckn:payment']",
"orderItems": "$in.message.order['beckn:orderItems']"
},
"sellerApiCalls": [
{
"step": 1,
"name": "verifyCart",
"description": "Ensure the cart still exists for this transaction.",
"method": "GET",
"path": "/cart",
"queryParams": {
"transactionId": "$transactionId"
},
"responseRef": "$step1",
"errorHandling": {
"emptyCart": "Abort; send NACK — no active cart for this transaction"
}
},
{
"step": "2a",
"name": "verifyPrices",
"description": "For EACH cart item: compare live product price against the price snapshot stored at select time. Abort if any price changed.",
"iterateOver": "$step1.items",
"iterationVar": "cartItem",
"method": "GET",
"path": "/products/{cartItem.productId}",
"responseRef": "$step2a[cartItem.productId]",
"priceChangeCheck": {
"snapshotPrice": "$state.priceSnapshot[cartItem.productId]",
"livePrice": "$step2a[cartItem.productId].price",
"onMismatch": "Send on_init with error: { code: 'PRICE_CHANGED', message: 'Price of {productId} has changed from {snapshotPrice} to {livePrice}' }"
}
},
{
"step": "2b",
"name": "verifyInventory",
"description": "For EACH cart item: confirm available inventory >= required quantity.",
"iterateOver": "$step1.items",
"iterationVar": "cartItem",
"method": "GET",
"path": "/inventory/{cartItem.productId}",
"responseRef": "$step2b[cartItem.productId]",
"stockCheck": {
"available": "$step2b[cartItem.productId].available",
"required": "cartItem.quantity",
"onFailure": "Send on_init with error: { code: 'OUT_OF_STOCK', message: 'Insufficient stock for {productId}' }"
}
}
],
"stateUpdates": {
"buyerInfo": "$buyer",
"fulfillmentInfo": "$fulfillment",
"paymentMethod": "$paymentIntent['beckn:acceptedPaymentMethod'][0]"
},
"callbackPayload": {
"method": "POST",
"url": "$in.context.bap_uri + '/on_init'",
"body": {
"context": {
"version": "$in.context.version",
"action": "on_init",
"timestamp": "$fn.now()",
"message_id": "$fn.uuid()",
"transaction_id": "$in.context.transaction_id",
"bpp_id": "$config.bppId",
"bpp_uri": "$config.bppUri",
"ttl": "$in.context.ttl"
},
"message": {
"order": {
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:Order",
"beckn:orderStatus": "PENDING",
"beckn:seller": "$config.providerId",
"beckn:buyer": "$buyer",
"beckn:orderItems": "$in.message.order['beckn:orderItems']",
"beckn:orderValue": {
"currency": "$config.defaultCurrency",
"value": "$step1.total"
},
"beckn:fulfillment": "$fulfillment",
"beckn:payment": {
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:Payment",
"beckn:paymentStatus": "INITIATED",
"beckn:amount": {
"currency": "$config.defaultCurrency",
"value": "$step1.total"
},
"beckn:acceptedPaymentMethod": "$config.acceptedPaymentMethods",
"beckn:paymentURL": "$config.paymentGatewayUrl + '?transaction_id=' + $transactionId + '&amount=' + $step1.total"
}
}
}
}
}
},
"confirm": {
"becknEndpoint": "POST /beckn/confirm",
"callbackEndpoint": "POST {$in.context.bap_uri}/on_confirm",
"phase": "Ordering",
"description": "Creates the seller order, processes payment, and confirms the order. Assigns the seller order ID as beckn:id.",
"immediateAck": {
"transaction_id": "$in.context.transaction_id",
"timestamp": "$fn.now()",
"ack_status": "ACK"
},
"payloadExtraction": {
"transactionId": "$in.context.transaction_id",
"bapUri": "$in.context.bap_uri",
"orderItems": "$in.message.order['beckn:orderItems']",
"orderValue": "$in.message.order['beckn:orderValue']",
"fulfillment": "$in.message.order['beckn:fulfillment']",
"payment": "$in.message.order['beckn:payment']",
"buyer": "$in.message.order['beckn:buyer']"
},
"derivedValues": {
"shippingAddress": {
"street": "$fulfillment['beckn:deliveryAttributes'].address.streetAddress",
"city": "$fulfillment['beckn:deliveryAttributes'].address.addressLocality",
"state": "$fulfillment['beckn:deliveryAttributes'].address.addressRegion",
"zipCode": "$fulfillment['beckn:deliveryAttributes'].address.postalCode",
"country": "$fulfillment['beckn:deliveryAttributes'].address.addressCountry"
},
"paymentMethod": "$payment['beckn:acceptedPaymentMethod'][0]",
"orderTotal": "$orderValue.value",
"currency": "$orderValue.currency"
},
"sellerApiCalls": [
{
"step": 1,
"name": "createOrder",
"description": "Create a new order in the seller system with status 'pending'.",
"method": "POST",
"path": "/orders",
"requestBody": {
"transactionId": "$transactionId",
"items": {
"_transform": "$fn.map($orderItems, item => ({",
"productId": "item['beckn:orderedItem']",
"quantity": "item['beckn:quantity'].unitQuantity",
"price": "item['beckn:price'].value / item['beckn:quantity'].unitQuantity",
"productName": "$state.priceSnapshot[item['beckn:orderedItem']].name"
},
"total": "$orderTotal",
"status": "pending",
"shippingAddress": "$shippingAddress",
"paymentMethod": "$paymentMethod"
},
"responseRef": "$step1",
"responseShape": {
"id": "string",
"transactionId": "string",
"status": "string",
"total": "number"
}
},
{
"step": 2,
"name": "processPayment",
"description": "Process payment against the newly created order.",
"method": "POST",
"path": "/payments/process",
"requestBody": {
"orderId": "$step1.id",
"amount": "$orderTotal",
"currency": "$currency",
"method": "$paymentMethod"
},
"responseRef": "$step2",
"responseShape": {
"id": "string",
"orderId": "string",
"amount": "number",
"status": "string",
"transactionId": "string"
},
"errorHandling": {
"paymentFailed": {
"condition": "$step2.status === 'failed'",
"action": "Send on_confirm with error and beckn:paymentStatus: 'FAILED'; do NOT proceed to step 3"
}
}
},
{
"step": 3,
"name": "confirmOrder",
"description": "Transition order status to 'confirmed' after successful payment.",
"condition": "$step2.status === 'completed' OR $step2.status === 'captured'",
"method": "PUT",
"path": "/orders/{$step1.id}/status",
"requestBody": {
"status": "confirmed"
},
"responseRef": "$step3",
"responseShape": {
"id": "string",
"status": "string"
}
}
],
"stateUpdates": {
"sellerOrderId": "$step1.id",
"paymentId": "$step2.id",
"paymentTxnRef": "$step2.transactionId"
},
"callbackPayload": {
"method": "POST",
"url": "$in.context.bap_uri + '/on_confirm'",
"successBody": {
"context": {
"version": "$in.context.version",
"action": "on_confirm",
"timestamp": "$fn.now()",
"message_id": "$fn.uuid()",
"transaction_id": "$in.context.transaction_id",
"bpp_id": "$config.bppId",
"bpp_uri": "$config.bppUri",
"ttl": "$in.context.ttl"
},
"message": {
"order": {
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:Order",
"beckn:id": "$step1.id",
"beckn:orderStatus": "CONFIRMED",
"beckn:seller": "$config.providerId",
"beckn:buyer": "$buyer",
"beckn:orderItems": "$in.message.order['beckn:orderItems']",
"beckn:orderValue": {
"currency": "$currency",
"value": "$step1.total"
},
"beckn:fulfillment": "$fulfillment",
"beckn:payment": {
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:Payment",
"beckn:id": "$step2.id",
"beckn:paymentStatus": "$fn.statusMap($step2.status, 'paymentSellerToBeckn')",
"beckn:amount": {
"currency": "$currency",
"value": "$step2.amount"
},
"beckn:txnRef": "$step2.transactionId",
"beckn:paidAt": "$fn.now()"
}
}
}
},
"failureBody": {
"_note": "Sent when step2.status === 'failed'",
"context": {
"version": "$in.context.version",
"action": "on_confirm",
"timestamp": "$fn.now()",
"message_id": "$fn.uuid()",
"transaction_id": "$in.context.transaction_id",
"bpp_id": "$config.bppId",
"bpp_uri": "$config.bppUri",
"ttl": "$in.context.ttl"
},
"message": {
"order": {
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:Order",
"beckn:id": "$step1.id",
"beckn:orderStatus": "FAILED",
"beckn:seller": "$config.providerId",
"beckn:buyer": "$buyer",
"beckn:payment": {
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:Payment",
"beckn:paymentStatus": "FAILED",
"beckn:amount": {
"currency": "$currency",
"value": "$step2.amount"
}
}
}
}
}
}
},
"status": {
"becknEndpoint": "POST /beckn/status",
"callbackEndpoint": "POST {$in.context.bap_uri}/on_status",
"phase": "Fulfillment",
"description": "Fetches the current order state from the seller and returns it in Beckn format.",
"immediateAck": {
"transaction_id": "$in.context.transaction_id",
"timestamp": "$fn.now()",
"ack_status": "ACK"
},
"payloadExtraction": {
"transactionId": "$in.context.transaction_id",
"bapUri": "$in.context.bap_uri",
"becknOrderId": "$in.message.order['beckn:id']",
"sellerOrderId": "$state.sellerOrderId"
},
"sellerApiCalls": [
{
"step": 1,
"name": "getOrder",
"description": "Fetch full order by seller order ID (resolved from adapter state).",
"method": "GET",
"path": "/orders/{$sellerOrderId}",
"responseRef": "$step1",
"responseShape": {
"id": "string",
"transactionId": "string",
"items": "OrderItem[]",
"total": "number",
"status": "string",
"shippingAddress": "Address",
"paymentMethod": "string",
"trackingId": "string | null",
"createdAt": "string",
"updatedAt": "string"
},
"errorHandling": {
"404": "Send on_status with empty body (Beckn allows empty message on status)"
}
}
],
"callbackPayload": {
"method": "POST",
"url": "$in.context.bap_uri + '/on_status'",
"body": {
"context": {
"version": "$in.context.version",
"action": "on_status",
"timestamp": "$fn.now()",
"message_id": "$fn.uuid()",
"transaction_id": "$in.context.transaction_id",
"bpp_id": "$config.bppId",
"bpp_uri": "$config.bppUri",
"ttl": "$in.context.ttl"
},
"message": {
"order": {
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:Order",
"beckn:id": "$step1.id",
"beckn:orderStatus": "$fn.statusMap($step1.status, 'orderSellerToBeckn')",
"beckn:seller": "$config.providerId",
"beckn:buyer": "$state.buyerInfo",
"beckn:orderItems": {
"_transform": "$fn.map($step1.items, item => ({",
"beckn:orderedItem": "item.productId",
"beckn:quantity": {
"unitQuantity": "item.quantity",
"unitCode": "UNIT"
},
"beckn:price": {
"currency": "$config.defaultCurrency",
"value": "item.price * item.quantity"
}
},
"beckn:orderValue": {
"currency": "$config.defaultCurrency",
"value": "$step1.total"
},
"beckn:fulfillment": {
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:Fulfillment",
"beckn:mode": "DELIVERY",
"beckn:fulfillmentStatus": "$fn.statusMap($step1.status, 'orderSellerToBeckn')",
"trackingAction": {
"_condition": "$step1.trackingId !== null",
"id": "$step1.trackingId",
"url": "$fn.buildTrackUrl($step1.trackingId)"
}
},
"beckn:payment": {
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:Payment",
"beckn:id": "$state.paymentId",
"beckn:paymentStatus": "COMPLETED",
"beckn:txnRef": "$state.paymentTxnRef"
}
}
}
}
}
},
"update": {
"becknEndpoint": "POST /beckn/update",
"callbackEndpoint": "POST {$in.context.bap_uri}/on_update",
"phase": "Fulfillment",
"description": "Applies buyer-initiated mutations to an in-flight order. Mutation type is detected from the diff between the inbound order and the stored order state.",
"immediateAck": {
"transaction_id": "$in.context.transaction_id",
"timestamp": "$fn.now()",
"ack_status": "ACK"
},
"payloadExtraction": {
"transactionId": "$in.context.transaction_id",
"bapUri": "$in.context.bap_uri",
"sellerOrderId": "$state.sellerOrderId",
"inboundOrderStatus": "$in.message.order['beckn:orderStatus']",
"inboundFulfillment": "$in.message.order['beckn:fulfillment']",
"inboundOrderItems": "$in.message.order['beckn:orderItems']"
},
"mutationDetection": {
"addressChange": {
"condition": "$inboundFulfillment differs from $state.fulfillmentInfo",
"description": "Buyer updated shipping address"
},
"returnRequest": {
"condition": "$inboundOrderStatus === 'RETURNED'",
"description": "Buyer is requesting a return post-delivery"
},
"quantityChange": {
"condition": "$inboundOrderItems quantities differ from confirmed order",
"description": "Buyer modifying item quantities (adapter-managed only)"
},
"statusChange": {
"condition": "$inboundOrderStatus differs from current seller status and is a valid seller status",
"description": "Explicit status override"
}
},
"sellerApiCalls": [
{
"step": "1_addressChange",
"name": "storeUpdatedAddress",
"condition": "mutationType === 'addressChange'",
"description": "No seller API endpoint for address update post-confirm. Store new address in adapter state.",
"method": null,
"action": "Update $state.fulfillmentInfo with $inboundFulfillment"
},
{
"step": "1_returnRequest",
"name": "initiateReturn",
"condition": "mutationType === 'returnRequest'",
"description": "Only valid when current order status is 'delivered'.",
"method": "PUT",
"path": "/orders/{$sellerOrderId}/return",
"responseRef": "$step1",
"errorHandling": {
"400": "Send on_update with error: order not in deliverable state for return"
}
},
{
"step": "1_statusChange",
"name": "updateStatus",
"condition": "mutationType === 'statusChange'",
"method": "PUT",
"path": "/orders/{$sellerOrderId}/status",
"requestBody": {
"status": "$fn.statusMap($inboundOrderStatus, 'orderBecknToSeller')"
},
"responseRef": "$step1"
}
],
"callbackPayload": {
"method": "POST",
"url": "$in.context.bap_uri + '/on_update'",
"body": {
"context": {
"version": "$in.context.version",
"action": "on_update",
"timestamp": "$fn.now()",
"message_id": "$fn.uuid()",
"transaction_id": "$in.context.transaction_id",
"bpp_id": "$config.bppId",
"bpp_uri": "$config.bppUri",
"ttl": "$in.context.ttl"
},
"message": {
"order": {
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:Order",
"beckn:id": "$sellerOrderId",
"beckn:orderStatus": "$fn.statusMap($step1.status, 'orderSellerToBeckn')",
"beckn:seller": "$config.providerId",
"beckn:buyer": "$state.buyerInfo",
"beckn:fulfillment": "$state.fulfillmentInfo",
"beckn:orderItems": "$in.message.order['beckn:orderItems']",
"beckn:orderValue": "$in.message.order['beckn:orderValue']"
}
}
}
}
},
"cancel": {
"becknEndpoint": "POST /beckn/cancel",
"callbackEndpoint": "POST {$in.context.bap_uri}/on_cancel",
"phase": "Fulfillment",
"description": "Cancels a full or partial order. Partial cancellation is managed in adapter state since the seller API only supports full-order cancellation.",
"immediateAck": {
"transaction_id": "$in.context.transaction_id",
"timestamp": "$fn.now()",
"ack_status": "ACK"
},
"payloadExtraction": {
"transactionId": "$in.context.transaction_id",
"bapUri": "$in.context.bap_uri",
"sellerOrderId": "$state.sellerOrderId",
"becknOrderId": "$in.message.order['beckn:id']",
"cancelledItems": "$in.message.order['beckn:orderItems']",
"isFullCancel": "cancelledItems is null OR cancelledItems.length === 0"
},
"sellerApiCalls": [
{
"step": "1_fullCancel",
"name": "cancelOrder",
"condition": "$isFullCancel OR allRemainingLinesCancelled",
"description": "Cancel the entire order in seller system.",
"method": "PUT",
"path": "/orders/{$sellerOrderId}/cancel",
"responseRef": "$step1",
"responseShape": {
"id": "string",
"status": "cancelled"
},
"errorHandling": {
"400": "Send on_cancel with error: order in non-cancellable state"
}
},
{
"step": "1_partialCancel",
"name": "storePartialCancellation",
"condition": "NOT $isFullCancel",
"description": "Seller has no partial cancel endpoint. Record cancelled line IDs in adapter state. Escalate to full cancel if all lines are now cancelled.",
"method": null,
"action": "Merge $cancelledItems into $state.cancelledLines; if $state.cancelledLines covers all order lines → invoke full cancel (step 1_fullCancel)"
}
],
"callbackPayload": {
"method": "POST",
"url": "$in.context.bap_uri + '/on_cancel'",
"body": {
"context": {
"version": "$in.context.version",
"action": "on_cancel",
"timestamp": "$fn.now()",
"message_id": "$fn.uuid()",
"transaction_id": "$in.context.transaction_id",
"bpp_id": "$config.bppId",
"bpp_uri": "$config.bppUri",
"ttl": "$in.context.ttl"
},
"message": {
"order": {
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:Order",
"beckn:id": "$sellerOrderId",
"beckn:orderStatus": "CANCELLED (full) OR INPROGRESS with partial cancellation reflected in line state",
"beckn:seller": "$config.providerId",
"beckn:buyer": "$state.buyerInfo",
"beckn:orderItems": {
"_note": "Echo all order items; mark cancelled lines with a cancellation flag in beckn:orderItemAttributes"
},
"beckn:payment": {
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:Payment",
"beckn:paymentStatus": "REFUNDED (if eligible based on payment state and cancellation policy)",
"beckn:txnRef": "$state.paymentTxnRef"
}
}
}
}
}
},
"track": {
"becknEndpoint": "POST /beckn/track",
"callbackEndpoint": "POST {$in.context.bap_uri}/on_track",
"phase": "Fulfillment",
"description": "Returns a tracking handle for an active shipment. Seller order contains a trackingId field once shipped.",
"immediateAck": {
"transaction_id": "$in.context.transaction_id",
"timestamp": "$fn.now()",
"ack_status": "ACK"
},
"payloadExtraction": {
"transactionId": "$in.context.transaction_id",
"bapUri": "$in.context.bap_uri",
"trackingRef": "$in.message.tracking.id",
"sellerOrderId": "$state.sellerOrderId",
"callbackUrl": "$in.message.tracking.callbackUrl"
},
"sellerApiCalls": [
{
"step": 1,
"name": "getOrderForTracking",
"description": "Fetch the order to extract trackingId and current status.",
"method": "GET",
"path": "/orders/{$sellerOrderId}",
"responseRef": "$step1",
"responseShape": {
"id": "string",
"status": "string",
"trackingId": "string | null"
},
"errorHandling": {
"404": "Send on_track with trackingStatus: DISABLED",
"noTracking": "If trackingId is null and status is not 'shipped': send on_track with trackingStatus: DISABLED"
}
}
],
"callbackPayload": {
"method": "POST",
"url": "$in.context.bap_uri + '/on_track'",
"body": {
"context": {
"version": "$in.context.version",
"action": "on_track",
"timestamp": "$fn.now()",
"message_id": "$fn.uuid()",
"transaction_id": "$in.context.transaction_id",
"bpp_id": "$config.bppId",
"bpp_uri": "$config.bppUri",
"ttl": "$in.context.ttl"
},
"message": {
"tracking": {
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:Tracking",
"tl_method": "http/get",
"url": "$fn.buildTrackUrl($step1.trackingId)",
"trackingStatus": "$fn.statusMap($step1.status, 'trackingSellerToBeckn')"
}
}
}
}
},
"support": {
"becknEndpoint": "POST /beckn/support",
"callbackEndpoint": "POST {$in.context.bap_uri}/on_support",
"phase": "Post-Fulfillment",
"description": "Returns static seller support contact details. No seller API call is made; all data comes from adapter configuration.",
"immediateAck": {
"transaction_id": "$in.context.transaction_id",
"timestamp": "$fn.now()",
"ack_status": "ACK"
},
"payloadExtraction": {
"transactionId": "$in.context.transaction_id",
"bapUri": "$in.context.bap_uri",
"refId": "$in.message.refId",
"refType": "$in.message.refType"
},
"sellerApiCalls": [],
"callbackPayload": {
"method": "POST",
"url": "$in.context.bap_uri + '/on_support'",
"body": {
"context": {
"version": "$in.context.version",
"action": "on_support",
"timestamp": "$fn.now()",
"message_id": "$fn.uuid()",
"transaction_id": "$in.context.transaction_id",
"bpp_id": "$config.bppId",
"bpp_uri": "$config.bppUri",
"ttl": "$in.context.ttl"
},
"message": {
"support": {
"@context": "https://raw.githubusercontent.com/beckn/protocol-specifications-new/refs/heads/main/schema/core/v2/context.jsonld",
"@type": "beckn:SupportInfo",
"name": "$config.support.name",
"phone": "$config.support.phone",
"email": "$config.support.email",
"url": "$config.support.url",
"hours": "$config.support.hours",
"channels": "$config.support.channels"
}
}
}
}
},
"rating": {
"becknEndpoint": "POST /beckn/rating",
"callbackEndpoint": "POST {$in.context.bap_uri}/on_rating",
"phase": "Post-Fulfillment",
"description": "Submits buyer ratings/reviews to the seller. ITEM-category ratings are posted as product reviews. ORDER, PROVIDER, FULFILLMENT ratings are logged in adapter state only (no seller endpoint).",
"immediateAck": {
"transaction_id": "$in.context.transaction_id",
"timestamp": "$fn.now()",
"ack_status": "ACK"
},
"payloadExtraction": {
"transactionId": "$in.context.transaction_id",
"bapUri": "$in.context.bap_uri",
"ratings": "$in.message.ratings"
},
"sellerApiCalls": [
{
"step": "1_itemReviews",
"name": "submitItemReviews",
"description": "For EACH rating where category === 'ITEM': post a product review.",
"iterateOver": "$ratings.filter(r => r.category === 'ITEM')",
"iterationVar": "rating",
"method": "POST",
"path": "/products/{rating.id}/reviews",
"requestBody": {
"productId": "rating.id",
"reviewerName": "$state.buyerInfo['beckn:displayName'] OR 'Beckn Buyer'",
"rating": "$fn.round(rating.ratingValue)",
"title": "rating.feedback.tags[0] OR 'Review'",
"comment": "rating.feedback.comments OR ''"
},
"responseRef": "$step1[rating.id]",
"responseShape": {
"id": "string",
"productId": "string",
"rating": "integer",
"createdAt": "string"
}
},
{
"step": "1_nonItemRatings",
"name": "logNonItemRatings",
"description": "For ORDER, PROVIDER, FULFILLMENT, AGENT ratings: no seller API endpoint exists. Log in adapter state.",
"iterateOver": "$ratings.filter(r => r.category !== 'ITEM')",
"iterationVar": "rating",
"method": null,
"action": "Append to $state.nonItemRatings: { id: rating.id, category: rating.category, value: rating.ratingValue, feedback: rating.feedback }"
},
{
"step": 2,
"name": "getAggregateRating",
"description": "Optionally fetch updated aggregate rating for the first rated product to include in on_rating callback.",
"condition": "$ratings.filter(r => r.category === 'ITEM').length > 0",
"method": "GET",
"path": "/products/{$ratings.filter(r => r.category === 'ITEM')[0].id}/reviews",
"responseRef": "$step2",
"responseShape": {
"reviews": "Review[]",
"total": "number",
"averageRating": "number"
}
}
],
"callbackPayload": {
"method": "POST",
"url": "$in.context.bap_uri + '/on_rating'",
"body": {
"context": {
"version": "$in.context.version",
"action": "on_rating",
"timestamp": "$fn.now()",
"message_id": "$fn.uuid()",
"transaction_id": "$in.context.transaction_id",
"bpp_id": "$config.bppId",
"bpp_uri": "$config.bppUri",
"ttl": "$in.context.ttl"
},
"message": {
"received": true,
"aggregate": {
"_condition": "$step2 is defined",
"count": "$step2.total",
"value": "$step2.averageRating",
"best": 5,
"worst": 1
}
}
}
}
}
},
"adapterStateSchema": {
"description": "Per-transaction adapter state, keyed on context.transaction_id. Must be persisted across async steps.",
"key": "context.transaction_id",
"fields": {
"sellerOrderId": {
"type": "string",
"setAt": "confirm.step1",
"description": "Seller's Order.id"
},
"paymentId": {
"type": "string",
"setAt": "confirm.step2",
"description": "Seller's Payment.id"
},
"paymentTxnRef": {
"type": "string",
"setAt": "confirm.step2",
"description": "Payment gateway transaction reference"
},
"buyerInfo": {
"type": "object",
"setAt": "init",
"description": "Beckn Buyer object from init payload"
},
"fulfillmentInfo": {
"type": "object",
"setAt": "init; updated at update",
"description": "Beckn Fulfillment object"
},
"paymentMethod": {
"type": "string",
"setAt": "init",
"description": "Resolved payment method string"
},
"priceSnapshot": {
"type": "object",
"setAt": "select",
"description": "Map of productId → { price, quantity } at select time"
},
"cancelledLines": {
"type": "array",
"setAt": "cancel (partial)",
"description": "Array of cancelled beckn:lineId or beckn:orderedItem values"
},
"nonItemRatings": {
"type": "array",
"setAt": "rating",
"description": "Log of non-ITEM category ratings"
}
}
},
"configSchema": {
"description": "Static seller/adapter configuration. Must be provided at deployment time.",
"fields": {
"bppId": {
"type": "string",
"example": "seller-bpp.example.com"
},
"bppUri": {
"type": "string",
"example": "https://seller-bpp.example.com"
},
"providerId": {
"type": "string",
"example": "default-seller"
},
"sellerName": {
"type": "string",
"example": "Acme Electronics"
},
"sellerApiBase": {
"type": "string",
"example": "http://localhost:9090"
},
"defaultCurrency": {
"type": "string",
"example": "USD"
},
"trackingBaseUrl": {
"type": "string",
"example": "https://track.seller.example.com"
},
"paymentGatewayUrl": {
"type": "string",
"example": "https://pay.seller.example.com/checkout"
},
"acceptedPaymentMethods": {
"type": "array",
"example": [
"CREDIT_CARD",
"DEBIT_CARD",
"WALLET"
]
},
"support": {
"name": {
"type": "string",
"example": "Acme Support"
},
"phone": {
"type": "string",
"example": "+1-800-000-0000"
},
"email": {
"type": "string",
"example": "support@acme.example"
},
"url": {
"type": "string",
"example": "https://acme.example/support"
},
"hours": {
"type": "string",
"example": "Mon–Fri 09:00–18:00 EST"
},
"channels": {
"type": "array",
"example": [
"PHONE",
"EMAIL",
"WEB"
]
}
}
}
}
}

Beckn v2 BAP → Proprietary Seller BPP: Protocol Mapping

Overview

This document describes how a BPP adapter translates each incoming Beckn v2 action API call into one or more proprietary seller API calls, and then constructs and dispatches the corresponding Beckn callback.

BAP                   BPP Adapter               Seller API
 |                        |                          |
 |── POST /beckn/select ─>|                          |
 |<── ACK ───────────────|                          |
 |                        |── GET /products/{id} ──>|
 |                        |<─ Product ──────────────|
 |                        |── POST /cart ──────────>|
 |                        |<─ Cart ─────────────────|
 |                        |                          |
 |<── POST /on_select ───|                          |
 |── ACK ────────────────>|                          |

The adapter:

  1. Receives a Beckn action call and immediately responds with ACK
  2. Asynchronously calls the seller API and translates the results
  3. Dispatches the on_{action} callback to context.bap_uri

Identity Model

Beckn Concept Seller API Equivalent Notes
context.transaction_id transactionId on Cart and Order Primary session key
beckn:Item.beckn:id Product.id Direct mapping
beckn:Order.beckn:id Order.id Assigned by seller on confirm
beckn:Order.beckn:seller Static: seller's BPP identifier Single seller
beckn:Offer.beckn:id Offer.id Direct mapping
beckn:OrderItem.beckn:orderedItem OrderItem.productId Direct mapping

The adapter maintains a transaction state store keyed on context.transaction_id to track:

  • Cart association (transactionId → seller cart state)
  • Order ID mapping (seller order.idbeckn:Order.beckn:id)
  • Payment ID for status tracking
  • Buyer identity info (used for review attribution in rating)
  • Price snapshot at select time (for change detection at init)
  • Partial cancellation line state (seller has no partial cancel API)

Status Mappings

Order Status

Seller Status Beckn beckn:orderStatus
pending PENDING
confirmed CONFIRMED
shipped INPROGRESS
delivered COMPLETED
cancelled CANCELLED
returned RETURNED

Payment Status

Seller Payment Status Beckn beckn:paymentStatus
initiated INITIATED
authorized AUTHORIZED
captured CAPTURED
completed COMPLETED
failed FAILED
refunded REFUNDED

Phase-to-Action Map

Beckn Phase BAP Action Seller API Operations
Discovery discover GET /search, GET /products, GET /offers
Ordering select Cart CRUD: GET /cart, POST /cart, PUT /cart, DELETE /cart
init GET /cart, GET /products, GET /inventory
confirm POST /orders, POST /payments/process, PUT /orders/{id}/status
Fulfillment status GET /orders/{id}
update PUT /orders/{id}/status, PUT /orders/{id}/return (mutation-dependent)
cancel PUT /orders/{id}/cancel
track GET /orders/{id}
Post-Fulfillment support None (static config)
rating POST /products/{id}/reviews

Action-by-Action Mapping

1. discoveron_discover — Discovery Phase

Purpose: Translate a Beckn search intent into seller product queries; return a formatted catalog.

Inbound fields used:

  • message.text_search — free-text query
  • message.filters.expression — JSONPath expression (parsed for category, minPrice, maxPrice, brand)
  • message.spatial — geographic constraints (passed as hints; seller /search does not natively support spatial filters)

Seller API sequence:

Step Call Purpose
1 GET /search?q=&category=&minPrice=&maxPrice= Primary text + filter search
1b GET /products?category= Fallback if no text query, only category
2 GET /offers?active=true Fetch all active offer schemes

JSONPath filter parsing rules:

  • @.category == 'X'category=X
  • @.price <= YmaxPrice=Y; @.price >= XminPrice=X
  • @.brand == 'B' → append to q

Catalog construction (on_discover message):

  • One beckn:Catalog for the entire BPP (single seller)
  • Each Product result → one beckn:Item (beckn:id = product.id)
  • Each active Offer → one beckn:Offer (beckn:id = offer.id, beckn:items = offer.applicableProductIds)
  • beckn:itemAttributes carries: price, currency, brand, stock, and product.attributes key-value pairs
  • Offer beckn:price uses a DISCOUNT component (percentage or flat discount value)

2. selecton_select — Ordering Phase

Purpose: Build or update the seller cart with the buyer's selected items; return a priced quote.

Inbound fields used:

  • context.transaction_id — cart session key
  • message.order.beckn:orderItems[] — selected items with quantity
    • beckn:orderedItemproductId
    • beckn:quantity.unitQuantityquantity
    • beckn:acceptedOfferofferId (if offer applied)

Seller API sequence:

Step Call Purpose
1 GET /cart?transactionId= Fetch existing cart
2a GET /products/{id} Validate each product; get live price
2b GET /products/{id}/offers Get product-specific offers
2c POST /cart Add new items not in current cart
2d PUT /cart Update quantity for existing cart items
2e DELETE /cart Remove cart items no longer in the selection
3 GET /cart?transactionId= Fetch final priced cart

Quote construction (on_select message):

  • Echo beckn:orderItems with beckn:price populated from live product prices
  • beckn:orderValue derived from cart total, broken into per-item UNIT price components
  • beckn:acceptedOffers populated if any offer is applicable and discount applied
  • beckn:orderStatus: "CREATED", beckn:payment.beckn:paymentStatus: "INITIATED"

3. initon_init — Ordering Phase

Purpose: Accept buyer and fulfillment details; validate prices and stock; return payment terms.

Inbound fields used:

  • message.order.beckn:buyer — buyer contact info stored in state
  • message.order.beckn:fulfillment — delivery mode and shipping address
  • message.order.beckn:payment.beckn:acceptedPaymentMethod — buyer's intended payment method

Seller API sequence:

Step Call Purpose
1 GET /cart?transactionId= Verify cart is still active
2 GET /products/{productId} (per item) Verify price hasn't changed since select
3 GET /inventory/{productId} (per item) Verify sufficient stock (available >= quantity)

Response construction (on_init message):

  • Echo order with buyer and fulfillment details
  • beckn:orderStatus: "PENDING"
  • beckn:payment.beckn:paymentStatus: "INITIATED"
  • beckn:payment.beckn:acceptedPaymentMethod: intersection of buyer's methods and seller's accepted methods (from config)
  • beckn:payment.beckn:paymentURL: payment gateway URL (if pre-payment required, from config)
  • If price changed → return error payload instead; ack_status: "NACK" or error in message

4. confirmon_confirm — Ordering Phase

Purpose: Finalize the order, process payment, and return a confirmed order with a seller-assigned ID.

Inbound fields used:

  • message.order.beckn:orderItems[] — final committed items
  • message.order.beckn:fulfillment.beckn:deliveryAttributes → shipping address
  • message.order.beckn:payment.beckn:acceptedPaymentMethod[0] → payment method
  • message.order.beckn:orderValue.value → expected total

Seller API sequence:

Step Call Purpose
1 POST /orders Create order (status: "pending")
2 POST /payments/process Process payment against order
3 PUT /orders/{id}/status"confirmed" Confirm order on payment success

Address mapping (fulfillment → shippingAddress):

  • beckn:deliveryAttributes.address.streetAddressstreet
  • beckn:deliveryAttributes.address.addressLocalitycity
  • beckn:deliveryAttributes.address.addressRegionstate
  • beckn:deliveryAttributes.address.postalCodezipCode
  • beckn:deliveryAttributes.address.addressCountrycountry

Response construction (on_confirm message):

  • Full order object with beckn:id = seller_order.id (stored in adapter state)
  • beckn:orderStatus: "CONFIRMED"
  • beckn:payment.beckn:paymentStatus: "COMPLETED" or "CAPTURED"
  • beckn:payment.beckn:txnRef: payment gateway reference ID from step 2 response
  • On payment failure: return error payload; beckn:payment.beckn:paymentStatus: "FAILED"

5. statuson_status — Fulfillment Phase

Purpose: Fetch the current order state and return it in Beckn format.

Inbound fields used:

  • message.order.beckn:id → resolved to seller_order.id via adapter state

Seller API sequence:

Step Call Purpose
1 GET /orders/{seller_order_id} Fetch full order

Response construction (on_status message):

  • Map order.statusbeckn:orderStatus using status table
  • If order.trackingId is present → include beckn:fulfillment.trackingAction.id = order.trackingId
  • Payment status derived from most recent payment record (use GET /payments/{paymentId} if payment ID is in adapter state)
  • Full order items echoed back with prices

6. updateon_update — Fulfillment Phase

Purpose: Apply a mutation to an in-flight order.

Update type detection and seller API calls:

Mutation Detection Heuristic Seller API Call
Shipping address change New beckn:fulfillment address in payload Store in adapter state (no seller update endpoint)
Item quantity change beckn:orderItems quantities differ from confirmed order Adapter-managed state; no seller endpoint post-confirm
Return initiation beckn:orderStatus == "RETURNED" or item return flag PUT /orders/{id}/return
Status override Explicit beckn:orderStatus field on update PUT /orders/{id}/status (where seller status enum allows)

Response construction (on_update message):

  • Reflect the resulting order state with applied mutations
  • Use adapter state for non-seller-API mutations

7. cancelon_cancel — Fulfillment Phase

Purpose: Cancel a full or partial order.

Inbound fields used:

  • message.order.beckn:id → seller order ID
  • message.order.beckn:orderItems[] (optional) → items to partially cancel

Seller API sequence:

Scenario Call Purpose
Full cancellation PUT /orders/{id}/cancel Cancel entire order
Partial cancellation Adapter state update only Mark specific lines as cancelled in state
All lines cancelled PUT /orders/{id}/cancel Escalate to full order cancel

Limitation: The seller API does not support partial order cancellation natively. Partial cancellations are managed entirely in adapter state. The adapter mirrors the cancelled line state in the on_cancel callback while keeping the order record intact in the seller system until all lines are cancelled.

Response construction (on_cancel message):

  • Full order echoed with cancelled items flagged (adapter state applied)
  • beckn:orderStatus: "CANCELLED" (full) or "INPROGRESS" with cancelled lines (partial)
  • If eligible for refund → beckn:payment.beckn:paymentStatus: "REFUNDED"

8. trackon_track — Fulfillment Phase

Purpose: Return a tracking handle or URL for an active shipment.

Inbound fields used:

  • message.tracking.id → seller order ID (or tracking ID if pre-mapped in state)

Seller API sequence:

Step Call Purpose
1 GET /orders/{seller_order_id} Fetch order and extract trackingId

Response construction (on_track message):

  • tracking.id = order.trackingId
  • tracking.url = "{config.trackingBaseUrl}?trackingId={order.trackingId}"
  • tl_method: "http/get" (redirect to tracking UI)
  • trackingStatus: "ACTIVE" if order status is shipped; "COMPLETED" if delivered; "DISABLED" otherwise

9. supporton_support — Post-Fulfillment Phase

Purpose: Return seller support contact details.

Seller API calls: None — support info is sourced from static seller configuration.

Response construction (on_support message):

beckn:SupportInfo:
  name:     config.support.name
  phone:    config.support.phone
  email:    config.support.email
  url:      config.support.url
  hours:    config.support.hours
  channels: config.support.channels

10. ratingon_rating — Post-Fulfillment Phase

Purpose: Submit buyer ratings/reviews to the seller.

Inbound fields used:

  • message.ratings[] — array of RatingInput objects
    • id → entity ID being rated
    • ratingValue → numeric score
    • categoryITEM | ORDER | FULFILLMENT | PROVIDER | AGENT
    • feedback.comments → review text
    • feedback.tags[] → review tags

Seller API sequence:

Scenario Call Purpose
category == "ITEM" POST /products/{ratingInput.id}/reviews Submit product review
category == "ORDER", "PROVIDER", "FULFILLMENT" None (adapter log) No direct seller endpoint

Review payload mapping:

  • productId: ratingInput.id
  • rating: round(ratingInput.ratingValue) (seller expects integer 1–5)
  • title: ratingInput.feedback.tags[0] or "Review" as fallback
  • comment: ratingInput.feedback.comments
  • reviewerName: buyer display name from adapter state (stored during init)

Response construction (on_rating message):

  • received: true
  • aggregate.value and aggregate.count: from GET /products/{id}/reviews response (optional enrichment)

Error Handling

Error Scenario Adapter Response
Product not found (step 2a of select) NACK on inbound; or error object in on_select callback
Price changed (init step 2 vs select snapshot) Error message in on_init; buyer must re-select
Insufficient stock (init step 3) Error message in on_init
Payment failed (confirm step 2) on_confirm with beckn:payment.beckn:paymentStatus: "FAILED" and error detail
Order cannot be cancelled (wrong status) on_cancel with error; beckn:orderStatus unchanged
Order not found in seller system NACK on inbound request
Seller API timeout / 5xx NACK immediately; retry policy per adapter config

Adapter Configuration Reference

The following values must be provided in static seller configuration:

Config Key Description Used In
config.bppId BPP identifier on Beckn network All callbacks (context)
config.bppUri BPP base URI All callbacks (context)
config.providerId Seller's provider ID in catalog discover, select, on_*
config.sellerName Display name of the seller discover catalog
config.sellerApiBase Seller API base URL All seller API calls
config.trackingBaseUrl Base URL for tracking page track
config.support.* Support contact details support
config.acceptedPaymentMethods Methods accepted by seller init
config.paymentGatewayUrl Payment gateway URL template init, confirm
"""
Select service — mapping.json §actions.select
BAP invocation:
POST ${RESTATE_INGRESS}/Select/handle/send
Body: { context, message: { order: { beckn:orderItems[], beckn:buyer? } } }
Flow:
Step 1 GET /cart?transactionId=
Step 2a GET /products/{id} (each orderItem — validate + live price)
Step 2b GET /products/{id}/offers (each orderItem)
Step 2c POST /cart (new items not in current cart)
Step 2d PUT /cart (existing items with changed quantity)
Step 2e DELETE /cart (stale items no longer in selection)
Step 3 GET /cart?transactionId= (final authoritative total)
State ← priceSnapshot
→ POST {bap_uri}/beckn/on_select
"""
from __future__ import annotations
import logging
import restate
import bap
import seller
import state
from transforms import (
build_select_order,
make_callback_context,
)
log = logging.getLogger("svc.select")
select_svc = restate.Service("select", invocation_retry_policy=restate.InvocationRetryPolicy(max_attempts=1))
@select_svc.handler()
async def handle(ctx: restate.Context, request: dict) -> None:
context = request.get("context", {})
order = request.get("message", {}).get("order", {})
txn_id = context.get("transaction_id", "")
bap_uri = context.get("bap_uri", "")
buyer = order.get("beckn:buyer")
order_items: list[dict] = order.get("beckn:orderItems", [])
log.info("[%s] select start items=%d", txn_id, len(order_items))
# Indexed by productId for quick diff
incoming: dict[str, dict] = {
oi["beckn:orderedItem"]: oi for oi in order_items
}
# ── step 1: current cart ─────────────────────────────────────────────
current_cart = await ctx.run(
"get_current_cart", seller.get_cart, args=(txn_id,)
)
existing: dict[str, dict] = {
ci["productId"]: ci for ci in current_cart.get("items", [])
}
# ── step 2a + 2b: validate products + get offers ─────────────────────
products_map: dict[str, dict] = {}
offers_map: dict[str, list] = {}
for pid, oi in incoming.items():
product = await ctx.run(
f"get_product_{pid}", seller.get_product, args=(pid,)
)
products_map[pid] = product
qty_required = (oi.get("beckn:quantity") or {}).get("unitQuantity", 1)
if product.get("stock", 999) < qty_required:
log.warning("[%s] insufficient stock for %s", txn_id, pid)
prod_offers_resp = await ctx.run(
f"get_offers_{pid}", seller.get_product_offers, args=(pid,)
)
offers_map[pid] = prod_offers_resp.get("offers", [])
# ── step 2c: add new items ───────────────────────────────────────────
for pid, oi in incoming.items():
if pid not in existing:
qty = (oi.get("beckn:quantity") or {}).get("unitQuantity", 1)
price = products_map[pid].get("price", 0)
await ctx.run(
f"add_cart_{pid}",
seller.add_cart_item,
args=(txn_id, pid, qty, price),
)
# ── step 2d: update changed quantities ───────────────────────────────
for pid, oi in incoming.items():
if pid in existing:
new_qty = (oi.get("beckn:quantity") or {}).get("unitQuantity", 1)
old_qty = existing[pid].get("quantity", 1)
if new_qty != old_qty:
await ctx.run(
f"update_cart_{pid}",
seller.update_cart_item,
args=(txn_id, pid, new_qty),
)
# ── step 2e: remove stale items ──────────────────────────────────────
for pid in list(existing.keys()):
if pid not in incoming:
await ctx.run(
f"delete_cart_{pid}",
seller.delete_cart_item,
args=(txn_id, pid),
)
# ── step 3: final cart ───────────────────────────────────────────────
final_cart = await ctx.run(
"get_final_cart", seller.get_cart, args=(txn_id,)
)
log.info(
"[%s] final cart items=%d total=%s",
txn_id, len(final_cart.get("items", [])), final_cart.get("total"),
)
# ── persist price snapshot to state ──────────────────────────────────
price_snapshot = {
ci["productId"]: {"price": ci.get("price", 0), "quantity": ci.get("quantity", 1)}
for ci in final_cart.get("items", [])
}
await state.update(txn_id, {"priceSnapshot": price_snapshot})
# ── build on_select callback ─────────────────────────────────────────
beckn_order = build_select_order(final_cart, products_map, offers_map, buyer)
callback = {
"context": make_callback_context(context, "on_select"),
"message": {"order": beckn_order},
}
await ctx.run("send_callback", bap.send_callback, args=(bap_uri, "on_select", callback))
log.info("[%s] select done", txn_id)
openapi: 3.0.3
info:
title: E-Commerce Seller API
description: >-
Seller API for e-commerce operations including products, cart, orders,
offers, payments, inventory, reviews, and search. The buyer identifies
themselves via a transactionId rather than a user account.
version: 2.0.0
contact:
name: Seller API Support
servers:
- url: 'http://localhost:9090'
description: Local development server
paths:
/products:
get:
tags:
- Products
summary: List products
operationId: listProducts
parameters:
- name: category
in: query
description: Filter products by category
required: false
schema:
type: string
responses:
'200':
description: A list of products
content:
application/json:
schema:
type: object
properties:
products:
type: array
items:
$ref: '#/components/schemas/Product'
total:
type: number
example: 10
required:
- products
- total
post:
tags:
- Products
summary: Create a product
operationId: createProduct
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Product'
responses:
'201':
description: Product created
content:
application/json:
schema:
$ref: '#/components/schemas/Product'
'/products/{id}':
get:
tags:
- Products
summary: Get product by ID
operationId: getProduct
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: A single product
content:
application/json:
schema:
$ref: '#/components/schemas/Product'
'404':
description: Product not found
put:
tags:
- Products
summary: Update a product
operationId: updateProduct
parameters:
- name: id
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Product'
responses:
'200':
description: Product updated
content:
application/json:
schema:
$ref: '#/components/schemas/Product'
'404':
description: Product not found
delete:
tags:
- Products
summary: Delete a product
operationId: deleteProduct
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: Product deleted
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Product deleted successfully
id:
type: string
example: prod-001
required:
- message
- id
'404':
description: Product not found
'/products/{id}/reviews':
get:
tags:
- Reviews
summary: Get reviews for a product
operationId: getProductReviews
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: Product reviews
content:
application/json:
schema:
type: object
properties:
reviews:
type: array
items:
$ref: '#/components/schemas/Review'
total:
type: number
example: 15
averageRating:
type: number
format: float
example: 4.2
required:
- reviews
- total
- averageRating
post:
tags:
- Reviews
summary: Add a review for a product
operationId: addProductReview
parameters:
- name: id
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Review'
responses:
'201':
description: Review added
content:
application/json:
schema:
$ref: '#/components/schemas/Review'
'/products/{id}/offers':
get:
tags:
- Offers
summary: Get active offers applicable to a product
operationId: getProductOffers
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: Offers applicable to this product
content:
application/json:
schema:
type: object
properties:
offers:
type: array
items:
$ref: '#/components/schemas/Offer'
total:
type: number
example: 2
required:
- offers
- total
'404':
description: Product not found
/cart:
get:
tags:
- Cart
summary: Get cart for a transaction
operationId: getCart
parameters:
- name: transactionId
in: query
required: true
schema:
type: string
example: txn-aabbcc11
responses:
'200':
description: Transaction cart
content:
application/json:
schema:
$ref: '#/components/schemas/Cart'
post:
tags:
- Cart
summary: Add item to cart
operationId: addToCart
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
transactionId:
type: string
example: txn-aabbcc11
productId:
type: string
example: prod-001
quantity:
type: integer
example: 2
price:
type: number
format: float
example: 29.99
required:
- transactionId
- productId
- quantity
- price
responses:
'201':
description: Item added to cart
content:
application/json:
schema:
$ref: '#/components/schemas/Cart'
put:
tags:
- Cart
summary: Update cart item quantity
operationId: updateCartItem
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
transactionId:
type: string
example: txn-aabbcc11
productId:
type: string
example: prod-001
quantity:
type: integer
example: 3
required:
- transactionId
- productId
- quantity
responses:
'200':
description: Cart item updated
content:
application/json:
schema:
$ref: '#/components/schemas/Cart'
'404':
description: Cart or item not found
delete:
tags:
- Cart
summary: Remove item from cart
operationId: removeFromCart
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
transactionId:
type: string
example: txn-aabbcc11
productId:
type: string
example: prod-001
required:
- transactionId
- productId
responses:
'200':
description: Item removed from cart
content:
application/json:
schema:
$ref: '#/components/schemas/Cart'
'404':
description: Cart not found
/orders:
post:
tags:
- Orders
summary: Create an order
operationId: createOrder
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
responses:
'201':
description: Order created
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
get:
tags:
- Orders
summary: List orders, optionally filtered by transactionId
operationId: listOrders
parameters:
- name: transactionId
in: query
required: false
schema:
type: string
example: txn-aabbcc11
responses:
'200':
description: A list of orders
content:
application/json:
schema:
type: object
properties:
orders:
type: array
items:
$ref: '#/components/schemas/Order'
total:
type: number
example: 5
required:
- orders
- total
'/orders/{id}':
get:
tags:
- Orders
summary: Get order by ID
operationId: getOrder
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: Order details
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
'404':
description: Order not found
'/orders/{id}/status':
put:
tags:
- Orders
summary: Update order status
operationId: updateOrderStatus
parameters:
- name: id
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
status:
type: string
enum:
- pending
- confirmed
- shipped
- delivered
- cancelled
- returned
example: confirmed
required:
- status
responses:
'200':
description: Order status updated
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
'400':
description: Invalid status value
'404':
description: Order not found
'/orders/{id}/cancel':
put:
tags:
- Orders
summary: Cancel an order
operationId: cancelOrder
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: Order cancelled
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
'400':
description: Order cannot be cancelled in its current status
'404':
description: Order not found
'/orders/{id}/return':
put:
tags:
- Orders
summary: Return an order
operationId: returnOrder
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: Order return initiated
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
'400':
description: Only delivered orders can be returned
'404':
description: Order not found
/offers:
get:
tags:
- Offers
summary: List offer schemes
operationId: listOffers
parameters:
- name: productId
in: query
description: Filter offers applicable to a specific product
required: false
schema:
type: string
- name: category
in: query
description: Filter offers applicable to a category
required: false
schema:
type: string
- name: active
in: query
description: Filter by active status
required: false
schema:
type: boolean
responses:
'200':
description: A list of offers
content:
application/json:
schema:
type: object
properties:
offers:
type: array
items:
$ref: '#/components/schemas/Offer'
total:
type: number
example: 3
required:
- offers
- total
post:
tags:
- Offers
summary: Create a new offer scheme
operationId: createOffer
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Offer'
responses:
'201':
description: Offer created
content:
application/json:
schema:
$ref: '#/components/schemas/Offer'
'/offers/{id}':
get:
tags:
- Offers
summary: Get offer by ID
operationId: getOffer
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: Offer details
content:
application/json:
schema:
$ref: '#/components/schemas/Offer'
'404':
description: Offer not found
put:
tags:
- Offers
summary: Update an offer scheme
operationId: updateOffer
parameters:
- name: id
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Offer'
responses:
'200':
description: Offer updated
content:
application/json:
schema:
$ref: '#/components/schemas/Offer'
'404':
description: Offer not found
delete:
tags:
- Offers
summary: Delete an offer scheme
operationId: deleteOffer
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: Offer deleted
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Offer deleted successfully
id:
type: string
example: offer-001
required:
- message
- id
'404':
description: Offer not found
/payments/process:
post:
tags:
- Payments
summary: Process a payment
operationId: processPayment
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
orderId:
type: string
example: order-001
amount:
type: number
format: float
example: 99.99
currency:
type: string
example: USD
method:
type: string
example: credit_card
required:
- orderId
- amount
- currency
- method
responses:
'201':
description: Payment processed
content:
application/json:
schema:
$ref: '#/components/schemas/Payment'
/payments/validate:
post:
tags:
- Payments
summary: Validate a payment
operationId: validatePayment
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
paymentId:
type: string
example: pay-001
transactionId:
type: string
example: txn-abc123
required:
- paymentId
- transactionId
responses:
'200':
description: Payment validation result
content:
application/json:
schema:
$ref: '#/components/schemas/Payment'
'/payments/{id}':
get:
tags:
- Payments
summary: Get payment by ID
operationId: getPayment
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: Payment details
content:
application/json:
schema:
$ref: '#/components/schemas/Payment'
'404':
description: Payment not found
'/inventory/{productId}':
get:
tags:
- Inventory
summary: Get inventory for a product
operationId: getInventory
parameters:
- name: productId
in: path
required: true
schema:
type: string
responses:
'200':
description: Inventory details
content:
application/json:
schema:
$ref: '#/components/schemas/InventoryItem'
'404':
description: Inventory not found
/inventory/low-stock:
get:
tags:
- Inventory
summary: Get low stock items
operationId: getLowStock
responses:
'200':
description: Low stock items
content:
application/json:
schema:
type: object
properties:
items:
type: array
items:
$ref: '#/components/schemas/InventoryItem'
total:
type: number
example: 3
required:
- items
- total
/search:
get:
tags:
- Search
summary: Search products
operationId: searchProducts
parameters:
- name: q
in: query
description: Search query string
required: false
schema:
type: string
- name: category
in: query
description: Filter by category
required: false
schema:
type: string
- name: minPrice
in: query
description: Minimum price filter
required: false
schema:
type: number
format: float
- name: maxPrice
in: query
description: Maximum price filter
required: false
schema:
type: number
format: float
responses:
'200':
description: Search results
content:
application/json:
schema:
type: object
properties:
results:
type: array
items:
$ref: '#/components/schemas/Product'
total:
type: number
example: 25
query:
type: string
example: laptop
required:
- results
- total
- query
/trending:
get:
tags:
- Search
summary: Get trending products
operationId: getTrending
responses:
'200':
description: Trending products
content:
application/json:
schema:
type: object
properties:
trending:
type: array
items:
$ref: '#/components/schemas/Product'
total:
type: number
example: 5
required:
- trending
- total
/health:
get:
tags:
- Health
summary: Health check
operationId: healthCheck
responses:
'200':
description: Service is healthy
content:
application/json:
schema:
type: object
properties:
status:
type: string
example: ok
timestamp:
type: string
format: date-time
example: '2026-01-15T10:30:00Z'
uptime:
type: number
example: 86400
required:
- status
- timestamp
components:
schemas:
Product:
type: object
properties:
id:
type: string
example: prod-001
name:
type: string
example: Wireless Bluetooth Headphones
description:
type: string
example: Premium noise-cancelling wireless headphones with 30hr battery life.
price:
type: number
format: float
example: 79.99
currency:
type: string
example: USD
category:
type: string
example: electronics
brand:
type: string
example: AudioMax
images:
type: array
items:
type: string
format: uri
example:
- 'https://example.com/images/headphones.jpg'
stock:
type: integer
example: 150
rating:
type: number
format: float
example: 4.5
reviewCount:
type: integer
example: 128
attributes:
type: object
additionalProperties:
type: string
example:
color: Midnight Black
storage: 128GB
createdAt:
type: string
format: date-time
example: '2026-01-10T08:00:00Z'
required:
- id
- name
- price
- category
Cart:
type: object
properties:
id:
type: string
example: cart-001
transactionId:
type: string
example: txn-aabbcc11
items:
type: array
items:
$ref: '#/components/schemas/CartItem'
total:
type: number
format: float
example: 159.98
updatedAt:
type: string
format: date-time
example: '2026-01-15T10:00:00Z'
required:
- id
- transactionId
- items
- total
CartItem:
type: object
properties:
productId:
type: string
example: prod-001
quantity:
type: integer
example: 2
price:
type: number
format: float
example: 79.99
required:
- productId
- quantity
- price
Order:
type: object
properties:
id:
type: string
example: order-001
transactionId:
type: string
example: txn-aabbcc11
items:
type: array
items:
$ref: '#/components/schemas/OrderItem'
total:
type: number
format: float
example: 189.97
status:
type: string
enum:
- pending
- confirmed
- shipped
- delivered
- cancelled
- returned
example: confirmed
shippingAddress:
$ref: '#/components/schemas/Address'
paymentMethod:
type: string
example: credit_card
trackingId:
type: string
nullable: true
example: track-001
createdAt:
type: string
format: date-time
example: '2026-01-15T10:30:00Z'
updatedAt:
type: string
format: date-time
example: '2026-01-15T10:30:00Z'
required:
- id
- transactionId
- items
- total
- status
OrderItem:
type: object
properties:
productId:
type: string
example: prod-001
productName:
type: string
example: Wireless Bluetooth Headphones
quantity:
type: integer
example: 1
price:
type: number
format: float
example: 79.99
required:
- productId
- quantity
- price
Address:
type: object
properties:
street:
type: string
example: 123 Main Street
city:
type: string
example: New York
state:
type: string
example: NY
zipCode:
type: string
example: '10001'
country:
type: string
example: US
required:
- street
- city
- state
- zipCode
- country
Payment:
type: object
properties:
id:
type: string
example: pay-001
orderId:
type: string
example: order-001
amount:
type: number
format: float
example: 189.97
currency:
type: string
example: USD
method:
type: string
example: credit_card
status:
type: string
enum:
- initiated
- authorized
- captured
- completed
- failed
- refunded
example: completed
transactionId:
type: string
example: txn-abc123
createdAt:
type: string
format: date-time
example: '2026-01-15T10:30:00Z'
required:
- id
- orderId
- amount
- currency
- method
- status
InventoryItem:
type: object
properties:
productId:
type: string
example: prod-001
productName:
type: string
example: Wireless Bluetooth Headphones
stock:
type: integer
example: 150
reserved:
type: integer
example: 10
available:
type: integer
example: 140
lowStockThreshold:
type: integer
example: 20
warehouse:
type: string
example: WH-West
updatedAt:
type: string
format: date-time
example: '2026-01-15T10:00:00Z'
required:
- productId
- stock
- available
Review:
type: object
properties:
id:
type: string
example: rev-001
productId:
type: string
example: prod-001
reviewerName:
type: string
example: John Doe
rating:
type: integer
minimum: 1
maximum: 5
example: 4
title:
type: string
example: Great headphones!
comment:
type: string
example: Excellent sound quality and comfortable to wear for long periods.
createdAt:
type: string
format: date-time
example: '2026-01-14T16:00:00Z'
required:
- id
- productId
- reviewerName
- rating
Offer:
type: object
properties:
id:
type: string
example: offer-001
code:
type: string
example: SAVE10
name:
type: string
example: 10% Off Electronics
description:
type: string
example: Get 10% off on all electronics. Max discount $50.
type:
type: string
enum:
- percentage
- flat
- bogo
example: percentage
value:
type: number
format: float
description: >-
Discount value. For percentage: the % amount (e.g. 10 = 10%). For
flat: the currency amount off. For bogo: the number of free items.
example: 10
minOrderValue:
type: number
format: float
description: Minimum order value required to apply this offer.
example: 100
maxDiscount:
type: number
format: float
nullable: true
description: Cap on the discount amount. Null means no cap.
example: 50
applicableProductIds:
type: array
items:
type: string
description: Product IDs this offer applies to. Empty array means all products.
example:
- prod-001
- prod-002
applicableCategories:
type: array
items:
type: string
description: Categories this offer applies to. Empty array means all categories.
example:
- electronics
startDate:
type: string
format: date-time
example: '2026-01-01T00:00:00Z'
endDate:
type: string
format: date-time
example: '2026-12-31T23:59:59Z'
active:
type: boolean
example: true
required:
- id
- code
- name
- type
- value
- minOrderValue
- applicableProductIds
- applicableCategories
- startDate
- endDate
- active
tags:
- name: Products
description: Product catalog operations
- name: Cart
description: Shopping cart operations (keyed by transactionId)
- name: Orders
description: Order management and status tracking
- name: Offers
description: Offer scheme definitions applicable to products or categories
- name: Payments
description: Payment processing and validation
- name: Inventory
description: Inventory tracking
- name: Reviews
description: Product reviews
- name: Search
description: Product search and trending
- name: Health
description: Service health check
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment