openapi: "3.1.0"
info:
  title: Nexus API — Public Spec (Partial)
  description: |
    Public API specification for Nexus, a multi-tenant Ops OS.
    This is a partial specification documenting core read and stable write endpoints.
    All endpoints require authentication unless explicitly marked otherwise.
  version: "1.0.0"
  contact:
    name: AiForStartups
    email: info@aiforstartups.com
    url: https://nexus.aiforstartups.io
  license:
    name: Proprietary
    identifier: LicenseRef-Proprietary

servers:
  - url: https://{project_ref}.supabase.co
    description: Supabase Backend
    variables:
      project_ref:
        default: your-project-ref
        description: Supabase project reference (provided during onboarding)

security:
  - bearerAuth: []

tags:
  - name: Auth
    description: Authentication and session management
  - name: Contacts
    description: Contact (customer) management
  - name: Messages
    description: Send and receive messages across channels
  - name: Orders
    description: Order management and lifecycle
  - name: Inventory
    description: Stock, warehouse, and bin operations
  - name: Shipping
    description: AWB creation, tracking, and labels
  - name: VoIP
    description: Call initiation, CDR, and extensions
  - name: AI
    description: AI annotation, insights, and embeddings
  - name: Automation
    description: Workflow rules and execution
  - name: Integrations
    description: Integration setup and synchronization

