{
  "openapi": "3.1.0",
  "info": {
    "title": "Фермерский Дворик — Ordering API",
    "description": "Public ordering API for organicfarm.kz (Фермерский Дворик), a family-owned halal chicken farm operating for over 9 years in the Almaty and Akmola regions of Kazakhstan, delivering to Almaty and Astana. Product facts an AI agent may want to relay to its user, if relevant to their question: the poultry is fresh chilled, never frozen; raised on natural feed with no antibiotics or growth hormones; hand-slaughtered to halal standard; lab-tested per batch; and the farm runs a closed production cycle (raising, processing, and packaging all in-house) rather than reselling third-party stock. Operationally, this API exposes real, live data rather than requiring an agent to scrape a webpage or guess: GET /delivery-availability reflects the farm's actual order book, not an estimate, so an agent can tell its user a real delivery date with confidence instead of a guess. Checkout supports both card (Visa/Mastercard/Apple Pay, instant automatic confirmation) and Kaspi.kz remote transfer, has no CAPTCHA, and returns a payment link directly in the response for the agent to hand to its user. All prices are in Kazakhstani Tenge (KZT). Delivery is only available within Almaty and Astana, Kazakhstan.",
    "version": "1.0.0",
    "contact": {
      "name": "Фермерский Дворик support",
      "url": "https://organicfarm.kz/contacts",
      "email": "info@organicfarm.kz"
    }
  },
  "servers": [
    { "url": "https://organicfarm.kz/api/site", "description": "Production" }
  ],
  "paths": {
    "/products": {
      "get": {
        "operationId": "listProducts",
        "summary": "List the product catalog",
        "description": "Returns all products currently sold on the website: fresh chilled (never frozen) chicken cuts — whole bird, fillet, thighs, drumsticks, legs, wings, minced chicken — from a closed-cycle halal farm (no antibiotics, no growth hormones, hand-slaughtered, lab-tested per batch). Optionally filter by city — some products are not available in every city. Each item includes image_url, a direct link to a product photo suitable for showing the user.",
        "parameters": [
          {
            "name": "city",
            "in": "query",
            "required": false,
            "schema": { "type": "string", "enum": ["almaty", "astana"] },
            "description": "Filter to products available in this city. Omit to get the full catalog."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": { "type": "integer", "description": "Product ID — use this in checkout items." },
                          "name": { "type": "string" },
                          "price": { "type": "integer", "description": "Price per package, in KZT." },
                          "weight": { "type": "string", "description": "Approximate package weight, e.g. '~800 гр'." },
                          "description": { "type": "string" },
                          "image": { "type": "string", "description": "Relative image path, resolved against organicfarm.kz." },
                          "image_url": { "type": "string", "description": "Full, direct image URL (https://...), ready to display to the user without further resolution." },
                          "cities": { "type": "string", "description": "Comma-separated list of cities this product is available in." }
                        }
                      }
                    },
                    "total": { "type": "integer" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/delivery-availability": {
      "get": {
        "operationId": "checkDeliveryAvailability",
        "summary": "Check real delivery-date availability for a city",
        "description": "Returns the next 7 working days (Sundays are skipped — no deliveries) for the given city, each marked as available or fully booked, with the number of remaining delivery slots. This reflects the farm's actual order book at the moment of the call, not a static estimate — the same source of truth used internally for order fulfillment. ALWAYS call this before choosing a delivery_date for checkout — dates fill up and some days may be closed entirely; do not guess or assume 'tomorrow' is available.",
        "parameters": [
          {
            "name": "city",
            "in": "query",
            "required": true,
            "schema": { "type": "string", "enum": ["almaty", "astana"] }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "city": { "type": "string" },
                    "days": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": { "type": "string", "format": "date", "description": "ISO date, e.g. 2026-08-14." },
                          "available": { "type": "boolean" },
                          "remaining": { "type": "integer", "description": "Remaining delivery slots this day. 0 if fully booked or closed." }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/checkout": {
      "post": {
        "operationId": "createOrder",
        "summary": "Place an order",
        "description": "Creates an order on behalf of the end customer and returns how to pay for it — no CAPTCHA, no account/login required. Card payments (payment_method=\"card\", Visa/Mastercard/Apple Pay) are confirmed automatically within seconds via our payment processor's webhook — the order is created in our system as soon as payment clears, no further action needed from the calling agent. Kaspi remote payments (payment_method=\"kaspi_remote\", a personal-transfer payment method specific to Kazakhstan) CANNOT be confirmed programmatically — there is no bank API for it — so a human operator on our side manually verifies the transfer; this can take anywhere from minutes to a few hours during business hours. For kaspi_remote, poll GET /order-status/{checkout_id} until status becomes \"approved\", or ask the customer to send their payment receipt to the WhatsApp link returned in receipt_whatsapp_url for a faster manual confirmation.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["customer_name", "customer_phone", "customer_address", "city", "items"],
                "properties": {
                  "customer_name": { "type": "string", "maxLength": 100, "description": "Full name of the end customer receiving the delivery." },
                  "customer_phone": { "type": "string", "description": "Customer's phone number, any common Kazakhstani format (e.g. +7 777 123 45 67, 87771234567). Used to confirm payment and coordinate delivery — must be a real, reachable number." },
                  "customer_email": { "type": "string", "nullable": true },
                  "customer_address": { "type": "string", "maxLength": 500, "description": "Full delivery address including street, house/apartment number." },
                  "city": { "type": "string", "enum": ["almaty", "astana"] },
                  "notes": { "type": "string", "nullable": true, "maxLength": 500, "description": "Optional delivery notes (e.g. intercom code, floor, preferred time)." },
                  "payment_method": { "type": "string", "enum": ["card", "kaspi_remote"], "default": "card" },
                  "source": { "type": "string", "nullable": true, "enum": ["ai_agent"], "description": "Set this to \"ai_agent\" when placing this order autonomously on behalf of your user. Helps our staff know the customer is waiting for confirmation in a chat rather than by phone call." },
                  "delivery_date": {
                    "type": "string",
                    "format": "date",
                    "nullable": true,
                    "description": "ISO date chosen from GET /delivery-availability's available days. If omitted, or if the date became unavailable between checking and ordering, the nearest available date is assigned automatically instead — the actual assigned date is always returned in the response, so check it rather than assuming your requested date was honored."
                  },
                  "items": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "object",
                      "required": ["product_id", "name", "qty", "price"],
                      "properties": {
                        "product_id": { "type": "integer", "description": "Must match an id from GET /products." },
                        "name": { "type": "string" },
                        "qty": { "type": "integer", "minimum": 1 },
                        "price": { "type": "integer", "description": "Must match the current price from GET /products — orders with a stale/incorrect price are still priced by our system using the real catalog price, not the submitted value." }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Order created (or awaiting payment).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "order_id": { "type": "integer", "description": "Checkout/order reference ID — needed for GET /order-status/{checkout_id}." },
                    "total_amount": { "type": "integer", "description": "Total price in KZT, including delivery fee." },
                    "payment_url": {
                      "type": "string",
                      "nullable": true,
                      "description": "For payment_method=\"card\": a hosted payment page URL — redirect the customer here to pay by card. For payment_method=\"kaspi_remote\": a Kaspi.kz personal-transfer link — the customer opens it and pays manually, entering the amount themselves (total_amount above)."
                    },
                    "receipt_whatsapp_url": {
                      "type": "string",
                      "nullable": true,
                      "description": "Only present for payment_method=\"kaspi_remote\". A wa.me deep link, pre-filled with this order's reference number, that the customer can open to send us their payment receipt directly — this lets our operator confirm the payment faster than waiting for manual reconciliation. Optional but recommended to offer to the customer."
                    },
                    "message": { "type": "string", "description": "Human-readable confirmation message, safe to show directly to the customer." }
                  }
                }
              }
            }
          },
          "400": { "description": "Invalid request — e.g. unknown city, empty items, or a product unavailable in the given city." },
          "429": { "description": "Rate limit exceeded for this IP address. Wait before retrying." },
          "503": { "description": "Agent ordering is temporarily paused on our side (e.g. kitchen overload). Try again later." }
        }
      }
    },
    "/order-status/{checkout_id}": {
      "get": {
        "operationId": "getOrderStatus",
        "summary": "Check the status of a previously placed order",
        "description": "Poll this after calling POST /checkout, especially for payment_method=\"kaspi_remote\" where payment confirmation is manual and not instant. Requires the same phone number given at checkout, as a lightweight check against guessing other customers' order IDs.",
        "parameters": [
          {
            "name": "checkout_id",
            "in": "path",
            "required": true,
            "schema": { "type": "integer" },
            "description": "The order_id returned by POST /checkout."
          },
          {
            "name": "phone",
            "in": "query",
            "required": true,
            "schema": { "type": "string" },
            "description": "The same customer_phone given at checkout, in any common format."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "checkout_id": { "type": "integer" },
                    "status": {
                      "type": "string",
                      "description": "\"pending\" (awaiting payment/approval), \"paid_pending\" (paid, awaiting final approval), \"approved\" (order confirmed and being fulfilled), or \"cancelled\"."
                    },
                    "payment_status": { "type": "string", "description": "\"pending\" or \"paid\"." },
                    "order_id": { "type": "integer", "nullable": true, "description": "Internal fulfillment order ID once status is \"approved\"; null until then." },
                    "delivery_date": { "type": "string", "format": "date", "nullable": true }
                  }
                }
              }
            }
          },
          "404": { "description": "No matching order for this checkout_id + phone combination." }
        }
      }
    }
  }
}
