openapi: 3.1.0
info:
  title: Leadfox API
  version: 1.0.0
  description: "## API Basics (Concise Version)\n[**⚠️⚠️⚠️\_PLEASE REFER TO THE API BASIC SECTION FIRST\_⚠️⚠️⚠️**](/getting-started/api-basics)\n\nMost collection endpoints support query parameters for filtering, sorting, field selection, and pagination:\n\n- **filters**: Filter resources using conditions (see Filter Syntax below)\n- **sort**: Sort results by field(s)\n- **fields**: Select specific fields to return\n- **skip**: Number of records to skip (pagination)\n- **limit**: Maximum number of records to return (default: 100)\n\n### Filter Syntax\n\n- Use comma `,` between conditions to `AND`\n- Use pipe `|` between values to `OR`\n- Use dot `.` to access nested attributes\n- Operators: `=`, `!=`, `>`, `>=`, `<`, `<=`, `exists`\n\n**Example**: `filters=name = Test List,deleted = false`\n\n### Sort Syntax\n\n- Use comma `,` to sort by multiple fields\n- Use minus `-` prefix for descending order\n\n**Example**: `sort=-name`\n\n### Fields Syntax\n\n- Use comma `,` to select multiple fields\n- `_id` and `_owner` are always included\n\n**Example**: `fields=name,description`\n"
  contact:
    name: Leadfox Support
    url: https://leadfox.co
  license:
    name: Proprietary
    url: https://leadfox.co/terms-and-conditions
servers:
  - url: https://rest.leadfox.co/v1
    description: Production server
x-tagGroups:
  - name: Authentication
    tags:
      - authentication
  - name: Campaigns
    tags:
      - campaigns-collection
      - campaigns-resource
  - name: Contacts
    tags:
      - contacts-collection
      - contacts-resource
  - name: Emails
    tags:
      - emails-collection
      - emails-resource
  - name: Folders
    tags:
      - folders-collection
      - folders-resource
  - name: Lists
    tags:
      - lists-collection
      - lists-resource
      - lists-contacts
  - name: MailingLists
    tags:
      - mailinglists-collection
      - mailinglists-resource
  - name: Metrics
    tags:
      - metrics
  - name: Properties
    tags:
      - properties-collection
      - properties-resource