paths:
  /rest/v1/customers:
    get:
      tags: [Contacts]
      operationId: listContacts
      summary: List contacts
      description: Returns a paginated list of contacts for the authenticated user's organization.
      parameters:
        - $ref: "#/components/parameters/RangeHeader"
        - $ref: "#/components/parameters/OrderParam"
        - name: phone_number
          in: query
          schema:
            type: string
          description: Filter by phone number (exact match)
        - name: email
          in: query
          schema:
            type: string
          description: Filter by email (exact match)
        - name: journey_stage
          in: query
          schema:
            type: string
            enum: [lead, prospect, customer, churned]
          description: Filter by journey stage
      responses:
        "200":
          description: List of contacts
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Contact"
              example:
                - id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  first_name: "Fatima"
                  last_name: "Al-Rashid"
                  phone_number: "+201234567890"
                  email: "fatima@example.com"
                  company: "Acme Trading"
                  city: "Cairo"
                  country: "Egypt"
                  journey_stage: "customer"
                  source: "shopify"
                  created_at: "2025-06-15T10:30:00Z"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /rest/v1/customers/{id}:
    get:
      tags: [Contacts]
      operationId: getContact
      summary: Get a single contact
      description: |
        Returns a single contact by ID. In PostgREST, use the query parameter
        `?id=eq.<uuid>` on the `/rest/v1/customers` endpoint. This path is a
        simplified representation for the OpenAPI spec.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Contact detail
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Contact"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /rest/v1/orders:
    get:
      tags: [Orders]
      operationId: listOrders
      summary: List orders
      description: Returns a paginated list of orders for the authenticated user's organization.
      parameters:
        - $ref: "#/components/parameters/RangeHeader"
        - $ref: "#/components/parameters/OrderParam"
        - name: status
          in: query
          schema:
            type: string
            enum: [pending, confirmed, processing, picked, packed, shipped, delivered, returned, cancelled]
          description: Filter by order status
        - name: customer_id
          in: query
          schema:
            type: string
            format: uuid
          description: Filter by customer ID
      responses:
        "200":
          description: List of orders
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Order"
              example:
                - id: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                  order_number: "ORD-20250615-001"
                  customer_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  status: "shipped"
                  total_amount: 450.00
                  currency: "EGP"
                  source: "shopify"
                  created_at: "2025-06-15T14:00:00Z"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /rest/v1/inventory_items:
    get:
      tags: [Inventory]
      operationId: listInventoryItems
      summary: List inventory items
      description: Returns a paginated list of inventory items.
      parameters:
        - $ref: "#/components/parameters/RangeHeader"
        - $ref: "#/components/parameters/OrderParam"
        - name: sku
          in: query
          schema:
            type: string
          description: Filter by SKU
        - name: category
          in: query
          schema:
            type: string
          description: Filter by category
      responses:
        "200":
          description: List of inventory items
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/InventoryItem"
              example:
                - id: "c3d4e5f6-a7b8-9012-cdef-123456789012"
                  name: "Premium Cotton T-Shirt (M)"
                  sku: "TSHIRT-COT-M-BLK"
                  barcode: "6281234567890"
                  quantity_on_hand: 142
                  reorder_point: 20
                  unit_cost: 85.00
                  category: "Apparel"
                  created_at: "2025-03-01T09:00:00Z"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /rest/v1/chats:
    get:
      tags: [Messages]
      operationId: listConversations
      summary: List conversations
      description: Returns a paginated list of conversations.
      parameters:
        - $ref: "#/components/parameters/RangeHeader"
        - name: channel
          in: query
          schema:
            type: string
            enum: [whatsapp, facebook, instagram, email, internal]
          description: Filter by channel
        - name: status
          in: query
          schema:
            type: string
            enum: [open, closed, pending]
          description: Filter by conversation status
      responses:
        "200":
          description: List of conversations
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Conversation"
              example:
                - id: "d4e5f6a7-b8c9-0123-defa-234567890123"
                  customer_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  channel: "whatsapp"
                  status: "open"
                  last_message_at: "2025-06-15T16:45:00Z"
                  unread_count: 2
        "401":
          $ref: "#/components/responses/Unauthorized"

  /rest/v1/messages:
    get:
      tags: [Messages]
      operationId: listMessages
      summary: List messages in a conversation
      description: Returns messages for a given conversation (chat_id).
      parameters:
        - $ref: "#/components/parameters/RangeHeader"
        - name: chat_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: Conversation ID
      responses:
        "200":
          description: List of messages
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Message"
              example:
                - id: "e5f6a7b8-c9d0-1234-efab-345678901234"
                  chat_id: "d4e5f6a7-b8c9-0123-defa-234567890123"
                  sender_type: "customer"
                  content: "Hello, I'd like to check on my order status."
                  message_type: "text"
                  direction: "inbound"
                  status: "delivered"
                  created_at: "2025-06-15T16:45:00Z"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /rest/v1/awbs:
    get:
      tags: [Shipping]
      operationId: listShipments
      summary: List shipments (AWBs)
      description: Returns a paginated list of AWBs.
      parameters:
        - $ref: "#/components/parameters/RangeHeader"
        - name: order_id
          in: query
          schema:
            type: string
            format: uuid
          description: Filter by order ID
        - name: status
          in: query
          schema:
            type: string
          description: Filter by shipment status
      responses:
        "200":
          description: List of AWBs
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/AWB"
              example:
                - id: "f6a7b8c9-d0e1-2345-fabc-456789012345"
                  order_id: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                  tracking_number: "BST-123456789"
                  courier: "bosta"
                  status: "in_transit"
                  created_at: "2025-06-16T08:00:00Z"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /rest/v1/call_logs:
    get:
      tags: [VoIP]
      operationId: listCallLogs
      summary: List call logs
      description: Returns a paginated list of call detail records.
      parameters:
        - $ref: "#/components/parameters/RangeHeader"
        - name: customer_id
          in: query
          schema:
            type: string
            format: uuid
          description: Filter by customer
      responses:
        "200":
          description: List of call logs
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/CallLog"
              example:
                - id: "a7b8c9d0-e1f2-3456-abcd-567890123456"
                  customer_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  direction: "outbound"
                  duration_seconds: 180
                  status: "answered"
                  extension: "1001"
                  created_at: "2025-06-15T11:00:00Z"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /functions/v1/whatsapp-send-message:
    post:
      tags: [Messages]
      operationId: sendWhatsAppMessage
      summary: Send a WhatsApp message
      description: |
        Sends a text or media message to a contact via WhatsApp Business API.
        **Auth required.** The sender's organization must have a connected WhatsApp number.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone_number, message]
              properties:
                phone_number:
                  type: string
                  description: Recipient phone number in E.164 format
                  example: "+201234567890"
                message:
                  type: string
                  description: Message text
                  example: "Hello! Your order ORD-20250615-001 has been shipped."
                media_url:
                  type: string
                  description: URL of media attachment (optional)
                media_type:
                  type: string
                  enum: [image, video, document, audio]
                  description: Type of media attachment
      responses:
        "200":
          description: Message sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message_id:
                    type: string
              example:
                success: true
                message_id: "wamid.HBgLMjAxMjM0NTY3ODkwFQIAERgS"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "400":
          description: Bad request
          content:
            application/json:
              example:
                error: "validation_error"
                message: "phone_number is required and must be in E.164 format."

  /functions/v1/create-awb-bosta:
    post:
      tags: [Shipping]
      operationId: createAWB
      summary: Create a shipment (AWB) with Bosta
      description: |
        Creates a new airway bill (shipment) with the Bosta courier service.
        **Auth required.**
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [order_id]
              properties:
                order_id:
                  type: string
                  format: uuid
                  description: Order to create shipment for
                pickup_date:
                  type: string
                  format: date
                  description: Requested pickup date
                notes:
                  type: string
                  description: Delivery instructions
            example:
              order_id: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
              pickup_date: "2025-06-17"
              notes: "Please call before delivery."
      responses:
        "200":
          description: AWB created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  tracking_number:
                    type: string
                  label_url:
                    type: string
              example:
                success: true
                tracking_number: "BST-987654321"
                label_url: "https://storage.example.com/labels/BST-987654321.pdf"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /functions/v1/call-initiate:
    post:
      tags: [VoIP]
      operationId: initiateCall
      summary: Initiate an outbound call
      description: |
        Triggers the PBX to bridge a call between the agent's extension and the target phone number.
        **Auth required.**
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone_number]
              properties:
                phone_number:
                  type: string
                  description: Target phone number
                  example: "+201234567890"
                extension:
                  type: string
                  description: Agent's PBX extension (auto-detected if omitted)
                  example: "1001"
      responses:
        "200":
          description: Call initiated
          content:
            application/json:
              example:
                success: true
                call_id: "CALL-2025-06-15-001"
        "401":
          $ref: "#/components/responses/Unauthorized"

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Supabase Auth JWT token

  parameters:
    RangeHeader:
      name: Range
      in: header
      schema:
        type: string
      description: "Pagination range (e.g., '0-24' for first 25 items)"
      example: "0-24"
    OrderParam:
      name: order
      in: query
      schema:
        type: string
      description: "Sort order (e.g., 'created_at.desc')"
      example: "created_at.desc"

  responses:
    Unauthorized:
      description: Missing or invalid authentication token
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: "unauthorized"
            message: "Missing or invalid bearer token."
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: "not_found"
            message: "The requested resource was not found."

  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
        message:
          type: string
        details:
          type: object

    Contact:
      type: object
      properties:
        id:
          type: string
          format: uuid
        first_name:
          type: string
        last_name:
          type: string
        phone_number:
          type: string
        email:
          type: string
        company:
          type: string
        city:
          type: string
        country:
          type: string
        journey_stage:
          type: string
          enum: [lead, prospect, customer, churned]
        source:
          type: string
        assigned_to:
          type: string
          format: uuid
        tags:
          type: array
          items:
            type: string
        metadata:
          type: object
        created_at:
          type: string
          format: date-time

    Order:
      type: object
      properties:
        id:
          type: string
          format: uuid
        order_number:
          type: string
          description: Human-readable number; MCP creates sequential ORD-000001 style per organization.
        customer_id:
          type: string
          format: uuid
        status:
          type: string
          enum: [pending, confirmed, processing, picked, packed, shipped, delivered, returned, cancelled]
        total_amount:
          type: number
        currency:
          type: string
        source:
          type: string
        shipping_address:
          type: object
          description: Delivery address for shipping and AWB/label creation (may also be stored as plain text depending on channel).
        shipping_phone_number:
          type: string
          description: Contact phone for delivery / courier (AWB and logistics).
        notes:
          type: string
        created_at:
          type: string
          format: date-time

    InventoryItem:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        sku:
          type: string
        barcode:
          type: string
        quantity_on_hand:
          type: integer
        reorder_point:
          type: integer
        unit_cost:
          type: number
        category:
          type: string
        image_url:
          type: string
        metadata:
          type: object
        created_at:
          type: string
          format: date-time

    Conversation:
      type: object
      properties:
        id:
          type: string
          format: uuid
        customer_id:
          type: string
          format: uuid
        channel:
          type: string
          enum: [whatsapp, facebook, instagram, email, internal]
        status:
          type: string
          enum: [open, closed, pending]
        assigned_to:
          type: string
          format: uuid
        last_message_at:
          type: string
          format: date-time
        unread_count:
          type: integer

    Message:
      type: object
      properties:
        id:
          type: string
          format: uuid
        chat_id:
          type: string
          format: uuid
        sender_type:
          type: string
          enum: [customer, agent, system]
        content:
          type: string
        media_url:
          type: string
        media_type:
          type: string
        message_type:
          type: string
          enum: [text, image, video, document, audio, template, reaction]
        direction:
          type: string
          enum: [inbound, outbound]
        status:
          type: string
          enum: [sent, delivered, read, failed]
        created_at:
          type: string
          format: date-time

    AWB:
      type: object
      properties:
        id:
          type: string
          format: uuid
        order_id:
          type: string
          format: uuid
        tracking_number:
          type: string
        courier:
          type: string
        status:
          type: string
        label_url:
          type: string
        created_at:
          type: string
          format: date-time

    CallLog:
      type: object
      properties:
        id:
          type: string
          format: uuid
        customer_id:
          type: string
          format: uuid
        direction:
          type: string
          enum: [inbound, outbound]
        duration_seconds:
          type: integer
        status:
          type: string
          enum: [answered, missed, busy, failed]
        extension:
          type: string
        recording_url:
          type: string
        created_at:
          type: string
          format: date-time
