{
  "openapi": "3.1.0",
  "info": {
    "title": "Farmsell Logistics API",
    "description": "Backend for the Farmsell Logistics platform: five role workspaces over one shared logistics lifecycle.",
    "version": "1.0.0"
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      }
    },
    "schemas": {}
  },
  "paths": {
    "/auth/login": {
      "post": {
        "summary": "Sign in",
        "tags": [
          "auth"
        ],
        "description": "Accounts are provisioned by administrators; there is no self-service registration.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "phone": {
                    "type": "string",
                    "minLength": 9,
                    "maxLength": 15
                  },
                  "password": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "surface": {
                    "type": "string",
                    "enum": [
                      "mobile",
                      "admin",
                      "cms",
                      "market"
                    ]
                  }
                },
                "required": [
                  "phone",
                  "password",
                  "surface"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "fullName": {
                          "type": "string"
                        },
                        "phone": {
                          "type": "string"
                        },
                        "roles": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "capabilities": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "farmer.read",
                              "farmer.manage",
                              "crop.read",
                              "crop.manage",
                              "batch.read",
                              "batch.create",
                              "batch.verify",
                              "batch.reject",
                              "package.create",
                              "inventory.manage",
                              "hub.read",
                              "hub.manage",
                              "hub.staff.manage",
                              "load.read",
                              "load.assign",
                              "trip.accept",
                              "pickup.confirm",
                              "handoff.confirm",
                              "delivery.manage",
                              "fleet.manage",
                              "vehicle.manage",
                              "driver.manage",
                              "catalog.browse",
                              "product.read",
                              "product.manage",
                              "pricing.manage",
                              "order.read",
                              "order.create",
                              "order.fulfil",
                              "order.cancel",
                              "basket.manage",
                              "groupbuy.join",
                              "groupbuy.manage",
                              "payment.read",
                              "payment.record",
                              "payout.approve",
                              "invoice.manage",
                              "ledger.read",
                              "earnings.read",
                              "content.read",
                              "content.author",
                              "content.publish",
                              "media.manage",
                              "newsletter.manage",
                              "application.review",
                              "issue.read",
                              "issue.manage",
                              "user.read",
                              "user.provision",
                              "user.suspend",
                              "role.manage",
                              "organization.manage",
                              "settings.manage",
                              "report.read",
                              "audit.read"
                            ]
                          }
                        },
                        "hubIds": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "organizationIds": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "mustUpdateCredentials": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "id",
                        "fullName",
                        "phone",
                        "roles",
                        "capabilities",
                        "hubIds",
                        "organizationIds",
                        "mustUpdateCredentials"
                      ],
                      "additionalProperties": false
                    },
                    "accessToken": {
                      "type": "string"
                    },
                    "refreshToken": {
                      "type": "string"
                    },
                    "expiresIn": {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991
                    }
                  },
                  "required": [
                    "user",
                    "accessToken",
                    "refreshToken",
                    "expiresIn"
                  ],
                  "additionalProperties": false
                }
              }
            }
          }
        }
      }
    },
    "/auth/credentials": {
      "post": {
        "summary": "Replace a temporary passcode with a chosen password",
        "tags": [
          "auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "currentPassword": {
                    "type": "string",
                    "minLength": 1
                  },
                  "newPassword": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 200,
                    "pattern": "[0-9]"
                  }
                },
                "required": [
                  "currentPassword",
                  "newPassword"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "fullName": {
                          "type": "string"
                        },
                        "phone": {
                          "type": "string"
                        },
                        "roles": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "capabilities": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "farmer.read",
                              "farmer.manage",
                              "crop.read",
                              "crop.manage",
                              "batch.read",
                              "batch.create",
                              "batch.verify",
                              "batch.reject",
                              "package.create",
                              "inventory.manage",
                              "hub.read",
                              "hub.manage",
                              "hub.staff.manage",
                              "load.read",
                              "load.assign",
                              "trip.accept",
                              "pickup.confirm",
                              "handoff.confirm",
                              "delivery.manage",
                              "fleet.manage",
                              "vehicle.manage",
                              "driver.manage",
                              "catalog.browse",
                              "product.read",
                              "product.manage",
                              "pricing.manage",
                              "order.read",
                              "order.create",
                              "order.fulfil",
                              "order.cancel",
                              "basket.manage",
                              "groupbuy.join",
                              "groupbuy.manage",
                              "payment.read",
                              "payment.record",
                              "payout.approve",
                              "invoice.manage",
                              "ledger.read",
                              "earnings.read",
                              "content.read",
                              "content.author",
                              "content.publish",
                              "media.manage",
                              "newsletter.manage",
                              "application.review",
                              "issue.read",
                              "issue.manage",
                              "user.read",
                              "user.provision",
                              "user.suspend",
                              "role.manage",
                              "organization.manage",
                              "settings.manage",
                              "report.read",
                              "audit.read"
                            ]
                          }
                        },
                        "hubIds": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "organizationIds": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "mustUpdateCredentials": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "id",
                        "fullName",
                        "phone",
                        "roles",
                        "capabilities",
                        "hubIds",
                        "organizationIds",
                        "mustUpdateCredentials"
                      ],
                      "additionalProperties": false
                    },
                    "accessToken": {
                      "type": "string"
                    },
                    "refreshToken": {
                      "type": "string"
                    },
                    "expiresIn": {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991
                    }
                  },
                  "required": [
                    "user",
                    "accessToken",
                    "refreshToken",
                    "expiresIn"
                  ],
                  "additionalProperties": false
                }
              }
            }
          }
        }
      }
    },
    "/auth/refresh": {
      "post": {
        "summary": "Exchange a refresh token for a new session",
        "tags": [
          "auth"
        ],
        "description": "Refresh tokens rotate on use; replaying one revokes the whole family.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "refreshToken": {
                    "type": "string",
                    "minLength": 20
                  },
                  "surface": {
                    "type": "string",
                    "enum": [
                      "mobile",
                      "admin",
                      "cms",
                      "market"
                    ]
                  }
                },
                "required": [
                  "refreshToken",
                  "surface"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "fullName": {
                          "type": "string"
                        },
                        "phone": {
                          "type": "string"
                        },
                        "roles": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "capabilities": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "farmer.read",
                              "farmer.manage",
                              "crop.read",
                              "crop.manage",
                              "batch.read",
                              "batch.create",
                              "batch.verify",
                              "batch.reject",
                              "package.create",
                              "inventory.manage",
                              "hub.read",
                              "hub.manage",
                              "hub.staff.manage",
                              "load.read",
                              "load.assign",
                              "trip.accept",
                              "pickup.confirm",
                              "handoff.confirm",
                              "delivery.manage",
                              "fleet.manage",
                              "vehicle.manage",
                              "driver.manage",
                              "catalog.browse",
                              "product.read",
                              "product.manage",
                              "pricing.manage",
                              "order.read",
                              "order.create",
                              "order.fulfil",
                              "order.cancel",
                              "basket.manage",
                              "groupbuy.join",
                              "groupbuy.manage",
                              "payment.read",
                              "payment.record",
                              "payout.approve",
                              "invoice.manage",
                              "ledger.read",
                              "earnings.read",
                              "content.read",
                              "content.author",
                              "content.publish",
                              "media.manage",
                              "newsletter.manage",
                              "application.review",
                              "issue.read",
                              "issue.manage",
                              "user.read",
                              "user.provision",
                              "user.suspend",
                              "role.manage",
                              "organization.manage",
                              "settings.manage",
                              "report.read",
                              "audit.read"
                            ]
                          }
                        },
                        "hubIds": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "organizationIds": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "mustUpdateCredentials": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "id",
                        "fullName",
                        "phone",
                        "roles",
                        "capabilities",
                        "hubIds",
                        "organizationIds",
                        "mustUpdateCredentials"
                      ],
                      "additionalProperties": false
                    },
                    "accessToken": {
                      "type": "string"
                    },
                    "refreshToken": {
                      "type": "string"
                    },
                    "expiresIn": {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991
                    }
                  },
                  "required": [
                    "user",
                    "accessToken",
                    "refreshToken",
                    "expiresIn"
                  ],
                  "additionalProperties": false
                }
              }
            }
          }
        }
      }
    },
    "/auth/logout": {
      "post": {
        "summary": "Revoke the current session family",
        "tags": [
          "auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "refreshToken": {
                    "type": "string",
                    "minLength": 20
                  },
                  "surface": {
                    "type": "string",
                    "enum": [
                      "mobile",
                      "admin",
                      "cms",
                      "market"
                    ]
                  }
                },
                "required": [
                  "refreshToken",
                  "surface"
                ]
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Default Response"
          }
        }
      }
    },
    "/batches": {
      "get": {
        "summary": "List batches",
        "tags": [
          "batches"
        ],
        "description": "Cursor-paginated. Results are scoped to what the caller may see: aggregators get their own batches, hub operators get their hubs.",
        "parameters": [
          {
            "schema": {
              "default": 25,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "in": "query",
            "name": "limit",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "cursor",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "ready-for-pickup",
                "in-transit-to-hub",
                "at-hub-verification",
                "verified",
                "rejected",
                "needs-review",
                "packaged",
                "available-for-order",
                "allocated"
              ]
            },
            "in": "query",
            "name": "status",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^hub_.*"
            },
            "in": "query",
            "name": "hubId",
            "required": false
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "crop": {
                            "type": "string"
                          },
                          "variety": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "quantityGrams": {
                            "type": "integer",
                            "minimum": -9007199254740991,
                            "maximum": 9007199254740991
                          },
                          "grade": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "draft",
                              "ready-for-pickup",
                              "in-transit-to-hub",
                              "at-hub-verification",
                              "verified",
                              "rejected",
                              "needs-review",
                              "packaged",
                              "available-for-order",
                              "allocated"
                            ]
                          },
                          "aggregatorId": {
                            "type": "string"
                          },
                          "originName": {
                            "type": "string"
                          },
                          "destinationHubId": {
                            "type": "string"
                          },
                          "readyAt": {
                            "anyOf": [
                              {
                                "type": "string",
                                "format": "date-time"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "verifiedAt": {
                            "anyOf": [
                              {
                                "type": "string",
                                "format": "date-time"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updatedAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        },
                        "required": [
                          "id",
                          "crop",
                          "variety",
                          "quantityGrams",
                          "grade",
                          "status",
                          "aggregatorId",
                          "originName",
                          "destinationHubId",
                          "readyAt",
                          "verifiedAt",
                          "createdAt",
                          "updatedAt"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "additionalProperties": false
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a batch",
        "tags": [
          "batches"
        ],
        "description": "Requires an Idempotency-Key header between 8 and 128 characters. A repeat with the same key and body replays the stored response instead of creating a second batch.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "crop": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "variety": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "quantityGrams": {
                    "type": "integer",
                    "exclusiveMinimum": 0,
                    "maximum": 100000000
                  },
                  "grade": {
                    "type": "string",
                    "enum": [
                      "A",
                      "B",
                      "C"
                    ]
                  },
                  "originName": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "destinationHubId": {
                    "type": "string",
                    "pattern": "^hub_.*"
                  }
                },
                "required": [
                  "crop",
                  "quantityGrams",
                  "grade",
                  "originName",
                  "destinationHubId"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "crop": {
                      "type": "string"
                    },
                    "variety": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "quantityGrams": {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991
                    },
                    "grade": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "draft",
                        "ready-for-pickup",
                        "in-transit-to-hub",
                        "at-hub-verification",
                        "verified",
                        "rejected",
                        "needs-review",
                        "packaged",
                        "available-for-order",
                        "allocated"
                      ]
                    },
                    "aggregatorId": {
                      "type": "string"
                    },
                    "originName": {
                      "type": "string"
                    },
                    "destinationHubId": {
                      "type": "string"
                    },
                    "readyAt": {
                      "anyOf": [
                        {
                          "type": "string",
                          "format": "date-time"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "verifiedAt": {
                      "anyOf": [
                        {
                          "type": "string",
                          "format": "date-time"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  },
                  "required": [
                    "id",
                    "crop",
                    "variety",
                    "quantityGrams",
                    "grade",
                    "status",
                    "aggregatorId",
                    "originName",
                    "destinationHubId",
                    "readyAt",
                    "verifiedAt",
                    "createdAt",
                    "updatedAt"
                  ],
                  "additionalProperties": false
                }
              }
            }
          }
        }
      }
    },
    "/batches/{batchId}": {
      "get": {
        "summary": "Get one batch",
        "tags": [
          "batches"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^btc_.*"
            },
            "in": "path",
            "name": "batchId",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "crop": {
                      "type": "string"
                    },
                    "variety": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "quantityGrams": {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991
                    },
                    "grade": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "draft",
                        "ready-for-pickup",
                        "in-transit-to-hub",
                        "at-hub-verification",
                        "verified",
                        "rejected",
                        "needs-review",
                        "packaged",
                        "available-for-order",
                        "allocated"
                      ]
                    },
                    "aggregatorId": {
                      "type": "string"
                    },
                    "originName": {
                      "type": "string"
                    },
                    "destinationHubId": {
                      "type": "string"
                    },
                    "readyAt": {
                      "anyOf": [
                        {
                          "type": "string",
                          "format": "date-time"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "verifiedAt": {
                      "anyOf": [
                        {
                          "type": "string",
                          "format": "date-time"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  },
                  "required": [
                    "id",
                    "crop",
                    "variety",
                    "quantityGrams",
                    "grade",
                    "status",
                    "aggregatorId",
                    "originName",
                    "destinationHubId",
                    "readyAt",
                    "verifiedAt",
                    "createdAt",
                    "updatedAt"
                  ],
                  "additionalProperties": false
                }
              }
            }
          }
        }
      }
    },
    "/batches/{batchId}/transitions": {
      "post": {
        "summary": "Move a batch to a new status",
        "tags": [
          "batches"
        ],
        "description": "Requires an Idempotency-Key header. Rejects transitions the batch lifecycle does not allow.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "enum": [
                      "draft",
                      "ready-for-pickup",
                      "in-transit-to-hub",
                      "at-hub-verification",
                      "verified",
                      "rejected",
                      "needs-review",
                      "packaged",
                      "available-for-order",
                      "allocated"
                    ]
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 500
                  }
                },
                "required": [
                  "to"
                ]
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^btc_.*"
            },
            "in": "path",
            "name": "batchId",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "crop": {
                      "type": "string"
                    },
                    "variety": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "quantityGrams": {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991
                    },
                    "grade": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "draft",
                        "ready-for-pickup",
                        "in-transit-to-hub",
                        "at-hub-verification",
                        "verified",
                        "rejected",
                        "needs-review",
                        "packaged",
                        "available-for-order",
                        "allocated"
                      ]
                    },
                    "aggregatorId": {
                      "type": "string"
                    },
                    "originName": {
                      "type": "string"
                    },
                    "destinationHubId": {
                      "type": "string"
                    },
                    "readyAt": {
                      "anyOf": [
                        {
                          "type": "string",
                          "format": "date-time"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "verifiedAt": {
                      "anyOf": [
                        {
                          "type": "string",
                          "format": "date-time"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  },
                  "required": [
                    "id",
                    "crop",
                    "variety",
                    "quantityGrams",
                    "grade",
                    "status",
                    "aggregatorId",
                    "originName",
                    "destinationHubId",
                    "readyAt",
                    "verifiedAt",
                    "createdAt",
                    "updatedAt"
                  ],
                  "additionalProperties": false
                }
              }
            }
          }
        }
      }
    }
  },
  "servers": [
    {
      "url": "/api/v1"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "auth",
      "description": "Sign in and credential rotation"
    },
    {
      "name": "batches",
      "description": "Batch lifecycle and custody"
    },
    {
      "name": "hubs",
      "description": "Collection hubs"
    },
    {
      "name": "health",
      "description": "Liveness and readiness"
    }
  ]
}
