openapi: 3.1.0
info:
  title: Zenlist Partner Integration API
  description: Partner API used for integration.  This is not a general purpose API and access is granted specifically to partners and brokerages we work with.
  version: 2.0.0
  contact:
    name: API Support
    url: https://zenlist.com
    email: support@zenlist.com

servers:
  - url: https://openapi.zenlist.com/v2
    description: Main (production) server

security:
  - ApiKeyAuth: []

tags:
  - name: subscription
    description: Subscription represents an active webhook registered endpoint for this Api Key.
    externalDocs:
      url: http://resthooks.org/
  - name: agent
    description: Agents represent an active Zenlist user that is registered with their local MLS.
  - name: client
    description: Clients represent an active Zenlist user that is connected to an agent.
  - name: client_invitation
    description: Client invitations represent a pending Zenlist user that is connected to an agent.
  - name: event
    description: Client events represent the activity of an active Zenlist user that is connected to an agent.
  - name: chat
    description: Chat events.
  - name: search
    description: Searches represent an active Zenlist client search.
  - name: message
    description: Message counts and aggregate message data for an account.

paths:
  /subscriptions:
    get:
      summary: Returns a list of subscriptions
      description: Returns the list of subscriptions that the provided key has currently active.
      tags:
        - subscription
      parameters:
        - name: "size"
          in: "query"
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 200
        - name: "from"
          in: "query"
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: A JSON array of subscriptions
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Subscription"
    post:
      summary: Subscribe to a webhook
      tags:
        - subscription
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                hook_url:
                  type: string
                  format: uri
                  example: https://myserver.com/send/callback/here
                resource:
                  type: string
                  enum: [agent, client, client_invitation, event, search]
              required:
                - hook_url
                - resource
      responses:
        "201":
          description: Webhook created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Subscription"
      callbacks:
        new_agent:
          "{new_agent#/hook_url}":
            post:
              requestBody:
                $ref: "#/components/schemas/Agent"
              responses:
                "200":
                  description: OK
        new_client:
          "{new_client#/hook_url}":
            post:
              requestBody:
                $ref: "#/components/schemas/Client"
              responses:
                "200":
                  description: OK
        new_client_invitation:
          "{new_client_invitation#/hook_url}":
            post:
              requestBody:
                $ref: "#/components/schemas/ClientInvitation"
              responses:
                "200":
                  description: OK
        new_event:
          "{new_event#/hook_url}":
            post:
              requestBody:
                $ref: "#/components/schemas/Event"
              responses:
                "200":
                  description: OK
  /subscriptions/{subscriptionId}:
    get:
      summary: Get the details of a webhook
      tags:
        - subscription
      parameters:
        - name: subscriptionId
          in: path
          required: true
          description: The Zenlist ID for the subscription
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Subscription"
    put:
      summary: Update a webhook
      tags:
        - subscription
      parameters:
        - name: subscriptionId
          in: path
          required: true
          description: The Zenlist ID for the subscription
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                hook_url:
                  type: string
                  format: uri
                  example: https://myserver.com/send/callback/here
                resource:
                  type: string
                  enum: [agent, client, client_invitation, event, search]
              required:
                - hook_url
                - resource
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Subscription"
    delete:
      summary: Remove a webhook
      tags:
        - subscription
      parameters:
        - name: subscriptionId
          in: path
          required: true
          description: The Zenlist ID for the subscription
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Subscription"
  /agents:
    get:
      summary: Returns a list of agents
      description: Returns the list of agents that the provided key has access to.
      tags:
        - agent
      parameters:
        - name: "size"
          in: "query"
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 200
        - name: "from"
          in: "query"
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: A JSON array of agents
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Agent"
  /agents/{agentId}:
    get:
      summary: Returns an agent by ID.
      tags:
        - agent
      parameters:
        - name: agentId
          in: path
          required: true
          description: The Zenlist ID for the agent
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Agent"
  /agents_cursor:
    get:
      summary: Returns a list of agents
      description: Returns the list of agents that the provided key has access to.
      tags:
        - agent
      parameters:
        - name: "size"
          in: "query"
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 200
        - name: "cursor"
          description: A cursor returned from a previous query for additional results, to be included alongside the original other provided querystrings
          in: "query"
          schema:
            type: string
        - name: "modified_at_start"
          description: An inclusive start datetime in an rfc3339 compatible format for when an agent record was last changed
          in: "query"
          schema:
            type: string
            format: date-time
        - name: "modified_at_end"
          description: An exclusive end datetime in an rfc3339 compatible format for when an agent record was last changed
          in: "query"
          schema:
            type: string
            format: date-time
      responses:
        "200":
          description: A JSON array of agents in a cursor response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Agent"
  /clients:
    get:
      summary: Returns a list of clients
      description: Returns the list of clients that the provided key has access to.
      tags:
        - client
      parameters:
        - name: "size"
          in: "query"
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 200
        - name: "cursor"
          in: "query"
          schema:
            type: string
      responses:
        "200":
          description: A JSON array of clients
          content:
            application/json:
              $ref: "#/components/schemas/ClientCursor"
  /clients/{clientId}:
    get:
      summary: Returns a client by ID.
      tags:
        - client
      parameters:
        - name: clientId
          in: path
          required: true
          description: The Zenlist ID for the client
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Client"
  /searches:
    get:
      summary: Returns a list of searches
      description: Returns the list of searches that the provided key has access to.
      tags:
        - search
      parameters:
        - name: "cursor"
          in: "query"
          schema:
            type: string
      responses:
        "200":
          description: A JSON array of searches
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SearchCursor"
  /searches/{searchId}:
    get:
      summary: Returns a search by ID.
      tags:
        - search
      parameters:
        - name: searchId
          in: path
          required: true
          description: The Zenlist ID for the search
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Search"
  /client_invitations:
    get:
      summary: Returns a list of client invitations
      description: Returns the list of client invitations that the provided key has access to.
      tags:
        - client_invitation
      parameters:
        - name: "size"
          in: "query"
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 200
        - name: "cursor"
          in: "query"
          schema:
            type: string
      responses:
        "200":
          description: A JSON array of client invitations
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClientInvitationCursor"
    post:
      summary: Send a new client invitation
      tags:
        - client_invitation
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                agent_email:
                  type: string
                  format: email
                client_email:
                  type: string
                  format: email
                client_first_name:
                  type: string
                client_last_name:
                  type: string
                client_phone:
                  type: string
              required:
                - agent_email
                - client_email
                - client_first_name
                - client_last_name
      responses:
        "201":
          description: Client invitation created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClientInvitation"
  /client_invitations/{clientInvitationId}:
    get:
      summary: Returns a client invitation by ID.
      tags:
        - client_invitation
      parameters:
        - name: clientInvitationId
          in: path
          required: true
          description: The Zenlist ID for the client invitation
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClientInvitation"
  /events:
    get:
      summary: Returns a list of client events
      description: Returns the list of client events that the provided key has access to.
      tags:
        - event
      parameters:
        - name: "size"
          in: "query"
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 200
        - name: "from"
          in: "query"
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: A JSON array of client events
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Event"
  /events/{eventId}:
    get:
      summary: Returns an event by ID.
      tags:
        - event
      parameters:
        - name: eventId
          in: path
          required: true
          description: The Zenlist ID for the event
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Event"
  /chats:
    get:
      summary: Returns a list of client chat messages
      description: Returns the list of client chat messages that the provided key has access to.
      tags:
        - chat
      parameters:
        - name: "size"
          in: "query"
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 200
        - name: "from"
          in: "query"
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: A JSON array of client chat messages
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Chat"
  /chats/{chatId}:
    get:
      summary: Returns a chat message by ID.
      tags:
        - chat
      parameters:
        - name: chatId
          in: path
          required: true
          description: The Zenlist ID for the chat message
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Chat"

  /messages/counts:
    get:
      summary: Returns aggregate message counts for an account
      description: >-
        Returns the unread message count across all conversations for the account
        associated with the given MLS member ID. Requires an MLS-scoped API key.
      tags:
        - message
      parameters:
        - name: member_mls_id
          in: query
          required: true
          description: The member MLS ID of the agent to look up
          schema:
            type: string
      responses:
        "200":
          description: Aggregate message counts
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageCounts"
        "401":
          description: Unauthorized — API key is missing, inactive, or not an MLS-scoped key
        "404":
          description: No agent found for the given member_mls_id, or the agent has no associated account

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
  schemas:
    Account:
      type: object
      required:
        - id
        - first_name
        - last_name
        - account_state
        - agent_ids
        - email
      properties:
        id:
          type: string
        first_name:
          type: string
        last_name:
          type: string
        account_state:
          type: string
          enum: [active, deactivating, unlinked]
        activated_at:
          type: string
          format: date-time
        active_date:
          type: string
          format: date-time
        agent_ids:
          type: array
          items: 
            type: string
        email:
          type: string
          format: email
    Subscription:
      type: object
      required:
        - id
        - hook_url
        - resource
        - created_at
      properties:
        id:
          type: string
        hook_url:
          type: string
        resource:
          type: string
          enum: [agent, client, client_invitation, event, search]
        created_at:
          type: string
          format: date-time
        secret:
          type: string
    Agent:
      type: object
      required:
        - id
        - name
        - first_name
        - last_name
        - mls
        - mls_id
        - modified_at
      properties:
        id:
          type: string
        name:
          type: string
        first_name:
          type: string
        last_name:
          type: string
        mls:
          type: string
        mls_id:
          type: string
        modified_at:
          type: string
          format: date-time
        phone:
          type: string
        email:
          type: string
          format: email
        claimed:
          type: boolean
        claimed_at:
          type: boolean
          format: date-time
        account_id:
          type: string
        account:
          $ref: "#/components/schemas/Account"
    AgentCursor:
      type: object
      required:
        - data
        - cursor
        - has_next_page
        - total
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Agent"
        cursor:
          type: string
        has_next_page:
          type: boolean
        total:
          type: integer
    ClientCursor:
      type: object
      required:
        - data
        - cursor
        - has_next_page
        - total
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Client"
        cursor:
          type: string
        has_next_page:
          type: boolean
        total:
          type: integer
    Client:
      type: object
      required:
        - id
        - name
        - first_name
        - last_name
        - email
        - phone
        - created_at
        - agent
      properties:
        id:
          type: string
        name:
          type: string
        first_name:
          type: string
        last_name:
          type: string
        email:
          type: string
          format: email
        phone:
          type: string
          format: phone
        created_at:
          type: string
          format: date-time
        agent_id:
          type: string
        modified_at:
          type: string
          format: date-time
    Search:
      type: object
      required:
        - id
        - created_at
      properties:
        id:
          type: string
        created_at:
          type: string
          format: date-time
    SearchCursor:
      type: object
      required:
        - data
        - cursor
        - has_next_page
        - total
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Search"
        cursor:
          type: string
        has_next_page:
          type: boolean
        total:
          type: integer
    ClientInvitation:
      type: object
      required:
        - id
        - state
        - first_name
        - last_name
        - email
        - agent_id
        - created_at
        - modified_at
      properties:
        id:
          type: string
        state:
          type: string
          enum: [pending, accepted, declined, cancelled]
        first_name:
          type: string
        last_name:
          type: string
        email:
          type: string
          format: email
        created_at:
          type: string
          format: date-time
        modified_at:
          type: string
          format: date-time
        agent_id:
          type: string
        client_id:
          type: string
        phone:
          type: string
          format: phone
        accepted_at:
          type: string
          format: date-time
        declined_at:
          type: string
          format: date-time
        cancelled_at:
          type: string
          format: date-time
    ClientInvitationCursor:
      type: object
      required:
        - data
        - cursor
        - has_next_page
        - total
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ClientInvitation"
        cursor:
          type: string
        has_next_page:
          type: boolean
        total:
          type: integer
    Event:
      type: object
      required:
        - id
        - created_at
        - event_type
        - client
      properties:
        id:
          type: string
        created_at:
          type: string
          format: date-time
        event_type:
          type: string
          enum:
            [
              listing_view,
              listings_save,
              listings_unsave,
              listings_request_tour,
              note_create,
              search_save,
              session_new,
            ]
        client:
          $ref: "#/components/schemas/Client"
        listing:
          $ref: "#/components/schemas/Listing"
        listings:
          type: array
          items:
            $ref: "#/components/schemas/Listing"
        note:
          $ref: "#/components/schemas/Note"
        search:
          $ref: "#/components/schemas/Search"
    Listing:
      type: object
      required:
        - id
        - mls
        - mls_id
      properties:
        id:
          type: string
        mls:
          type: string
        mls_id:
          type: string
    Note:
      type: object
      required:
        - id
        - created_at
        - note
      properties:
        id:
          type: string
        created_at:
          type: string
          format: date-time
        note:
          type: string
    Chat:
      type: object
      required:
        - id
      properties:
        id:
          type: string
    MessageCounts:
      type: object
      required:
        - unread
      properties:
        unread:
          type: integer
          description: Total unread messages across all conversations
