{
  "openapi": "3.0.3",
  "info": {
    "title": "EQUINOX CRM API",
    "version": "16.22.0",
    "description": "Cloudflare Workers + D1-backed CRM for property/visa/education businesses.",
    "contact": {
      "name": "EQUINOX",
      "email": "support@equinox-proptech.com"
    }
  },
  "servers": [
    {
      "url": "https://crm.equinox-server.com"
    }
  ],
  "paths": {
    "/api/health": {
      "get": {
        "summary": "Health check",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/auth": {
      "post": {
        "summary": "Login (email + password) → returns sessionToken",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  }
                },
                "required": [
                  "email",
                  "password"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      }
    },
    "/api/auth/logout": {
      "post": {
        "summary": "Logout",
        "security": [
          {
            "sessionToken": []
          }
        ]
      }
    },
    "/api/call": {
      "post": {
        "summary": "Generic D1 handler dispatch",
        "security": [
          {
            "sessionToken": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "func": {
                    "type": "string"
                  },
                  "args": {
                    "type": "object"
                  },
                  "_sessionToken": {
                    "type": "string"
                  }
                },
                "required": [
                  "func"
                ]
              }
            }
          }
        }
      }
    },
    "/api/ai": {
      "post": {
        "summary": "AI assistant",
        "security": [
          {
            "sessionToken": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "task": {
                    "type": "string",
                    "enum": [
                      "chat",
                      "summarize",
                      "draft_email",
                      "extract_actions",
                      "ocr_passport",
                      "parse_lead"
                    ]
                  },
                  "data": {
                    "type": "object"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/ingest/email": {
      "post": {
        "summary": "Inbound email ingest",
        "security": [
          {
            "ingestKey": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "from": {
                    "type": "string"
                  },
                  "subject": {
                    "type": "string"
                  },
                  "snippet": {
                    "type": "string"
                  },
                  "date": {
                    "type": "string"
                  }
                },
                "required": [
                  "from"
                ]
              }
            }
          }
        }
      }
    },
    "/api/ingest/whatsapp": {
      "post": {
        "summary": "WhatsApp message ingest",
        "security": [
          {
            "ingestKey": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "from": {
                    "type": "string"
                  },
                  "body": {
                    "type": "string"
                  },
                  "direction": {
                    "type": "string",
                    "enum": [
                      "inbound",
                      "outbound"
                    ]
                  }
                },
                "required": [
                  "from"
                ]
              }
            }
          }
        }
      }
    },
    "/api/ingest/line": {
      "post": {
        "summary": "LINE Messaging API webhook",
        "security": [
          {
            "ingestKey": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "events": {
                    "type": "array"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/portal/api/login": {
      "post": {
        "summary": "Customer portal login"
      }
    },
    "/partner/api/login": {
      "post": {
        "summary": "Partner portal login"
      }
    },
    "/api/rest/me": {
      "get": {
        "tags": [
          "REST"
        ],
        "security": [
          {
            "sessionToken": []
          }
        ],
        "summary": "Current user info"
      }
    },
    "/api/rest/customers": {
      "get": {
        "tags": [
          "REST"
        ],
        "security": [
          {
            "sessionToken": []
          }
        ],
        "summary": "List customers",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/api/rest/customers/{id}": {
      "get": {
        "tags": [
          "REST"
        ],
        "security": [
          {
            "sessionToken": []
          }
        ],
        "summary": "Get customer details",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/api/rest/leads": {
      "get": {
        "tags": [
          "REST"
        ],
        "security": [
          {
            "sessionToken": []
          }
        ],
        "summary": "List leads"
      }
    },
    "/api/rest/units": {
      "get": {
        "tags": [
          "REST"
        ],
        "security": [
          {
            "sessionToken": []
          }
        ],
        "summary": "List unit listings",
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "rent",
                "sale"
              ]
            }
          },
          {
            "name": "project",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/api/rest/properties": {
      "get": {
        "tags": [
          "REST"
        ],
        "security": [
          {
            "sessionToken": []
          }
        ],
        "summary": "List properties"
      }
    },
    "/api/rest/bookings": {
      "get": {
        "tags": [
          "REST"
        ],
        "security": [
          {
            "sessionToken": []
          }
        ],
        "summary": "List booking requests"
      }
    },
    "/api/public/units": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Public unit listings (no auth)",
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "project",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "bedrooms",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "priceMax",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          }
        ]
      }
    },
    "/api/public/blog": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Published blog posts",
        "parameters": [
          {
            "name": "lang",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tag",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/api/booking/request": {
      "post": {
        "tags": [
          "Public"
        ],
        "summary": "Submit booking request",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string"
                  },
                  "phone": {
                    "type": "string"
                  },
                  "preferredAt": {
                    "type": "string"
                  },
                  "unitSlug": {
                    "type": "string"
                  },
                  "notes": {
                    "type": "string"
                  }
                },
                "required": [
                  "preferredAt"
                ]
              }
            }
          }
        }
      }
    },
    "/api/auth/tos": {
      "get": {
        "tags": [
          "Ops"
        ],
        "summary": "Current TOS version + acceptance status"
      },
      "post": {
        "tags": [
          "Ops"
        ],
        "security": [
          {
            "sessionToken": []
          }
        ],
        "summary": "Accept current TOS"
      }
    },
    "/api/chat/ws": {
      "get": {
        "tags": [
          "Realtime"
        ],
        "summary": "Staff chat WebSocket (DurableObject)",
        "parameters": [
          {
            "name": "thread",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "token",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/api/presence/ws": {
      "get": {
        "tags": [
          "Realtime"
        ],
        "summary": "Presence WebSocket"
      }
    },
    "/api/rag/search": {
      "post": {
        "tags": [
          "AI"
        ],
        "summary": "Vector search",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "query": {
                    "type": "string"
                  },
                  "topK": {
                    "type": "integer"
                  },
                  "kind": {
                    "type": "string"
                  }
                },
                "required": [
                  "query"
                ]
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "sessionToken": {
        "type": "http",
        "scheme": "bearer",
        "description": "Session token from /api/auth (POST). Used as _sessionToken in /api/call body OR x-session-token header on REST."
      },
      "ingestKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-equinox-key",
        "description": "Shared secret for /api/ingest/* endpoints."
      }
    },
    "schemas": {
      "Customer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "fullName": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "owner": {
            "type": "object"
          }
        }
      },
      "Lead": {
        "type": "object",
        "properties": {
          "lead_id": {
            "type": "string"
          },
          "lead_name": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "source": {
            "type": "string"
          }
        }
      },
      "Unit": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "project": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "price": {
            "type": "number"
          },
          "bedrooms": {
            "type": "integer"
          }
        }
      },
      "Booking": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "preferred_at": {
            "type": "string"
          },
          "status": {
            "type": "string"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        }
      }
    }
  },
  "x-handler-list": "POST /api/call?func=getInitData ... See /docs for the full handler list."
}