tags:
  - name: authentication
    x-displayName: Authentication
    description: "[**⚠️⚠️⚠️\_PLEASE REFER TO THE AUTHENTICATION SECTION FIRST\_⚠️⚠️⚠️**](/)  \n\n1. Generate an API key and secret from the [API page](https://app.leadfox.co/lf/#/v1/api)\n2. Exchange them for a JWT token via `POST /v1/auth`\n3. Include the JWT token in the `Authorization` header as `JWT <token>`\n"
  - name: lists-collection
    x-displayName: Lists - Collection
    description: >
      Manage contact lists for segmentation and targeting.


      Lists are used to group contacts for sending `EmailCampaigns` or
      organizing them.

      Use lists to create segments like "Webinar Attendees" or "Black Friday
      Leads".
  - name: lists-resource
    x-displayName: Lists - Resource
    description: Operations on individual `List` resources
  - name: lists-contacts
    x-displayName: Lists - Contacts
    description: Manage contact membership in lists
  - name: contacts-collection
    x-displayName: Contacts - Collection
    description: >
      Manage contacts in your marketing database.


      `Contacts` represent people in your marketing database, identified by a
      unique email address.

      Each `Contact` can have custom `Properties`, belong to `Lists`, and have
      subscription preferences for `MailingLists` if GDPR is enabled.
  - name: contacts-resource
    x-displayName: Contacts - Resource
    description: Operations on individual `Contact` resources
  - name: properties-collection
    x-displayName: Properties - Collection
    description: >
      Manage `Contact` custom `Properties`.


      You can define custom `Properties` that store additional information in a
      `Contact`.

      Built-in system `Properties` like "email", "firstname", "lastname",
      "phone", and "gender" already exist,

      but you can create your own custom `Properties` for your specific needs
      like "company", "industry", or "jobtitle".


      Each `Property` has a shortcode that serves as the key in a `Contact`'s
      properties object.

      All property values are stored as strings.


      **Example `Contact` with properties:**

      ```json

      {
        "_id": "000000000000000000000001",
        "properties": {
          "firstname": "John",
          "lastname": "Doe",
          "email": "john.doe@example.com",
          "company": "Doe's Inc.",
          "jobtitle": "CEO"
        }
      }

      ```
  - name: properties-resource
    x-displayName: Properties - Resource
    description: Operations on individual `Property` resources
  - name: mailinglists-collection
    x-displayName: MailingLists - Collection
    description: >
      Manage GDPR-compliant email subscription categories.


      `MailingLists` represent email categories that `Contacts` can opt-in or
      opt-out of, such as "General communication", "Newsletter", or
      "Promotions".

      They are used to ensure GDPR compliance by respecting `Contact`
      subscription preferences.


      `MailingLists` work in combination with `Lists`: use `Lists` to target
      specific `Contacts`,

      while `MailingLists` ensure you only send to those who opted-in to that
      category.
  - name: mailinglists-resource
    x-displayName: MailingLists - Resource
    description: Operations on individual `MailingList` resources
  - name: folders-collection
    x-displayName: Folders - Collection
    description: >
      Organize campaigns and email designs.


      `Folders` are used to organize and group campaigns and emails for better
      management and organization.
  - name: folders-resource
    x-displayName: Folders - Resource
    description: Operations on individual `Folder` resources
  - name: campaigns-collection
    x-displayName: Campaigns - Collection
    description: >
      Manage a `Client`'s `Campaigns`.


      **Note: The `Campaign` entity supports multiple types, but only
      `EmailCampaign` (type = 2) is documented.  

      Refrain from using other campaign types.**
  - name: campaigns-resource
    x-displayName: Campaigns - Resource
    description: Operations on individual `Campaign` resources
  - name: emails-collection
    x-displayName: Emails - Collection
    description: >
      Manage email designs.


      `Emails` represent email designs used in `EmailCampaigns`.

      Each `Email` contains HTML content, subject line, sender information, and
      other email-specific settings.
  - name: emails-resource
    x-displayName: Emails - Resource
    description: Operations on individual `Email` resources
  - name: metrics
    x-displayName: Metrics
    description: "Retrieve performance metrics for `EmailCampaigns` and `Emails`.  \n[⚠️⚠️⚠️\_PLEASE REFER TO THE METRICS SECTION IN THE EMAILING GUIDE FIRST\_⚠️⚠️⚠️**](/common-use-cases/emailing#retrieving-emailcampaign-metrics)\n"
