{
  "info": {
    "name": "Setu Custom Commerce API v1 (API key)",
    "_postman_id": "6f1d6f0e-7d1b-4f7e-9a51-5e7c0c0a0001",
    "description": "Requests Setu sends to a store in API-key mode. Set baseUrl to your Store API URL (e.g. https://shop.example.com/api/setu/v1) and apiKey to a key generated in your admin. Guide: https://setu.vantelyx.dev/developers/custom-commerce-api. Order requests send test: true — cancel them in your admin afterwards.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{apiKey}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://shop.example.com/api/setu/v1"
    },
    {
      "key": "apiKey",
      "value": ""
    },
    {
      "key": "productId",
      "value": "p_42"
    },
    {
      "key": "variantId",
      "value": "v_42_m_black"
    },
    {
      "key": "orderId",
      "value": ""
    },
    {
      "key": "idempotencyKey",
      "value": "setu:test:postman:1"
    },
    {
      "key": "orderNumber",
      "value": ""
    },
    {
      "key": "paymentSessionId",
      "value": ""
    }
  ],
  "item": [
    {
      "name": "Store — identity, version, capabilities",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/store",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "store"
          ]
        },
        "description": "Must return api_version \"1\" and capabilities. A wrong, revoked or rotated key must return 401 invalid_token."
      }
    },
    {
      "name": "Products — first page",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/products?limit=50",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "products"
          ],
          "query": [
            {
              "key": "limit",
              "value": "50"
            }
          ]
        },
        "description": "Cursor pagination: repeat with cursor=<next_cursor> until next_cursor is null."
      }
    },
    {
      "name": "Products — incremental",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/products?limit=50&updated_since=2026-01-01T00:00:00.000Z",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "products"
          ],
          "query": [
            {
              "key": "limit",
              "value": "50"
            },
            {
              "key": "updated_since",
              "value": "2026-01-01T00:00:00.000Z"
            }
          ]
        },
        "description": ""
      }
    },
    {
      "name": "Product — by id",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/products/{{productId}}",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "products",
            "{{productId}}"
          ]
        },
        "description": ""
      }
    },
    {
      "name": "Categories",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/categories?limit=100",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "categories"
          ],
          "query": [
            {
              "key": "limit",
              "value": "100"
            }
          ]
        },
        "description": "Optional in API-key mode; may return an empty page."
      }
    },
    {
      "name": "Checkout — validate",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "application/json"
          }
        ],
        "url": {
          "raw": "{{baseUrl}}/checkout/validate",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "checkout",
            "validate"
          ]
        },
        "body": {
          "mode": "raw",
          "raw": "{\n  \"lines\": [\n    {\n      \"product_id\": \"{{productId}}\",\n      \"variant_id\": \"{{variantId}}\",\n      \"quantity\": 1\n    }\n  ],\n  \"shipping_address\": {\n    \"line1\": \"Test address\",\n    \"city\": \"Kathmandu\",\n    \"area\": \"Test\",\n    \"country\": \"NP\"\n  }\n}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        },
        "description": ""
      }
    },
    {
      "name": "Shipping — quote (optional: shipping.quotes)",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "application/json"
          }
        ],
        "url": {
          "raw": "{{baseUrl}}/shipping/quotes",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "shipping",
            "quotes"
          ]
        },
        "body": {
          "mode": "raw",
          "raw": "{\n  \"lines\": [\n    {\n      \"product_id\": \"{{productId}}\",\n      \"variant_id\": \"{{variantId}}\",\n      \"quantity\": 1\n    }\n  ],\n  \"destination\": {\n    \"line1\": \"Test address\",\n    \"city\": \"Kathmandu\",\n    \"area\": \"Test\",\n    \"country\": \"NP\"\n  }\n}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        },
        "description": "Only for stores that list shipping.quotes. Expect 200 {quote, problems}; the quote expires on its own. Send its id as shipping_quote_id and its shipping_minor in POST /orders. An undeliverable destination returns 422 delivery_unavailable. Add latitude and longitude together, or neither."
      }
    },
    {
      "name": "Order — create TEST order (idempotent)",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Idempotency-Key",
            "value": "{{idempotencyKey}}"
          },
          {
            "key": "Content-Type",
            "value": "application/json"
          }
        ],
        "url": {
          "raw": "{{baseUrl}}/orders",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "orders"
          ]
        },
        "body": {
          "mode": "raw",
          "raw": "{\n  \"external_reference\": \"ext_postman_{{$timestamp}}\",\n  \"lines\": [\n    {\n      \"product_id\": \"{{productId}}\",\n      \"variant_id\": \"{{variantId}}\",\n      \"quantity\": 1,\n      \"unit_price_minor\": 285000\n    }\n  ],\n  \"currency\": \"NPR\",\n  \"expected_subtotal_minor\": 285000,\n  \"shipping_minor\": 15000,\n  \"discount_minor\": 0,\n  \"tax_minor\": 0,\n  \"expected_total_minor\": 300000,\n  \"customer\": {\n    \"name\": \"Setu Conformance Test\",\n    \"phone\": \"+9779800000000\",\n    \"email\": null\n  },\n  \"shipping_address\": {\n    \"line1\": \"Test address, do not ship\",\n    \"city\": \"Kathmandu\",\n    \"area\": \"Test\",\n    \"country\": \"NP\"\n  },\n  \"payment\": {\n    \"method\": \"cash_on_delivery\"\n  },\n  \"channel\": \"panel\",\n  \"note\": \"Test order from the Setu Postman collection\",\n  \"source\": {\n    \"platform\": \"setu\",\n    \"channel\": \"panel\",\n    \"conversation_ref\": null,\n    \"setu_order_ref\": \"ext_postman_{{$timestamp}}\"\n  },\n  \"test\": true\n}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        },
        "description": "Send twice with the same Idempotency-Key and body: the second response must be identical with Idempotent-Replayed: true. Adjust unit_price_minor and totals to your live price first."
      }
    },
    {
      "name": "Order — by id",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/orders/{{orderId}}",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "orders",
            "{{orderId}}"
          ]
        },
        "description": ""
      }
    },
    {
      "name": "Order — by number (optional: orders.lookup_by_number)",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/orders/{{orderNumber}}",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "orders",
            "{{orderNumber}}"
          ]
        },
        "description": "Only for stores that list orders.lookup_by_number. GET /orders/{id} also accepts the customer-facing order number. URL-encode numbers that contain special characters."
      }
    },
    {
      "name": "Payment session — create (optional: payments.sessions)",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Idempotency-Key",
            "value": "{{$guid}}"
          },
          {
            "key": "Content-Type",
            "value": "application/json"
          }
        ],
        "url": {
          "raw": "{{baseUrl}}/orders/{{orderId}}/payment-sessions",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "orders",
            "{{orderId}}",
            "payment-sessions"
          ]
        },
        "body": {
          "mode": "raw",
          "raw": "{}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        },
        "description": "Only for stores that list payments.sessions. Use an order created with an online payment option. Body {} uses the order's method, or send {\"method\": \"<online option id>\"}. Expect 201 {payment_session}. A cash-on-delivery or paid order returns 409 payment_not_allowed. With a test order the store must not contact a real gateway. Copy payment_session.id into paymentSessionId. Postman makes a new Idempotency-Key per send; while a session is pending the store must return that same session."
      }
    },
    {
      "name": "Payment session — by id (optional: payments.sessions)",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/payment-sessions/{{paymentSessionId}}",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "payment-sessions",
            "{{paymentSessionId}}"
          ]
        },
        "description": "Re-checks the gateway and returns the current status: pending, paid, failed, expired or cancelled."
      }
    },
    {
      "name": "Order action — cancel (optional: orders.cancel)",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Idempotency-Key",
            "value": "{{$guid}}"
          },
          {
            "key": "Content-Type",
            "value": "application/json"
          }
        ],
        "url": {
          "raw": "{{baseUrl}}/orders/{{orderId}}/cancel",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "orders",
            "{{orderId}}",
            "cancel"
          ]
        },
        "body": {
          "mode": "raw",
          "raw": "{\n  \"expected_status\": \"pending\",\n  \"reason\": \"Conformance test\"\n}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        },
        "description": "Only for stores that list orders.cancel. Expect 200 {order} with status cancelled and the store's own restock. A paid, shipped, delivered or already cancelled order returns 409 invalid_transition. Requires Idempotency-Key; sending again with the SAME key must replay the answer with Idempotent-Replayed: true and no second side effect (Postman makes a new key per send, so set a fixed value to test replay). A stale expected_status or expected_updated_at returns 409 order_changed. Use a TEST order only."
      }
    },
    {
      "name": "Order action — fulfil (optional: orders.fulfil)",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Idempotency-Key",
            "value": "{{$guid}}"
          },
          {
            "key": "Content-Type",
            "value": "application/json"
          }
        ],
        "url": {
          "raw": "{{baseUrl}}/orders/{{orderId}}/fulfil",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "orders",
            "{{orderId}}",
            "fulfil"
          ]
        },
        "body": {
          "mode": "raw",
          "raw": "{}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        },
        "description": "Only for stores that list orders.fulfil. Expect 200 {order}, usually status shipped. Allowed only for a confirmed or processing order that is cash on delivery or paid; otherwise 409 invalid_transition. A reason field returns 400 invalid_request. Requires Idempotency-Key; sending again with the SAME key must replay the answer with Idempotent-Replayed: true and no second side effect (Postman makes a new key per send, so set a fixed value to test replay). A stale expected_status or expected_updated_at returns 409 order_changed. Use a TEST order only."
      }
    },
    {
      "name": "Order action — record cash payment (optional: orders.cash_payment)",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Idempotency-Key",
            "value": "{{$guid}}"
          },
          {
            "key": "Content-Type",
            "value": "application/json"
          }
        ],
        "url": {
          "raw": "{{baseUrl}}/orders/{{orderId}}/cash-payment",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "orders",
            "{{orderId}}",
            "cash-payment"
          ]
        },
        "body": {
          "mode": "raw",
          "raw": "{}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        },
        "description": "Only for stores that list orders.cash_payment. Expect 200 {order} with payment_status paid. An online-payment order must return 409 payment_method_mismatch; an already paid or cancelled order returns 409 invalid_transition. Requires Idempotency-Key; sending again with the SAME key must replay the answer with Idempotent-Replayed: true and no second side effect (Postman makes a new key per send, so set a fixed value to test replay). A stale expected_status or expected_updated_at returns 409 order_changed. Use a TEST order only."
      }
    },
    {
      "name": "Negative — invalid key returns 401",
      "request": {
        "method": "GET",
        "auth": {
          "type": "bearer",
          "bearer": [
            {
              "key": "token",
              "value": "sk_setu_invalid_example_key",
              "type": "string"
            }
          ]
        },
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/store",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "store"
          ]
        },
        "description": "Expect 401 {\"error\":{\"code\":\"invalid_token\",…}}."
      }
    }
  ]
}
