Learn by Directing AI
All materials

api-contract.md

API Contract -- Cacao Ixchel

This document defines the REST API contract. The frontend builds against this contract. The backend implements it. Both sides agree on request and response shapes, status codes, and error formats.

1. Inventory Endpoints

GET /api/inventory

Returns all inventory items, optionally filtered by stage or farm.

Query parameters:

  • stage (optional): "fermenting" | "drying" | "roasting" | "finished"
  • farm (optional): farm ID

Response (200 OK):

{
  items: Array<{
    id: string;
    farm_name: string;
    farm_id: string;
    cacao_variety: string;
    stage: "fermenting" | "drying" | "roasting" | "finished";
    weight_kg: number;
    harvest_date: string; // ISO 8601
    product_count: number; // number of products from this batch
    available_count: number; // products not allocated to orders
  }>
}

GET /api/inventory/:id

Returns a single inventory item with batch traceability.

Response (200 OK):

{
  id: string;
  farm: { id: string; name: string; location: string; cacao_variety: string; };
  stage: string;
  weight_kg: number;
  harvest_date: string;
  flavour_profile: string | null;
  products: Array<{
    id: string;
    name: string;
    type: "bar" | "drinking_chocolate" | "nibs";
    quantity_available: number;
    allocated_to: string | null; // order customer name or null
  }>
}

Response (404 Not Found):

{ error: "Batch not found" }

PUT /api/inventory/:id

Updates the stage of a batch. Valid transitions: fermenting -> drying -> roasting -> finished. Reverse transitions are not allowed.

Request body:

{ stage: "fermenting" | "drying" | "roasting" | "finished" }

Response (200 OK):

{ id: string; stage: string; updated_at: string; }

Response (400 Bad Request):

{ error: "Invalid stage transition", current: string, requested: string }

2. Order Endpoints

POST /api/orders

Creates a new order with product allocation. Prevents double-allocation: if any requested product is already allocated to another order, the request fails.

Request body:

{
  customer_name: string; // required, non-empty, no whitespace-only
  country: string; // required
  products: Array<{ product_id: string; quantity: number; }> // quantity must be positive integer
}

Response (201 Created):

{
  id: string;
  customer_name: string;
  country: string;
  status: "confirmed";
  products: Array<{ id: string; name: string; }>;
  created_at: string;
}

Response (400 Bad Request):

{ error: string; field?: string; }

Examples: "Customer name is required" (field: "customer_name"), "Quantity must be a positive integer" (field: "products")

Response (409 Conflict):

{ error: "Product already allocated", product_id: string; allocated_to: string; }

GET /api/orders

Returns all orders, optionally filtered by status.

Query parameters:

  • status (optional): "confirmed" | "in_production" | "shipped" | "delivered"

Response (200 OK):

{
  orders: Array<{
    id: string;
    customer_name: string;
    country: string;
    status: "confirmed" | "in_production" | "shipped" | "delivered";
    product_count: number;
    created_at: string;
  }>
}

PUT /api/orders/:id/status

Updates an order's status.

Request body:

{ status: "confirmed" | "in_production" | "shipped" | "delivered" }

Response (200 OK):

{ id: string; status: string; updated_at: string; }

Response (404 Not Found):

{ error: "Order not found" }