paths:
  /auth:
    post:
      tags:
        - authentication
      summary: Authenticate and obtain JWT token
      description: |
        Exchange your API key and secret for a JWT token.

        The JWT token is used to authenticate subsequent API requests.
      operationId: authenticate
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - apikey
                - secret
              properties:
                apikey:
                  type: string
                secret:
                  type: string
      responses:
        '200':
          description: Authentication successful
          content:
            application/json:
              schema:
                type: object
                properties:
                  jwt:
                    type: string
                  mode:
                    type: string
                    description: Authentication mode used
                  client:
                    type: string
                    description: Client name
                  clientid:
                    type: string
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /list:
    get:
      tags:
        - lists-collection
      summary: List all Lists
      operationId: get-lists
      parameters:
        - $ref: '#/components/parameters/Filters'
        - $ref: '#/components/parameters/Sort'
        - $ref: '#/components/parameters/Fields'
        - $ref: '#/components/parameters/Skip'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Returns an array of `List` objects
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListArray'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
    post:
      tags:
        - lists-collection
      summary: Create a new List
      operationId: create-list
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                description:
                  type: string
      responses:
        '201':
          description: Returns the created `List`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/List'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /list/{id}:
    get:
      tags:
        - lists-resource
      summary: Get a List by ID
      operationId: get-list-by-id
      parameters:
        - $ref: '#/components/parameters/Id'
        - $ref: '#/components/parameters/Fields'
      responses:
        '200':
          description: Returns the `List`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/List'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
    put:
      tags:
        - lists-resource
      summary: Update a List by ID
      operationId: update-list
      parameters:
        - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
      responses:
        '200':
          description: Returns the updated `List`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/List'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
    delete:
      tags:
        - lists-resource
      summary: Delete a List by ID
      operationId: delete-list
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: '`List` deleted successfully'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /list/{list_id}/contact:
    get:
      tags:
        - lists-contacts
      summary: List Contacts in a List
      operationId: get-list-contacts
      parameters:
        - name: list_id
          in: path
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/Filters'
        - $ref: '#/components/parameters/Sort'
        - $ref: '#/components/parameters/Fields'
        - $ref: '#/components/parameters/Skip'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Returns an array of `Contact` objects
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContactArray'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /list/{list_id}/contact/count:
    get:
      tags:
        - lists-contacts
      summary: Count Contacts in a List
      operationId: count-list-contacts
      parameters:
        - name: list_id
          in: path
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/Filters'
      responses:
        '200':
          description: Returns the count of `Contacts`
          content:
            application/json:
              schema:
                type: object
                properties:
                  count:
                    type: integer
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /list/{list_id}/contact/{contact_id}:
    post:
      tags:
        - lists-contacts
      summary: Add a Contact to a List
      operationId: add-contact-to-list
      parameters:
        - name: list_id
          in: path
          required: true
          schema:
            type: string
        - name: contact_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '201':
          description: '`Contact` added to `List` successfully'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
    delete:
      tags:
        - lists-contacts
      summary: Remove a Contact from a List
      operationId: remove-contact-from-list
      parameters:
        - name: list_id
          in: path
          required: true
          schema:
            type: string
        - name: contact_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: '`Contact` removed from `List` successfully'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /property:
    get:
      tags:
        - properties-collection
      summary: List all Properties
      operationId: get-properties
      parameters:
        - $ref: '#/components/parameters/Filters'
        - $ref: '#/components/parameters/Sort'
        - $ref: '#/components/parameters/Fields'
        - $ref: '#/components/parameters/Skip'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Returns an array of `Property` objects
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PropertyArray'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
    post:
      tags:
        - properties-collection
      summary: Create a new Property
      operationId: create-property
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - shortcode
              properties:
                name:
                  type: string
                shortcode:
                  type: string
                description:
                  type: string
      responses:
        '201':
          description: Returns the created `Property`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Property'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /property/{id}:
    get:
      tags:
        - properties-resource
      summary: Get a Property by ID
      operationId: get-property-by-id
      parameters:
        - $ref: '#/components/parameters/Id'
        - $ref: '#/components/parameters/Fields'
      responses:
        '200':
          description: Returns the `Property`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Property'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
    put:
      tags:
        - properties-resource
      summary: Update a Property by ID
      operationId: update-property
      parameters:
        - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                shortcode:
                  type: string
                description:
                  type: string
      responses:
        '200':
          description: Returns the updated `Property`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Property'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
    delete:
      tags:
        - properties-resource
      summary: Delete a Property by ID
      operationId: delete-property
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: '`Property` deleted successfully'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /mailinglist:
    get:
      tags:
        - mailinglists-collection
      summary: List all MailingLists
      operationId: get-mailing-lists
      parameters:
        - $ref: '#/components/parameters/Filters'
        - $ref: '#/components/parameters/Sort'
        - $ref: '#/components/parameters/Fields'
        - $ref: '#/components/parameters/Skip'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: |
            Returns an array of `MailingList` objects

            **Example: Get the default MailingList**
            ```
            GET /v1/mailinglist?filters=default = true
            ```
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MailingListArray'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /mailinglist/{id}:
    get:
      tags:
        - mailinglists-resource
      summary: Get a MailingList by ID
      operationId: get-mailing-list-by-id
      parameters:
        - $ref: '#/components/parameters/Id'
        - $ref: '#/components/parameters/Fields'
      responses:
        '200':
          description: Returns the `MailingList`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MailingList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /contact:
    get:
      tags:
        - contacts-collection
      summary: List all Contacts
      operationId: get-contacts
      parameters:
        - $ref: '#/components/parameters/Filters'
        - $ref: '#/components/parameters/Sort'
        - $ref: '#/components/parameters/Fields'
        - $ref: '#/components/parameters/Skip'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Returns an array of `Contact` objects
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContactArray'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
    post:
      tags:
        - contacts-collection
      summary: Create a new Contact
      operationId: create-contact
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
              properties:
                email:
                  type: string
                properties:
                  type: object
      responses:
        '201':
          description: Returns the created `Contact`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /contact/upsert:
    post:
      tags:
        - contacts-collection
      summary: Upsert a Contact
      description: >-
        Insert a new `Contact` if it doesn't exist, or update it if it does
        (based on email)
      operationId: upsert-contact
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
              properties:
                email:
                  type: string
                properties:
                  type: object
      responses:
        '200':
          description: Returns the upserted `Contact`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /contact/{id}:
    get:
      tags:
        - contacts-resource
      summary: Get a Contact by ID
      operationId: get-contact-by-id
      parameters:
        - $ref: '#/components/parameters/Id'
        - $ref: '#/components/parameters/Fields'
      responses:
        '200':
          description: Returns the `Contact`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
    put:
      tags:
        - contacts-resource
      summary: Update a Contact by ID
      description: >
        Partial update of a `Contact`. Only `properties` can be updated via this
        endpoint.


        **Special behavior for properties:**

        The `properties` object is merged with existing properties, not
        replaced.

        To update a single property, send only that property.


        **Example updating properties:**

        ```json

        {
          "properties": {
            "company": "New Company Inc."
          }
        }

        ```

        This will update only the "company" property while keeping other
        properties intact.


        **To manage list membership, use the dedicated endpoints documented in
        the `Lists` section.**
      operationId: update-contact
      parameters:
        - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                properties:
                  type: object
      responses:
        '200':
          description: Returns the updated `Contact`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
    delete:
      tags:
        - contacts-resource
      summary: Delete a Contact by ID
      operationId: delete-contact
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: '`Contact` deleted successfully'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /folder:
    get:
      tags:
        - folders-collection
      summary: List all Folders
      operationId: get-folders
      parameters:
        - $ref: '#/components/parameters/Filters'
        - $ref: '#/components/parameters/Sort'
        - $ref: '#/components/parameters/Fields'
        - $ref: '#/components/parameters/Skip'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Returns an array of `Folder` objects
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FolderArray'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
    post:
      tags:
        - folders-collection
      summary: Create a new Folder
      operationId: create-folder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                description:
                  type: string
      responses:
        '201':
          description: Returns the created `Folder`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Folder'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /folder/{id}:
    get:
      tags:
        - folders-resource
      summary: Get a Folder by ID
      operationId: get-folder-by-id
      parameters:
        - $ref: '#/components/parameters/Id'
        - $ref: '#/components/parameters/Fields'
      responses:
        '200':
          description: Returns the `Folder`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Folder'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
    put:
      tags:
        - folders-resource
      summary: Update a Folder by ID
      operationId: update-folder
      parameters:
        - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
      responses:
        '200':
          description: Returns the updated `Folder`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Folder'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
    delete:
      tags:
        - folders-resource
      summary: Delete a Folder by ID
      operationId: delete-folder
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: '`Folder` deleted successfully'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /campaign:
    get:
      tags:
        - campaigns-collection
      summary: List all Campaigns
      operationId: get-campaigns
      parameters:
        - $ref: '#/components/parameters/Filters'
        - $ref: '#/components/parameters/Sort'
        - $ref: '#/components/parameters/Fields'
        - $ref: '#/components/parameters/Skip'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Returns an array of `Campaign` objects
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CampaignArray'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
    post:
      tags:
        - campaigns-collection
      summary: Create a new Campaign
      description: "[⚠️⚠️⚠️\_PLEASE REFER TO THE EMAILING GUIDE BEFORE USING THE CAMPAIGN API\_⚠️⚠️⚠️**](/common-use-cases/emailing)  \nCreate a new `Campaign` (currently only `EmailCampaign` type is supported).\n\n**Required before creating:**\n- At least one `Folder` to organize campaigns\n- At least one `Email` design to use in the campaign\n\n**Required before sending:**\n- At least one `List` containing the `Contacts` to send to\n"
      operationId: create-campaign
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Campaign'
      responses:
        '201':
          description: Returns the created `Campaign`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Campaign'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /campaign/{id}:
    get:
      tags:
        - campaigns-resource
      summary: Get a Campaign by ID
      operationId: get-campaign-by-id
      parameters:
        - $ref: '#/components/parameters/Id'
        - $ref: '#/components/parameters/Fields'
      responses:
        '200':
          description: Returns the `Campaign`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Campaign'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
    put:
      tags:
        - campaigns-resource
      summary: Update a Campaign by ID
      description: >
        Partial update of a `Campaign`.


        **To schedule an `EmailCampaign`**, set the `start` date to a future
        datetime.

        **To send an `EmailCampaign`**, update the `status` to "a" (active).
      operationId: update-campaign
      parameters:
        - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                status:
                  $ref: '#/components/schemas/EmailCampaignStatus'
                start:
                  type: string
                  format: date-time
                batchConfiguration:
                  $ref: '#/components/schemas/BatchConfiguration'
                emails:
                  type: array
                  items:
                    $ref: '#/components/schemas/EmailVariant'
                lists:
                  type: array
                  items:
                    $ref: '#/components/schemas/CampaignList'
                mailinglist:
                  $ref: '#/components/schemas/CampaignMailingList'
      responses:
        '200':
          description: Returns the updated `Campaign`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Campaign'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
    delete:
      tags:
        - campaigns-resource
      summary: Delete a Campaign by ID
      operationId: delete-campaign
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: '`Campaign` deleted successfully'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /email:
    get:
      tags:
        - emails-collection
      summary: List all Emails
      operationId: get-emails
      parameters:
        - $ref: '#/components/parameters/Filters'
        - $ref: '#/components/parameters/Sort'
        - $ref: '#/components/parameters/Fields'
        - $ref: '#/components/parameters/Skip'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Returns an array of `Email` objects
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmailArray'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
    post:
      tags:
        - emails-collection
      summary: Create a new Email
      operationId: create-email
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                description:
                  type: string
                subject:
                  type: string
                preheader:
                  type: string
                fromemail:
                  type: string
                fromname:
                  type: string
                unsubscribetext:
                  type: string
                editor:
                  type: string
                  default: bee
                  enum:
                    - bee
                body:
                  type: string
                json:
                  type: string
      responses:
        '201':
          description: Returns the created `Email`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Email'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /email/{id}:
    get:
      tags:
        - emails-resource
      summary: Get an Email by ID
      operationId: get-email-by-id
      parameters:
        - $ref: '#/components/parameters/Id'
        - $ref: '#/components/parameters/Fields'
      responses:
        '200':
          description: Returns the `Email`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Email'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
    put:
      tags:
        - emails-resource
      summary: Update an Email by ID
      operationId: update-email
      parameters:
        - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                subject:
                  type: string
                preheader:
                  type: string
                fromemail:
                  type: string
                fromname:
                  type: string
                unsubscribetext:
                  type: string
                editor:
                  type: string
                  default: bee
                  enum:
                    - bee
                body:
                  type: string
                json:
                  type: string
      responses:
        '200':
          description: Returns the updated `Email`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Email'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
    delete:
      tags:
        - emails-resource
      summary: Delete an Email by ID
      operationId: delete-email
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: '`Email` deleted successfully'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /metric/{element_type}/{element_id}/email:
    get:
      tags:
        - metrics
      summary: Get email metrics
      operationId: get-email-metrics
      parameters:
        - name: element_type
          in: path
          required: true
          schema:
            type: string
            enum:
              - campaign
              - target
          description: |
            campaign = metrics for an `EmailCampaign` (all variants)
            target = metrics for a specific `Email` variant
        - name: element_id
          in: path
          required: true
          schema:
            type: string
        - name: compile
          in: query
          required: false
          schema:
            type: boolean
          description: Get aggregated totals
        - name: startdate
          in: query
          required: false
          schema:
            type: string
            format: date-time
        - name: enddate
          in: query
          required: false
          schema:
            type: string
            format: date-time
      responses:
        '200':
          description: Returns email metrics
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/EmailMetricTotalArray'
                  - $ref: '#/components/schemas/EmailMetricDailyArray'
              examples:
                total:
                  summary: Total metrics (compile=true)
                  value:
                    - _id: <element_id>
                      sent: 1000
                      open: 250
                      uniqueopen: 200
                      click: 50
                      uniqueclick: 45
                      bounce: 10
                      unsubscribe: 5
                      spam: 1
                daily:
                  summary: Daily metrics (startdate + enddate)
                  value:
                    - _id:
                        group: <element_id>
                        year: 2025
                        month: 1
                        day: 1
                      sent: 100
                      open: 25
                      uniqueopen: 20
                      click: 5
                      uniqueclick: 4
                      bounce: 1
                      unsubscribe: 0
                      spam: 0
                    - _id:
                        group: <element_id>
                        year: 2025
                        month: 1
                        day: 2
                      sent: 100
                      open: 22
                      uniqueopen: 18
                      click: 4
                      uniqueclick: 3
                      bounce: 2
                      unsubscribe: 1
                      spam: 0
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
components:
  securitySchemes:
    securitySchemes:
      JWTAuth:
        type: http
        scheme: bearer
        bearerFormat: JWT
        description: |
          JWT token obtained from `/v1/auth` endpoint.

          Include in Authorization header as: `Authorization: JWT <token>`
  parameters:
    Filters:
      name: filters
      in: query
      required: false
      schema:
        type: string
    Sort:
      name: sort
      in: query
      required: false
      schema:
        type: string
    Fields:
      name: fields
      in: query
      required: false
      schema:
        type: string
    Skip:
      name: skip
      in: query
      required: false
      schema:
        type: integer
        minimum: 0
        default: 0
    Limit:
      name: limit
      in: query
      required: false
      schema:
        type: integer
        minimum: 1
        default: 100
    Id:
      name: id
      in: path
      required: true
      schema:
        type: string
  responses:
    BadRequest:
      description: Bad Request - Invalid input or malformed request
    Unauthorized:
      description: Unauthorized - Missing or invalid authentication
    InternalServerError:
      description: Internal Server Error - Unexpected server error
    NotFound:
      description: Not Found - Resource does not exist
    Forbidden:
      description: Forbidden - Authenticated but lacking permissions
  schemas:
    List:
      type: object
      required:
        - _id
        - _owner
        - name
      properties:
        _id:
          type: string
        _owner:
          type: string
        name:
          type: string
        description:
          type: string
    ListArray:
      type: array
      items:
        $ref: '#/components/schemas/List'
    Property:
      type: object
      required:
        - _id
        - _owner
        - name
        - shortcode
      properties:
        _id:
          type: string
        _owner:
          type: string
        name:
          type: string
        description:
          type: string
        shortcode:
          type: string
          description: >-
            Unique key used to reference this `Property` in a `Contact`'s
            properties object
    PropertyArray:
      type: array
      items:
        $ref: '#/components/schemas/Property'
    MailingList:
      type: object
      required:
        - _id
        - _owner
        - name
      properties:
        _id:
          type: string
        _owner:
          type: string
        name:
          type: string
        description:
          type: string
        default:
          type: boolean
          description: Whether this is the default `MailingList` ("General communication")
    MailingListArray:
      type: array
      items:
        $ref: '#/components/schemas/MailingList'
    Contact:
      type: object
      required:
        - _id
        - _owner
        - email
      properties:
        _id:
          type: string
        _owner:
          type: string
        email:
          type: string
        name:
          type: string
          description: >-
            Full name, computed from properties.firstname and
            properties.lastname
        properties:
          type: object
          description: >-
            Custom `Property` values, where keys are `Property`
            shortcodes<br>(key-value object)
        subscribed:
          type: boolean
        created:
          type: string
          format: date-time
        lifecycle:
          type: string
        lists:
          type: array
          items:
            type: object
            properties:
              list:
                type: string
              time:
                type: string
                format: date-time
        mailinglists:
          type: array
          items:
            type: object
            properties:
              mailinglist:
                type: string
              status:
                type: string
                enum:
                  - opt-in
                  - opt-out
              updated:
                type: string
                format: date-time
        sessionTrackingConsent:
          type: object
          properties:
            status:
              type: string
              enum:
                - DENIED
                - GRANTED
            updated:
              type: string
              format: date-time
        emailTrackingConsent:
          type: object
          properties:
            status:
              type: string
              enum:
                - DENIED
                - GRANTED
            updated:
              type: string
              format: date-time
    ContactArray:
      type: array
      items:
        $ref: '#/components/schemas/Contact'
    Folder:
      type: object
      required:
        - _id
        - _owner
        - name
      properties:
        _id:
          type: string
        _owner:
          type: string
        name:
          type: string
        description:
          type: string
    FolderArray:
      type: array
      items:
        $ref: '#/components/schemas/Folder'
    Campaign:
      oneOf:
        - $ref: '#/components/schemas/EmailCampaign'
    CampaignArray:
      type: array
      items:
        $ref: '#/components/schemas/Campaign'
    Email:
      type: object
      required:
        - _id
        - _owner
        - name
      properties:
        _id:
          type: string
        _owner:
          type: string
        name:
          type: string
        description:
          type: string
        subject:
          type: string
        preheader:
          type: string
        fromemail:
          type: string
        fromname:
          type: string
        unsubscribetext:
          type: string
        editor:
          type: string
          default: bee
          enum:
            - bee
        body:
          type: string
        json:
          type: string
    EmailArray:
      type: array
      items:
        $ref: '#/components/schemas/Email'
    EmailMetricTotal:
      type: object
      properties:
        _id:
          type: string
          description: Element ID (Campaign or Email)
        sent:
          type: integer
        open:
          type: integer
        uniqueopen:
          type: integer
        click:
          type: integer
        uniqueclick:
          type: integer
        bounce:
          type: integer
        unsubscribe:
          type: integer
        spam:
          type: integer
    EmailMetricTotalArray:
      type: array
      items:
        $ref: '#/components/schemas/EmailMetricTotal'
    EmailMetricDaily:
      type: object
      properties:
        _id:
          type: object
          properties:
            group:
              type: string
              description: Element ID (Campaign or Email)
            year:
              type: integer
            month:
              type: integer
            day:
              type: integer
        sent:
          type: integer
        open:
          type: integer
        uniqueopen:
          type: integer
        click:
          type: integer
        uniqueclick:
          type: integer
        bounce:
          type: integer
        unsubscribe:
          type: integer
        spam:
          type: integer
    EmailMetricDailyArray:
      type: array
      items:
        $ref: '#/components/schemas/EmailMetricDaily'
    Error:
      type: object
      properties:
        error:
          type: string
        code:
          type: integer
        message:
          type: string
    BaseCampaign:
      type: object
      required:
        - _id
        - _owner
        - name
        - type
        - folder
        - type
      properties:
        _id:
          type: string
        _owner:
          type: string
        name:
          type: string
        description:
          type: string
        folder:
          type: string
    EmailCampaignStatus:
      type: string
      enum:
        - d
        - a
        - i
      description: d = draft, a = active, i = inactive
    BatchConfigurationNone:
      type: object
      title: None
      description: Send all emails at once without batching
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - none
    BatchConfigurationFixedCount:
      type: object
      title: Fixed Count
      description: Send a fixed number of batches with optional delay between each
      required:
        - type
        - count
      properties:
        type:
          type: string
          enum:
            - fixed_count
        count:
          type: integer
          description: Number of batches to send
        delay:
          type: string
          format: duration
    BatchConfigurationFixedSize:
      type: object
      title: Fixed Size
      description: Send batches of fixed size with optional delay between each
      required:
        - type
        - size
      properties:
        type:
          type: string
          enum:
            - fixed_size
        size:
          type: integer
          description: Number of emails per batch
        delay:
          type: string
          format: duration
          description: Duration between batches (e.g., PT1H for 1 hour)
    BatchConfiguration:
      oneOf:
        - $ref: '#/components/schemas/BatchConfigurationNone'
        - $ref: '#/components/schemas/BatchConfigurationFixedCount'
        - $ref: '#/components/schemas/BatchConfigurationFixedSize'
      discriminator:
        propertyName: type
        mapping:
          none: '#/components/schemas/BatchConfigurationNone'
          fixed_count: '#/components/schemas/BatchConfigurationFixedCount'
          fixed_size: '#/components/schemas/BatchConfigurationFixedSize'
    EmailVariant:
      type: object
      required:
        - email
        - weight
      properties:
        email:
          type: string
        weight:
          type: integer
          minimum: 0
          maximum: 100
          description: A/B test weight (0-100). Sum of all weights must equal 100.
    CampaignListIncludeAll:
      type: object
      title: All Contacts
      description: Include all contacts
      required:
        - list
      properties:
        list:
          type: 'null'
    CampaignListInclude:
      type: object
      title: Include List
      description: Include contacts from this list
      required:
        - list
      properties:
        list:
          type: string
    CampaignListExclude:
      type: object
      title: Exclude List
      description: Exclude contacts from this list
      required:
        - list
        - excluded
      properties:
        list:
          type: string
        excluded:
          type: boolean
          enum:
            - true
    CampaignList:
      anyOf:
        - $ref: '#/components/schemas/CampaignListIncludeAll'
        - $ref: '#/components/schemas/CampaignListInclude'
        - $ref: '#/components/schemas/CampaignListExclude'
    CampaignMailingList:
      type: object
      required:
        - mailinglist
        - statusFilter
      properties:
        mailinglist:
          type: string
        statusFilter:
          type: integer
          enum:
            - 0
            - 1
            - 2
          description: 0 = opt-in only, 1 = undefined only, 2 = opt-in or undefined
        language:
          type: string
          enum:
            - en
            - fr
    EmailCampaign:
      type: object
      title: EmailCampaign (type=2)
      allOf:
        - $ref: '#/components/schemas/BaseCampaign'
        - type: object
          properties:
            type:
              type: integer
              enum:
                - 2
            status:
              $ref: '#/components/schemas/EmailCampaignStatus'
            start:
              type: string
              format: date-time
            batchConfiguration:
              $ref: '#/components/schemas/BatchConfiguration'
            emails:
              type: array
              items:
                $ref: '#/components/schemas/EmailVariant'
            lists:
              type: array
              items:
                $ref: '#/components/schemas/CampaignList'
            mailinglist:
              $ref: '#/components/schemas/CampaignMailingList'
