﻿---
openapi: 3.1.2
info:
  contact:
    email: help@heymarket.com
    name: API Support
    url: https://heymarket.com
  description: |
    Welcome to Heymarket’s API! Many users access Heymarket via the web app, mobile apps, or one of the out-of-the-box platform integrations.
    However, much of Heymarket’s functionality can also be accessed via the API.
    The flexibility and scalability of the API makes it an excellent choice for integrating Heymarket into existing applications.

    This documentation provides details on: available authentication methods, endpoints that are available and error message information.

    If you want to get right to the action, download the collection to start performing calls from Postman or other similar software.

    All valid HTTP request and response bodies are encoded in JSON. For security purposes API requests must be made over HTTPS. Calls made over plain HTTP or without authentication will fail.

    Base URL: https://api.heymarket.com

    # Rate Limit
    The Heymarket API has a rate limit of 500 requests per minute. Exceeding the rate limit will result in responses with a 429 status code.

    # Request IDs
    Each API request has an associated request identifier. You can find this value in the response headers, under `X-Request-Id`.
    If you need to contact us about a specific request, providing the request identifier will ensure the fastest possible resolution.

    # Webhooks
    You can read more about [getting started with webhooks](https://help.heymarket.com/hc/en-us/articles/4416494130701-Getting-Started-with-Webhooks) from our articles collection.

    # Additional Admin APIs
        For more information about additional Admin APIs, please contact us at <help@heymarket.com>.
  license:
    name: Heymarket
    url: https://heymarket.com/legal
  termsOfService: https://heymarket.com/tos
  title: Heymarket API
  version: '1.0'
tags:
- name: Inboxes
  description: |
    The `Inbox` object represents a read only view of the team's inbox(es) in Heymarket.

    Any API requests that deal with message sending or scheduling will require an inbox ID as a parameter.

    #### The Inbox object

    | Attributes | Description                                                           |
    | ---------- | --------------------------------------------------------------------- |
    | `id`       | Unique identifier for the object                                      |
    | `name`    | Name of the inbox                                                     |
    | `team`     | Unique identifier for your team                                        |
    | `members`  | Unique identifier for your team members who have access to the inbox |
    | `phones`   | Heymarket phone number(s) associated with the inbox                    |
- name: Users
  description: |
    The `User` object represents a team user in Heymarket.

    #### The User object

    | Attributes         | Description                                                   |
    | ------------------ | ------------------------------------------------------------- |
    | `id`               | Unique identifier for the object                              |
    | `name`             | Name of the user                                              |
    | `email`            | Email address of the user                                     |
    | `phone`            | Phone number of the user                                      |
    | `role_id`          | Unique identifier of the role of the user within the team     |
    | `team_id`          | Unique identifier for the team the user belongs to            |
    | `created_at`       | Creation date of the team user                                |
    | `updated_at`       | Last updated date of the team user                            |
    | `user_created_at`  | Creation date of the base user                                |
    | `user_updated_at`  | Last updated date of the base user                            |
- name: User Groups
  description: |
    The `User Group` object represents a named group of team users in Heymarket.

    Groups are created and their membership managed in the Heymarket web app. They are used to
    scope objects such as lists and templates, and to address several teammates at once.

    #### The User Group object

    | Attributes    | Description                                                        |
    | ------------- | ------------------------------------------------------------------ |
    | `id`          | Unique identifier for the object                                   |
    | `name`        | Name of the group                                                  |
    | `description` | Description of the group. Omitted when the group has none          |
    | `member_ids`  | Unique identifiers of the group's members                          |
    | `members`     | The group's members, as `User` objects                             |
- name: Team
  description: |
    The `Team` object represents a read only view of the team in Heymarket.

    #### The Team object

    | Attributes | Description                                                           |
    | ---------- | --------------------------------------------------------------------- |
    | `id`       | Unique identifier for the object                                      |
    | `name`    | Name of the team                                                     |
    | `list_size` | List size limit
    | `members`  | Unique identifiers for the users who have access to the team as well as their role within the team|
- name: Contacts
  description: |
    The `Contact` object is a Heymarket contact. The contacts in Heymarket are used for storing information such as name, phone number, and email address. Contacts also store custom fields which can be used for search, list creation, and merge tokens while sending messages.

    #### The Contact object

    | Attributes         | Description                                                                |
    | ------------------ | -------------------------------------------------------------------------- |
    | `id`               | Unique identifier for the object                                           |
    | `display_name`     | Display name, usually a combination of first and last name                 |
    | `first`            | First name                                                                 |
    | `last`             | Last name                                                                  |
    | `email`            | Email address                                                              |
    | `phone`            | Phone number                                                               |
    | `custom`           | Custom fields with value if present, only lists contact field ID and value |
    | `team_id`          | Unique identifier for your Heymarket team                                  |
    | `creator_id`       | Unique identifier for the user who created the contact, same as the member ID returned by Get Inboxes                        |
    | `shared`           | Boolean value for whether the contact is shared with team members          |
    | `created`          | Creation date                                                              |
    | `updated`          | Last updated date                                                          |
    | `rev`              | Last revision number                                                       |
    | `op`               | Operation performed                                                        |
    | `assigned_user_id` | Contact Owner ID                                                           |
    | `tags`             | Array of up to 5 `tag_id` objects                                                         |
    | `is_opted_out`     | Is this contact opted out of messaging?                                    |
- name: Conversations
  description: The `Conversation` object represents a messaging thread in Heymarket.
- name: Lists
  description: |
    The `List` object in Heymarket is used for organizing multiple phone numbers. You can send a single message to a list, the contacts will never see each other and can reply privately. Lists are also used as a source of contacts for your campaigns.

    #### The List object

    | Attributes   | Description                                         |
    | ------------ | --------------------------------------------------- |
    | `id`         | Unique identifier for the object                    |
    | `name`       | List name                                           |
    | `local_id`   | Client provided unique identifier for the object    |
    | `targets`    | Object containing multiple `ListTarget` objects     |
    | `team_id`    | Unique identifier for your Heymarket team            |
    | `creator_id` | Unique identifier for the user who created the list |
    | `shared`     | If the list is shared with team members                    |
    | `created`    | Creation date                                       |
    | `updated`    | Last updated date                                   |
    | `rev`        | Last revision number                                |
    | `op`         | Operation performed                                        |

    The `targets` object is a light weight representation of a contact. `phone` is used as a key and it has name fields as optional attributes which will be used as a merge token during message sending if there is no real Heymarket contact available for this phone number.

    #### The ListTarget object

    | Attributes | Description             |
    | ---------- | ----------------------- |
    | `f`        | First name (if available) |
    | `l`        | Last name (if available)  |
- name: Templates
  description: |
    The `Template` object in Heymarket is a pre-defined message with text and media. Templates can also contain merge tokens which will insert the appropriate custom field values when sending the message. The main body of the template is stored in the `content` attribute.

    #### The Template object

    | Attributes   | Description                                         |
    | ------------ | --------------------------------------------------- |
    | `id`         | Unique identifier for the object                    |
    | `name`       | Template name                                       |
    | `local_id`   | Client provided unique identifier for the object    |
    | `content`    | Object representing template content                |
    | `team_id`    | Unique identifier for your Heymarket team            |
    | `creator_id` | Unique identifier for the user who created the template, same as the member ID returned by Get Inboxes |
    | `shared`     | If the template is shared with team members                |
    | `created`    | Creation date                                       |
    | `updated`    | Last updated date                                   |
    | `rev`        | Last revision number                                |
    | `op`         | Operation performed                                        |

    #### The Content object

    | Attributes | Description                     |
    | ---------- | ------------------------------- |
    | `text`     | Message text                    |
    | `gallery`  | Array of `GalleryEntry` objects |
- name: Messages
  description: |
    The `Messages` API is a powerful Heymarket feature. With it you can send messages to individual contacts or a list of contacts.
    It has all the features of Heymarket's native clients, including sending messages from a template, sending media, and inserting merge tokens.
- name: Schedule
  description: 'With the `Scheduling` API you can schedule a message to be sent to an individual contact or to a list.'
- name: Surveys
  description: 'Ask your customers a set of standardized questions to quickly understand their experience with your company.'
- name: Tags
  description: |
    The `Tag` object in Heymarket.
    #### The Tag object
    | Attributes   | Description                                         |
    | ------------ | --------------------------------------------------- |
    | `id`         | Unique identifier for the object                    |
    | `tag`        | Tag name                                            |
    | `color`      | Color                                               |
    | `team_id`    | Unique identifier for your Heymarket team           |
    | `created`    | Creation date                                       |
    | `updated`    | Last updated date                                   |
    | `rev`        | Last revision number                                |
- name: Links
  description: |
    The `Link` object represents a tracked short URL owned by your team.

    Link tracking must be enabled for your team to use these endpoints. Requests from teams without the
    link tracking feature return `403` with the message `link_tracking_not_available`.

    All link operations are scoped to the team identified by your API credentials — links belonging to
    other teams cannot be read, updated, or deleted, and lookups for them return `404`.

    #### The Link object

    | Attributes | Description |
    | ---------- | ----------- |
    | `id` | Unique identifier for the object |
    | `short_code` | Code segment of the short URL |
    | `short_url` | Full short URL to include in messages |
    | `original_url` | Destination URL the short link redirects to |
    | `team_id` | Unique identifier for your team |
    | `target` | Target the link is attributed to (optional) |
    | `conversation_id` | Conversation the link is attributed to (optional) |
    | `broadcast_id` | Broadcast the link is attributed to (optional) |
    | `campaign_step_id` | Campaign step the link is attributed to (optional) |
    | `template_id` | Template the link is attributed to (optional) |
    | `last_visited_at` | Timestamp of the most recent click (optional) |
    | `created_at` | Creation date |
- name: Webhooks
  description: Events Heymarket sends to webhook URLs configured for a team.
paths:
  "/v1/inboxes":
    get:
      description: Get all the Inbox objects associated with your team.
      responses:
        '200':
          description: Inboxes in a team
          content:
            application/json:
              schema:
                items:
                  "$ref": "#/components/schemas/doc.Inbox"
                type: array
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Get Inboxes
      tags:
      - Inboxes
      operationId: v1GetInboxes
  "/v1/users/get":
    get:
      description: "Get User objects associated with your team either by email or
        by phone.\n\n#### Request body attributes\n\n| Attributes | Description                                                                                                                                                                                      |\n|
        ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        |\n| `phone`    | **array of strings (optional)** <br>Phone numbers of users
        to search for within the team.<br> [E.164](https://en.wikipedia.org/wiki/E.164)
        number format except the plus sign (e.g. 14155553434).|\n| `email`    | **array
        of strings (optional)** <br>Email addresses of users to search for within
        the team.                                                                                                      |\n
        \nA list of your team users will be returned within the `memberships` attribute.\n\n####
        Example of Request body for `User` get\nBasic `/users/get` request body:\n```json\n\n
        \   {\n      \"phone\": [\"15105553344\", \"15105553345\"],\n      \"email\":
        [\"help@heymarket.com\", \"example@heymarket.com\"]\n    }\n\n```\n\nLegacy
        endpoint: this GET operation reads a JSON request body. Some API clients and
        playgrounds do not support GET request bodies.\n"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                phone:
                  type: array
                  items:
                    type: string
                email:
                  type: array
                  items:
                    type: string
              example:
                phone:
                - '15105553344'
                - '15105553345'
                email:
                - help@heymarket.com
                - example@heymarket.com
        description: User fetch json
        required: true
      responses:
        '200':
          description: Team user information
          content:
            application/json:
              schema:
                type: object
                properties:
                  memberships:
                    type: array
                    items:
                      "$ref": "#/components/schemas/doc.TeamUser"
              examples:
                response:
                  value:
                    memberships:
                    - id: 1
                      phone: '12345678900'
                      name: John Doe
                      email: john.doe@email.com
                      role_id: 1
                      created_at: '2018-01-01T00:00:00Z'
                      updated_at: '2018-01-01T00:00:00Z'
                      user_created_at: '2018-01-01T00:00:00Z'
                      user_updated_at: '2018-01-01T00:00:00Z'
                      team_id: 1
        '400':
          description: bad_data/error
          content:
            application/json:
              schema:
                type: string
              examples:
                response:
                  value: error
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Get Users
      tags:
      - Users
      operationId: v1GetUsers
      x-heymarket-legacy-get-body: true
  "/v1/users/update":
    post:
      description: "Update User objects associated with your team.\n\n#### Request
        body attributes\n\n| Attributes | Description |\n| --- | --- |\n| `users`
        | **array of update user objects (required)** <br>Array of update user objects.
        |\n\n#### Update user object attributes\n\n| Attributes | Description |\n|
        --- | --- |\n| `id` | **integer (required)** <br>Unique identifier for the
        object. |\n| `first_name` | **string (optional)** <br>Updated first name of
        the user.<br>`first_name` is linked to `last_name` and should be provided
        together or one will be overwritten. |\n| `last_name` | **string (optional)**
        <br>Updated last name of the user.<br>`last_name` is linked to `first_name`
        so both should be provided at all times or one will be overwritten. |\n| `email`
        | **string (optional)** <br>Updated email address of the user. |\n| `phone`
        | **string (optional)** <br>Updated phone number of the user.<br> [E.164](https://en.wikipedia.org/wiki/E.164)
        number format except the plus sign (e.g. 14155553434). |\n| `role_id` | **integer
        (optional)** <br>Updated unique identifier of the team user role. |\n\nA list
        of your updated team users will be returned within the `memberships` attribute.\n\n####
        Example of Request body for `User` update\nBasic `/users/update` request body:\n```json\n
        \ \n    {\n      \"users\": [\n        {\n          \"id\": 1,\n          \"first_name\":
        \"John\",\n          \"last_name\": \"Doe\"\n        },\n        {\n          \"id\":
        2,\n          \"role_id\": 4\n        }\n      ]\n    }\n\n```\n"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                users:
                  type: array
                  items:
                    type: object
                    properties:
                      id:
                        type: integer
                      first_name:
                        type: string
                      last_name:
                        type: string
                      email:
                        type: string
                      phone:
                        type: string
                      role_id:
                        type: integer
              example:
                users:
                - id: 1
                  first_name: John
                  last_name: Doe
                - id: 2
                  role_id: 4
        description: User fetch json
        required: true
      responses:
        '200':
          description: Updated team user information
          content:
            application/json:
              schema:
                type: object
                properties:
                  memberships:
                    type: array
                    items:
                      "$ref": "#/components/schemas/doc.TeamUser"
              examples:
                response:
                  value:
                    memberships:
                    - id: 1
                      phone: '12345678900'
                      name: John Doe
                      email: john.doe@email.com
                      role_id: 1
                      created_at: '2018-01-01T00:00:00Z'
                      updated_at: '2018-01-01T00:00:00Z'
                      user_created_at: '2018-01-01T00:00:00Z'
                      user_updated_at: '2018-01-01T00:00:00Z'
                      team_id: 1
        '400':
          description: bad_data/error
          content:
            application/json:
              schema:
                type: string
              examples:
                response:
                  value: error
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Update Users
      tags:
      - Users
      operationId: v1UpdateUsers
  "/v1/user-groups":
    post:
      description: |
        List the user groups in your team, each with its members.

        Sending an empty JSON body returns every group in the team, ordered by name. A group with
        no members is returned with `member_ids` and `members` as empty arrays, never `null`.

        Only current members of the team are returned. A user who has been removed from the team
        is omitted from both `member_ids` and `members`, so the two attributes always agree and
        every id in `member_ids` resolves to an object in `members`.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `group_ids` | **array of integers (optional)** <br> Return only these groups. Omit the attribute, or send an empty array, to return every group in the team. Ids that do not belong to your team are ignored rather than rejected. At most 200 ids (`too_many_group_ids` above that). |

        #### Example request body
        ```json

            {
              "group_ids": [7, 9]
            }

        ```
      requestBody:
        content:
          application/json:
            schema:
              properties:
                group_ids:
                  items:
                    type: integer
                  type: array
              type: object
      responses:
        '200':
          description: user groups in a team, each with its members
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.UserGroupsResp"
        '400':
          description: bad_data, too_many_group_ids
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.V2ErrorResp"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: List User Groups
      tags:
      - User Groups
      operationId: listUserGroups
  "/v1/team":
    get:
      description: Get the object of the Heymarket team.
      responses:
        '200':
          description: Team Object
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.Team"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Get Team
      tags:
      - Team
      operationId: v1GetTeam
  "/v1/contact":
    post:
      description: |
        Create a contact in your team.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `phone` | **string (required)** <br> [E.164](https://en.wikipedia.org/wiki/E.164) number format except the plus sign (e.g. 14155553434). |
        | `first` | **string (optional)** <br> First name. |
        | `last` | **string (optional)** <br> Last name. |
        | `display_name` | **string (optional)** <br> Display name, usually combination of first and last name unless a different name is provided. |
        | `email` | **string (optional)** <br> Email address|
        | `custom` | **object (optional)** <br> Contact custom fields. It is an Object of custom field ID and values. |
        | `avatar` | **string (optional)** <br> URL for the Avatar image |
        | `assignee_id` | **integer (optional)** <br> Contact Owner ID. It is the ID of a team member. <br> Use `-1` to unassign the Contact Owner. |
        | `tags` | **array of up to 5 `tag_id` objects (optional)** <br>  Contact tags. |
        | `is_opted_out` | **boolean** <br> Is this contact opted out of messaging? |

        A list of your team contact custom fields can be fetched from `/v1/contact-fields`.

        #### Example of Request body for `Contact` create
        Basic contact request body:
        ```json

            {
              "phone": "15105553344",
              "last": "Contact",
              "first": "API",
              "display_name": "API Contact",
              "email": "help@heymarket.com",
              "custom": {},
              "is_opted_out": true
            }

        ```
      requestBody:
        "$ref": "#/components/requestBodies/doc.V2ContactReq"
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.V2CreateUpdateResp"
        '400':
          description: error
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Create Contact
      tags:
      - Contacts
      operationId: v1CreateContact
  "/v1/contact/{id}":
    delete:
      description: Delete a Contact by its ID.
      parameters:
      - description: Contact ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      responses:
        '200':
          description: ok
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: ok
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Delete Contact
      tags:
      - Contacts
      operationId: v1DeleteContact
    get:
      description: Get a Contact by its ID.
      parameters:
      - description: Contact ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.Contact"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Get Contact
      tags:
      - Contacts
      operationId: v1GetContact
    put:
      description: |
        Update a Contact by its ID.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `phone` | **string (required)** <br> [E.164](https://en.wikipedia.org/wiki/E.164) number format except the plus sign (e.g. 14155553434). |
        | `first` | **string (optional)** <br> First name. |
        | `last` | **string (optional)** <br> Last name. |
        | `display_name` | **string (optional)** <br> Display name, usually combination of first and last name unless a different name is provided. |
        | `email` | **string (optional)** <br> Email address|
        | `custom` | **object (optional)** <br> Contact custom fields. It is an Object where keys are custom field IDs (as strings) and values are the field values. **Important:** You must use the numeric ID of the custom field as the key, not the field name. |
        | `avatar` | **string (optional)** <br> URL for the Avatar image |
        | `assignee_id` | **integer (optional)** <br> Contact Owner ID. It is the ID of a team member. <br> Use `-1` to unassign the Contact Owner. |
        | `tags` | **array of up to 5 `tag_id` objects (optional)** <br>  Contact tags. |
        | `is_opted_out` | **boolean** <br> Is this contact opted out of messaging? |

        #### Query Parameters

        | Parameter | Description |
        | --- | --- |
        | `overwrite` | **boolean (optional)** <br> When `true`, replaces all existing custom fields with the provided ones. When `false` (default), merges the provided custom fields with existing ones. Note: the contact's name is always overwritten regardless of this parameter. |

        #### How to use Custom Fields

        To update custom fields, you need to use the **numeric ID** of the custom field, not its name. Follow these steps:

        1. First, call `POST /v1/contact-fields` to retrieve all custom fields and their IDs
        2. Find the ID of the custom field you want to update
        3. Use that ID as a **string key** in the `custom` object

        #### Examples

        **Example 1: Basic contact update**
        ```json
        {
          "phone": "14155551234",
          "first": "Jane",
          "last": "Smith",
          "email": "jane.smith@example.com"
        }
        ```

        **Example 2: Update with custom fields**

        To include custom fields, first retrieve your custom field IDs from `POST /v1/contact-fields`.
        Then use the numeric ID (as a string) as the key:
        ```json
        {
          "phone": "14155551234",
          "first": "Jane",
          "last": "Smith",
          "email": "jane.smith@example.com",
          "custom": {
            "123": "Premium",           // Custom field ID 123 = "Customer Tier"
            "456": "2023-11-15",        // Custom field ID 456 = "Sign Up Date"
            "789": "Active"             // Custom field ID 789 = "Status"
          }
        }
        ```

        **Example 3: Update only custom fields**
        ```json
        {
          "phone": "14155551234",       // phone is required
          "custom": {
            "123": "VIP",               // Update "Customer Tier" to VIP
            "456": "2024-01-20"         // Update "Sign Up Date"
          }
        }
        ```
        **Note:** A list of your team contact custom fields can be fetched from `POST /v1/contact-fields`.
      parameters:
      - description: Contact ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      - description: Overwrite existing custom fields. Note that the contact's name
          is overwritten regardless of this parameter
        in: query
        name: overwrite
        required: false
        schema:
          type: boolean
      requestBody:
        "$ref": "#/components/requestBodies/doc.V2ContactUpdateReq"
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.V2CreateUpdateResp"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Update Contact
      tags:
      - Contacts
      operationId: v1UpdateContact
  "/v1/contact-fields":
    post:
      description: |
        Get all your contact custom fields for your team.
        Does not use pagination. Request consists of an empty JSON body.

        #### Custom Field Object

        | Attributes | Description |
        | --- | --- |
        | `id` | Unique identifier for the object |
        | `title` | field name |
        | `tid` | Unique identifier for your Heymarket team |
        | `uid` | Unique identifier for the user who created the Custom Field |
        | `rev` | Last revision number |
        | `op` | Operation performed |
      requestBody:
        "$ref": "#/components/requestBodies/pagination.V2PaginationParams"
      responses:
        '200':
          description: array of Contact fields in a team
          content:
            application/json:
              schema:
                items:
                  "$ref": "#/components/schemas/doc.ContactField"
                type: array
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Contact fields List
      tags:
      - Contacts
      operationId: v1ContactFieldsList
  "/v1/contacts/fields":
    post:
      description: |
        Create one or more contact custom fields for your team.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `title` | **string (required)** <br> Field name. |
      requestBody:
        content:
          application/json:
            schema:
              type: array
              items:
                "$ref": "#/components/schemas/doc.V2ContactFieldReq"
        description: Contact field definitions
        required: true
      responses:
        '200':
          description: Created contact fields
          content:
            application/json:
              schema:
                type: array
                items:
                  "$ref": "#/components/schemas/doc.CreateContactFieldResponse"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Create contact fields
      tags:
      - Contacts
      operationId: v1CreateContactFields
  "/v1/contact/get":
    post:
      description: |
        Fetch a contact by phone number, or by the combination of `type` and `external_id`.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `phone` | **string (optional)** <br> Phone number of the contact in this team you want to fetch. |
        | `type` | **string (optional)** <br> External service type. Must be provided with `external_id`. |
        | `external_id` | **string (optional)** <br> External contact ID. Must be provided with `type`. |
      requestBody:
        "$ref": "#/components/requestBodies/doc.V2ContactGetReq"
      responses:
        '200':
          description: Contact in team
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.Contact"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Paginate Contact
      tags:
      - Contacts
      operationId: v1ListContact
  "/v1/contacts":
    post:
      description: |
        Get all the contacts for a team. Requires paginating to fetch all.
        Sending a request with empty JSON body will return the 30 most recent Contacts.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `date` | **string (optional)** <br> Last timestamp, usually `updated` attribute of last returned item from request.<br>[RFC 3339](http://www.faqs.org/rfcs/rfc3339.html)  format.|
      requestBody:
        "$ref": "#/components/requestBodies/pagination.V2PaginationParams"
      responses:
        '200':
          description: array of Contacts in team
          content:
            application/json:
              schema:
                items:
                  "$ref": "#/components/schemas/doc.Contact"
                type: array
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Paginate Contact
      tags:
      - Contacts
      operationId: v1ListContactPostContacts
  "/v1/contacts/count":
    post:
      description: |
        Get the count of all contacts from a team that would be returned with the query parameters provided.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `date` | **string (optional)** <br> Last timestamp, usually `updated` attribute of last returned item from request.<br>[RFC 3339](http://www.faqs.org/rfcs/rfc3339.html)  format.|
      requestBody:
        "$ref": "#/components/requestBodies/pagination.V2PaginationParams"
      responses:
        '200':
          description: count of Contacts in team
          content:
            application/json:
              schema:
                type: string
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Contact count
      tags:
      - Contacts
      operationId: v1ContactCount
  "/v1/contact/status":
    post:
      description: |
        Get the status of a given contact to see if they are unsubscribed and/or blocked.

        For teams on per-inbox opt-out mode, the response additionally includes an
        `inbox_opt_outs` array listing the specific inboxes the contact is opted out of, and
        `global_opt_out` telling you whether a team-wide opt-out row also exists. Both must be
        read together: a number can carry a global row *and* scoped rows, in which case every
        inbox is blocked even though `inbox_opt_outs` lists only some of them.
        The `unsubscribed` / `unsubscribed_contact` / `unsubscribed_admin` booleans keep
        their existing any-row semantics and are unaffected.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `id` | **integer (required without `phone`)** <br> Heymarket ID of the contact status you want to fetch. |
        | `phone` | **string (required without `id`)** <br> Phone number of the contact whose status you want to fetch. |
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.ContactStatusRequest"
        description: Heymarket ID of the contact
        required: true
      responses:
        '200':
          description: Status of the contact
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.ContactStatusResponse"
        '400':
          description: error
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Contact status
      tags:
      - Contacts
      operationId: v1ContactStatus
  "/v1/contact/set_status":
    post:
      description: |
        Set the status for a contact.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `id` | **integer (required without `phone`)** <br> Heymarket ID of the contact whose status you want to set. |
        | `phone` | **string (required without `id`)** <br> Phone number of the contact whose status you want to set. |
        | `status` | **string (required)** <br> The status to set to (active, blocked, unblocked, subscribed, unsubscribed). |
        | `inbox_ids` | **array of integers (optional)** <br> Scope a `subscribed`/`active`/`unsubscribed` change to specific inboxes instead of team-wide. Applies only to teams on per-inbox opt-out mode (requires the inbox-level opt-out entitlement). Omit to keep the existing global behavior; an empty array is rejected. Every id must belong to the team. Max 500. Ignored for `blocked`/`unblocked`. |
        | `user_id` | **integer (optional)** <br> Acting user for a `subscribed`/`active`/`unsubscribed` change, recorded on the row. On a team using per-inbox opt-out, supplying it without `inbox_ids` scopes the change to that user's inboxes. On a team using global opt-out (or one without the entitlement) the change stays team-wide and the response reports `"applied_scope": "team_wide"`. Must be an active member of the authenticated team. Ignored for `blocked`/`unblocked`. |

        A `subscribed` change against a contact who holds a **removable** team-wide opt-out widens
        to a full re-subscribe, even when `inbox_ids` or `user_id` scoped the request. A team-wide
        opt-out — set before the team moved to per-inbox mode, or written automatically after a
        delivery failure — blocks every inbox, so clearing one inbox's row would leave the contact
        blocked everywhere while reporting success. The widened re-subscribe removes the contact's
        opt-outs from **every** inbox on the team, including inboxes `inbox_ids` did not name and
        inboxes the `user_id` cannot access.

        An opt-out the contact set themselves (e.g. by replying `STOP` or `STOPALL`) is never
        removed, so a contact-set team-wide opt-out does not widen the request: the removal stays on
        the inboxes you asked for, `applied_scope` is absent, and that row keeps blocking every
        inbox. A `200` therefore does not mean the contact is reachable again — read
        `/v1/contact/status` if you need the resulting state. When a contact-set opt-out is all that
        is left to remove — the inboxes you named hold no agent-set row of their own, or an unscoped
        request finds only contact-set rows — the request returns `403 set_from_contact`.
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.ContactSetStatusRequest"
        description: Heymarket ID of the contact and status to set
        required: true
      responses:
        '200':
          description: >-
            Status set. Returns the text body `success`, except when the change landed team-wide
            although the request tried to scope it: that answers JSON with `applied_scope: team_wide`
            and no inbox list, so a wider-than-requested scope is visible in the body. Two cases
            reach it — `user_id` supplied on a team whose mode keeps every change team-wide, and a
            `subscribed`/`active` request scoped with `inbox_ids` against a removable team-wide
            opt-out, which is revoked in full across every inbox on the team.
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: success
            application/json:
              schema:
                type: object
                properties:
                  applied_scope:
                    type: string
                    enum: [team_wide]
              examples:
                widened:
                  value:
                    applied_scope: team_wide
        '400':
          description: >-
            Bad data provided (text/plain), or an inbox scoping problem (application/json with a
            stable `code`): `inbox_ids_empty`, `too_many_inbox_ids`, `invalid_inbox_ids` (with
            `invalid_inbox_ids` listing the offending ids), `no_valid_inboxes`,
            `global_opt_out_mode`, `invalid_user_id`, `user_inbox_access_denied` (with `invalid_inbox_ids` listing the inboxes the user cannot access).
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: error
            application/json:
              schema:
                type: object
        '403':
          description: >-
            Inbox scoping requested but inbox-level opt-out is not available for this team
            (code `not_entitled`), or a `subscribed`/`active` change that found nothing removable
            because the opt-outs still blocking the contact were set by the contact
            (code `set_from_contact`).
          content:
            application/json:
              schema:
                type: object
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Set contact status
      tags:
      - Contacts
      operationId: v1SetContactStatus
  "/v1/unsubscribe":
    post:
      description: |
        Opt a phone number out of messaging.

        By default this creates a global (team-wide) opt-out that blocks the number on every inbox.

        For teams on per-inbox opt-out mode, you may pass an optional `inbox_ids` array to opt the
        number out of only those inboxes (a scoped opt-out). Omitting `inbox_ids` keeps the existing
        global behavior. Scoped opt-outs require the inbox-level opt-out entitlement; if the team is
        not entitled a `403` is returned, and if the team is not on per-inbox mode a `400` is returned.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `number` | **string (required)** <br> Phone number to opt out. [E.164](https://en.wikipedia.org/wiki/E.164) number format except the plus sign (e.g. 14155553434). |
        | `inbox_ids` | **array of integers (optional)** <br> Inboxes to scope the opt-out to. Per-inbox opt-out mode only. Omit for a global (team-wide) opt-out; an empty array is rejected. Every id must belong to the team. Max 500. |
        | `user_id` | **integer (optional)** <br> Acting user, recorded on the created rows. On a team using per-inbox opt-out, supplying it without `inbox_ids` scopes the opt-out to that user's inboxes. On a team using global opt-out (or one without the entitlement) the opt-out stays team-wide and the response reports `"applied_scope": "team_wide"`. Must be an active member of the authenticated team. |
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.UnsubscribeRequest"
        required: true
      responses:
        '201':
          description: >-
            Number opted out. A global request with neither `inbox_ids` nor `user_id` returns the
            text body `ok`; a scoped request returns JSON listing the inboxes the opt-out landed on.
            A request carrying `user_id` that the team's mode kept team-wide answers JSON with
            `applied_scope: team_wide` and no inbox list, so a wider-than-requested scope is always
            visible in the body.
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: ok
            application/json:
              schema:
                type: object
                properties:
                  opted_out_inbox_ids:
                    type: array
                    items:
                      type: integer
                  applied_scope:
                    type: string
                    enum: [team_wide]
                    description: >-
                      Present only when `user_id` was supplied and the opt-out was applied
                      team-wide because the team is on global opt-out mode or lacks the
                      inbox-level opt-out entitlement. It replaces the inbox list rather than
                      accompanying it.
              examples:
                scoped:
                  value:
                    opted_out_inbox_ids: [2, 5]
                widened:
                  value:
                    applied_scope: team_wide
        '400':
          description: >-
            Invalid phone number or malformed body (text/plain), or an inbox scoping problem
            (application/json with a `code`): `inbox_ids_empty`, `too_many_inbox_ids`,
            `invalid_inbox_ids` (with `invalid_inbox_ids` listing the offending ids),
            `no_valid_inboxes`, `global_opt_out_mode`, `invalid_user_id`, `user_inbox_access_denied` (with `invalid_inbox_ids` listing the inboxes the user cannot access).
          content:
            text/plain:
              schema:
                type: string
            application/json:
              schema:
                type: object
        '403':
          description: Inbox-level opt-out is not available for this team (code `not_entitled`).
          content:
            application/json:
              schema:
                type: object
        '422':
          description: >-
            Scoped requests only. The opt-out landed on some inboxes but not all
            (code `partial_inbox_failure`); the body lists `opted_out_inbox_ids` and
            `failed_inbox_ids` so a client can retry the remainder.
          content:
            application/json:
              schema:
                type: object
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Opt out a number
      tags:
      - Contacts
      operationId: v1Unsubscribe
  "/v1/unsubscribe/{number}":
    delete:
      description: |
        Opt a phone number back in ("subscribe").

        By default this removes every opt-out row for the number, re-subscribing it team-wide.

        For teams on per-inbox opt-out mode, you may pass the optional repeatable `inbox_id` query
        parameter to opt the number back into only those inboxes (removing just those scoped rows and
        leaving other-inbox opt-outs in place). Omitting `inbox_id` keeps the existing remove-all
        behavior. Scoped opt-in requires the inbox-level opt-out entitlement (`403` if not entitled)
        and the team to be on per-inbox mode (`400` otherwise).

        **A removable team-wide opt-out widens the request to a full re-subscribe**, even when it is
        scoped with `inbox_id` or `user_id`. A team-wide opt-out — set before the team moved to
        per-inbox mode, or written automatically after a delivery failure — blocks every inbox, so
        removing one inbox's scoped row would leave the number blocked everywhere while reporting
        success. The widened re-subscribe removes the number's opt-outs from **every** inbox on the
        team, including inboxes the request did not name and inboxes the `user_id` cannot access.
        The scoped response shape is unchanged in this case.

        An opt-out the contact set themselves (e.g. by replying `STOP` or `STOPALL`) is never
        removed, so a contact-set team-wide opt-out does not widen the request: the removal stays on
        the inboxes you asked for, `applied_scope` is absent, and that row keeps blocking every
        inbox. A `200` with an `opted_in_inbox_ids` list therefore does not mean the number is
        reachable again — read `/v1/contact/status` if you need the resulting state. When a
        contact-set opt-out is all that is left to remove — the inboxes you named hold no agent-set
        row of their own, or an unscoped request finds only contact-set rows — the request returns
        `403 set_from_contact`.
      parameters:
      - description: The phone number to opt back in. [E.164](https://en.wikipedia.org/wiki/E.164) number format except the plus sign (e.g. 14155553434).
        in: path
        name: number
        required: true
        schema:
          type: string
      - description: Inbox id(s) to scope the opt-in to. Repeat the parameter for multiple inboxes (e.g. `?inbox_id=2&inbox_id=5`). Per-inbox opt-out mode only. Omit for a team-wide re-subscribe, or pass user_id to cover that user's inboxes.
        in: query
        name: inbox_id
        required: false
        explode: true
        schema:
          type: array
          items:
            type: integer
      - description: >-
          Optional acting user. On a team using per-inbox opt-out, supplying user_id without
          inbox_id opts the number back into the inboxes that user belongs to (skipping inboxes with
          no phone number). With inbox_id it does not change the scope, but every requested inbox
          must be one the user can access. The user must be an active member of the authenticated
          team. On a team using global opt-out this parameter has no effect on scope. Opt-outs the
          contact set themselves are never removed, so the number can stay opted out after a
          successful request.
        in: query
        name: user_id
        required: false
        schema:
          type: integer
      responses:
        '200':
          description: >-
            Number opted back in. A team-wide request (neither `inbox_id` nor `user_id`) returns the
            text body `ok`; a scoped request returns JSON listing the inboxes actually opted back in
            — inboxes skipped because the contact set the opt-out themselves, or had no scoped row,
            are absent. A request carrying `user_id` that the team's mode kept team-wide answers JSON
            with `applied_scope: team_wide` and no inbox list, since that lane removes every
            removable row rather than the named user's inboxes. A scoped request that hit a
            removable team-wide opt-out reports `applied_scope: team_wide` alongside the inbox list,
            because that row belongs to no inbox and an otherwise empty `opted_in_inbox_ids` would
            look identical to a request that removed nothing.
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: ok
            application/json:
              schema:
                type: object
                properties:
                  opted_in_inbox_ids:
                    type: array
                    items:
                      type: integer
                  applied_scope:
                    type: string
                    enum: [team_wide]
                    description: >-
                      Present when the re-subscribe was applied team-wide, removing every removable
                      row on the team: either `user_id` was supplied and the team is on global
                      opt-out mode or lacks the inbox-level opt-out entitlement (no inbox list is
                      returned), or a scoped request hit a removable team-wide opt-out (the inbox
                      list is still returned, naming the inbox-scoped rows that went with it,
                      including inboxes outside the requested scope).
              examples:
                scoped:
                  value:
                    opted_in_inbox_ids: [2]
                widened:
                  value:
                    applied_scope: team_wide
                team_wide_row_revoked:
                  value:
                    opted_in_inbox_ids: []
                    applied_scope: team_wide
        '400':
          description: Invalid phone number, or inbox scoping requested but the team is not on per-inbox opt-out mode / no valid inboxes.
          content:
            application/json:
              schema:
                type: object
        '403':
          description: >-
            Inbox-level opt-out is not available for this team (code `not_entitled`), or the request
            found nothing removable because the opt-outs still blocking the number were set by the
            contact (code `set_from_contact`).
          content:
            application/json:
              schema:
                type: object
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Opt in a number
      tags:
      - Contacts
      operationId: v1Subscribe
  "/v1/unsubscribed_numbers":
    post:
      description: |
        Paginated sync feed of the team's opt-out rows. The feed includes both live rows and
        tombstones: rows that were re-subscribed are soft-deleted and remain visible here (and
        only here) so an external sync service can observe re-subscribes.

        #### Semantics

        - `deleted_at` null = currently opted out, set = re-subscribed at that time.
        - Consumers must key on (`number` + `inbox_id`/global scope), not row `id` — an identical
          re-opt-out mints a new row `id` and hard-purges the superseded tombstone. State remains
          correct because the new live row arrives at a later cursor position.
        - Rows are ordered ascending by (`updated_at`, `id`). `updated_at` moves on opt-out
          creation and on re-subscribe (soft delete).

        #### Pagination

        Cursor = pass the last row's `updated_at` as `date` and `id` as `id`. An empty JSON body
        returns the first page (oldest rows first). Keep polling with the last returned row's
        cursor; an empty array means you are caught up.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `date` | **string (optional)** <br> Cursor: `updated_at` of the last returned row, [RFC 3339](http://www.faqs.org/rfcs/rfc3339.html) format. Omit for the first page. |
        | `id` | **integer (optional)** <br> Cursor: `id` of the last returned row. Breaks ties between rows sharing the same `updated_at`; always pass it together with `date`. |
        | `limit` | **integer (optional)** <br> Rows per page. Defaults to 30, capped at 100. |
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                date:
                  description: "Cursor: updated_at of the last returned row (RFC 3339). Omit for the first page."
                  format: date-time
                  type: string
                id:
                  description: "Cursor: id of the last returned row; tie-breaker for equal updated_at values."
                  type: integer
                limit:
                  description: Rows per page. Defaults to 30, capped at 100.
                  type: integer
        description: Pagination json
        required: true
      responses:
        '200':
          description: array of opt-out rows (live rows and tombstones) in cursor order
          content:
            application/json:
              schema:
                items:
                  "$ref": "#/components/schemas/doc.UnsubscribedNumberFeedRow"
                type: array
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Paginate Unsubscribed Numbers (sync feed)
      tags:
      - Contacts
      operationId: v1ListUnsubscribedNumbers
  "/v1/unsubscribed_numbers/count":
    post:
      description: |
        Count of opt-out rows (live rows and tombstones) for the team, matching exactly the rows
        `/v1/unsubscribed_numbers` returns for the same cursor. Send an empty body `{}` for the
        total, or the same `date` / `id` you would send to the feed to count only what remains
        after that cursor — useful for sizing the work still to sync.

        Note this differs from the other `/count` endpoints: an absent `date` means the beginning
        of the feed, not now, because this feed pages forward.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `date` | **string (optional)** <br> Cursor timestamp. Counts rows changed strictly after it. Omit to count the whole feed. |
        | `id` | **integer (optional)** <br> Row id tie-breaker, paired with `date`. Use the `id` of the last row you received. |
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                date:
                  type: string
                  format: date-time
                id:
                  type: integer
        description: Empty json for the total, or the feed cursor to count what remains after it
        required: true
      responses:
        '200':
          description: count of opt-out rows (live rows and tombstones) matching the cursor
          content:
            application/json:
              schema:
                type: object
                properties:
                  count:
                    type: integer
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Count Unsubscribed Numbers (sync feed)
      tags:
      - Contacts
      operationId: v1ListUnsubscribedNumbersCount
  "/v1/bulk/unsubscribe_status":
    post:
      description: |
        Check the opt-out status of one or more phone numbers.

        `is_unsubscribed` is true when the number has any opt-out row (global or inbox-scoped),
        matching the contact rollup. For teams on per-inbox opt-out mode, each result additionally
        includes `inbox_opt_outs` (the specific inboxes the number is opted out of) and `global_opt_out`
        (whether a team-wide opt-out row exists). These breakdown fields are omitted for global-only
        opt-outs and for teams not on per-inbox mode; the `is_unsubscribed` / `set_from_contact`
        booleans keep their existing semantics.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `numbers` | **array of strings (optional)** <br> Phone numbers to check. |
        | `target` | **string (optional)** <br> A group target whose member numbers are also checked. |
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.BulkUnsubscribeStatusRequest"
        required: true
      responses:
        '200':
          description: Opt-out status per number
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.BulkUnsubscribeStatusResponse"
        '400':
          description: Bad data provided
          content:
            text/plain:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Bulk check opt-out status
      tags:
      - Contacts
      operationId: v1BulkUnsubscribeStatus
  "/v1/bulk/unsubscribe":
    post:
      description: |
        Opt many phone numbers out of messaging in a single request (bulk equivalent of
        `POST /v1/unsubscribe`).

        By default this creates a global (team-wide) opt-out per number that blocks each number on
        every inbox.

        For teams on per-inbox opt-out mode, you may pass an optional `inbox_ids` array to opt each
        number out of only those inboxes (a scoped opt-out). Omitting `inbox_ids` keeps the global
        behavior. Scoped opt-outs require the inbox-level opt-out entitlement; if the team is not
        entitled a `403` is returned, and if the team is not on per-inbox mode a `400` is returned.

        Numbers are deduplicated and validated individually: invalid numbers are returned in
        `failed_phone_numbers` and do not stop the rest of the batch. A maximum of 5000 phone
        numbers may be sent per request.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `phone_numbers` | **array of strings (required)** <br> Phone numbers to opt out. [E.164](https://en.wikipedia.org/wiki/E.164) number format except the plus sign (e.g. 14155553434). Max 5000. |
        | `inbox_ids` | **array of integers (optional)** <br> Inboxes to scope the opt-out to. Per-inbox opt-out mode only. Omit for a global (team-wide) opt-out; an empty array is rejected. Every id must belong to the team. Max 500. |
        | `user_id` | **integer (optional)** <br> Acting user, recorded on the created rows. On a team using per-inbox opt-out, supplying it without `inbox_ids` scopes every number to that user's inboxes. On a team using global opt-out (or one without the entitlement) the batch stays team-wide and the response reports `"applied_scope": "team_wide"`. Must be an active member of the authenticated team. |
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.BulkUnsubscribeRequest"
        required: true
      responses:
        '200':
          description: Bulk opt-out result
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.BulkUnsubscribeResponse"
        '400':
          description: >-
            phone_numbers missing/empty, over the 5000 limit, inbox_ids supplied as an empty array
            or over 500 entries, one or more inbox ids not owned by the team (`invalid_inbox_ids`
            lists them), or the team is not on per-inbox opt-out mode. The body carries a stable
            snake_case `code`: `phone_numbers_required`, `too_many_phone_numbers`, `bad_data`,
            `inbox_ids_empty`, `too_many_inbox_ids`, `invalid_inbox_ids`, `no_valid_inboxes`,
            `global_opt_out_mode`, `invalid_user_id`, `user_inbox_access_denied` (with `invalid_inbox_ids` listing the inboxes the user cannot access).
          content:
            application/json:
              schema:
                type: object
        '403':
          description: Inbox-level opt-out is not available for this team (code `not_entitled`).
          content:
            application/json:
              schema:
                type: object
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Bulk opt out numbers
      tags:
      - Contacts
      operationId: v1BulkUnsubscribe
  "/v1/batch/contacts":
    post:
      description: |
        Create contacts in batch, optionally overwriting any existing records. Any records that were unable to be created (or updated if overwrite = true) will be returned in the response body.

        If all records were successfully created and / or overwritten, responds with "ok".

        #### Request body attributes

        Expecting a JSON array of the following contact definitions:

        | Attributes | Description |
        | --- | --- |
        | `first` | **string (optional)** <br> First name. |
        | `last` | **string (optional)** <br> Last name. |
        | `display_name` | **string (optional)** <br> Display name. |
        | `phone` | **string (optional)** <br> Phone number.<br>[E.164](https://en.wikipedia.org/wiki/E.164) number format except the plus sign (e.g. 14155553434). |
        | `email` | **string (optional)** <br> Email address. |
        | `is_opted_out` | **boolean** <br> Is this contact opted out of messaging? |
      parameters:
      - description: Overwrite existing matching contacts
        in: query
        name: overwrite
        required: false
        schema:
          type: boolean
      requestBody:
        content:
          application/json:
            schema:
              items:
                "$ref": "#/components/schemas/doc.V2ContactReq"
              type: array
        description: Array of json contacts to create
        required: true
      responses:
        '200':
          description: Contacts not created or overwritten
          content:
            application/json:
              schema:
                items:
                  "$ref": "#/components/schemas/doc.Contact"
                type: array
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Batch contact create
      tags:
      - Contacts
      operationId: v1BatchContactCreate
  "/v1/conversations":
    post:
      description: |
        Get all the conversations for a team. Requires paginating to fetch all.

        #### Request body attributes

        | Attributes | Description |
        | ---------- | ----------- |
        | `id` | **integer (required)** <br>A User ID in the inbox, used to verify the user belongs to any of the inboxes provided in the `filter` attribute.|
        | `filters` | **object (required)** <br>`inboxes` (integer[], required) The Inbox IDs to include conversations for. <br>`closed` (boolean, optional) Filter by closed conversations <br>`unread` (boolean, optional) Filter by unread conversations for the given user.|
        | `date` | **string (optional)** <br> Last timestamp, usually `updated` attribute of last returned item from request.<br>[RFC 3339](http://www.faqs.org/rfcs/rfc3339.html) format.|


        #### Example request body to paginate `conversations`
        ```json

            {
              {
                "date": "2022-01-01T00:00:00+00:00",
                "id": 1111,
                "filters": {
                  "inboxes": [2222,3333],
                  "closed": false,
                  "unread": false
                }
              }
            }

        ```
      requestBody:
        "$ref": "#/components/requestBodies/pagination.V2PaginationParams"
      responses:
        '200':
          description: array of Conversations in team
          content:
            application/json:
              schema:
                items:
                  "$ref": "#/components/schemas/doc.Conversation"
                type: array
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Paginate Conversations
      tags:
      - Conversations
      operationId: v1ListConversations
  "/v1/conversations/count":
    post:
      description: |
        Get the count of all conversations from a team that would be returned with the query parameters provided.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `date` | **string (optional)** <br> Last timestamp, usually `updated` attribute of last returned item from request.<br>[RFC 3339](http://www.faqs.org/rfcs/rfc3339.html) format.|
      requestBody:
        "$ref": "#/components/requestBodies/pagination.V2PaginationParams"
      responses:
        '200':
          description: count of Conversations in team
          content:
            application/json:
              schema:
                type: string
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Conversation count
      tags:
      - Conversations
      operationId: v1ConversationCount
  "/v1/conversations/read":
    post:
      description: |
        Mark the conversation as read
        #### Request body attributes

        | Attributes | Description |
        | ---------- | ----------- |
        | `inbox_id` | **integer** <br>Unique identifier of the inbox. |
        | `target`   | **string (required if `targets` does not exist)** <br>Phone number corresponding to the conversation to be marked as read.  |
        | `targets`  | **array of strings (required if `target` does not exist)** <br>Array of phone numbers corresponding to the group MMS conversation to be marked as read. <br>|
        | `user_id`  | **integer** <br>Unique identifier of the user. |
      requestBody:
        "$ref": "#/components/requestBodies/doc.MarkMessageRequest"
      responses:
        '200':
          description: Marked as Read
          content:
            application/json:
              schema:
                type: string
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: mark conversation as read
      tags:
      - Conversations
      operationId: v1MarkConversationAsRead
  "/v1/conversations/reassign":
    post:
      description: |
        Reassign conversation to another user

        #### Request body attributes

        | Attributes   | Description |
        | ----------   | ----------- |
        | `inbox_id`   | **integer** <br>Unique identifier of the inbox. |
        | `target`     | **string (required if `targets` does not exist)** <br>Phone number corresponding to the conversation to be reassigned. |
        | `targets`    | **array of strings (required if `target` does not exist)** <br>Array of phone numbers corresponding to the group MMS conversation to be reassigned. <br>|
        | `user_id`    | **integer** <br>Unique identifier of the user. |
        | `reassign_id`| **integer** <br>Unique identifier of the user to assign to. |
      requestBody:
        "$ref": "#/components/requestBodies/doc.MarkMessageRequest"
      responses:
        '200':
          description: ok
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: ok
        '400':
          description: bad_data
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: bad_data
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: reassign conversation
      tags:
      - Conversations
      operationId: v1ReassignConversation
  "/v1/conversations/unread":
    post:
      description: |
        Mark the conversation as unread
        #### Request body attributes

        | Attributes | Description |
        | ---------- | ----------- |
        | `inbox_id` | **integer** <br>Unique identifier of the inbox. |
        | `target`   | **string (required if `targets` does not exist)** <br>Phone number corresponding to the conversation to be marked as unread.  |
        | `targets`  | **array of strings (required if `target` does not exist)** <br>Array of phone numbers corresponding to the group MMS conversation to be marked as unread. <br>|
        | `user_id`  | **integer** <br>Unique identifier of the user. |
      requestBody:
        "$ref": "#/components/requestBodies/doc.MarkMessageRequest"
      responses:
        '200':
          description: Marked as Unread
          content:
            application/json:
              schema:
                type: string
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: mark conversation as unread
      tags:
      - Conversations
      operationId: v1MarkConversationAsUnread
  "/v1/conversations/{id}":
    get:
      description: |
        Get Conversation by ID
        #### Request body attributes

        | Attributes | Description |
        | ---------- | ----------- |
        | `id` | **integer** <br>Unique identifier of the conversation. |
      parameters:
      - description: Conversations ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.Conversation"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Get Conversation by ID
      tags:
      - Conversations
      operationId: v1GetConversationByID
  "/v1/conversations/open":
    post:
      description: |
        Mark the conversation as open.
        #### Request body attributes

        | Attributes | Description |
        | ---------- | ----------- |
        | `chat_id`  | **integer (optional if `target` exists)** <br>Unique identifier of the conversation. |
        | `inbox_id` | **integer (required)** <br>Unique identifier of the inbox. |
        | `target`   | **string (optional if `chat_id` exists and required if `targets` does not exist)** <br>Generally the phone number corresponding to the conversation to be marked as open.<br>[E.164](https://en.wikipedia.org/wiki/E.164) number format except the plus sign (e.g. 14155553434).  |
        | `targets`  | **array of strings (required if `target` does not exist)** <br>Array of phone numbers corresponding to the group MMS conversation to be marked as open.<br>[E.164](https://en.wikipedia.org/wiki/E.164) number format except the plus sign (e.g. ["14155554343", "12158375180"]).  |
        | `user_id`  | **integer (required)** <br>Unique identifier of the user to be marked as the conversation opener. |

        #### Example request body to open `conversations`
        ```json

            {
              "chat_id": 1,
              "inbox_id": 2,
              "target": "14151234567",
              "user_id": 3
            }

        ```
      requestBody:
        "$ref": "#/components/requestBodies/doc.OpenCloseConversationRequest"
      responses:
        '200':
          description: Marked as open
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: ok
        '400':
          description: bad_data
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: bad_data
        '500':
          description: server_error
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: error
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Mark conversation as open
      tags:
      - Conversations
      operationId: v1MarkConversationAsOpen
  "/v1/conversations/close":
    post:
      description: |
        Mark the conversation as closed/archived.
        #### Request body attributes

        | Attributes | Description |
        | ---------- | ----------- |
        | `chat_id`  | **integer (optional if `target` exists)** <br>Unique identifier of the conversation. |
        | `inbox_id` | **integer (required)** <br>Unique identifier of the inbox. |
        | `target`   | **string (optional if `chat_id` exists and required if `targets` does not exist)** <br>Generally the phone number corresponding to the conversation to be marked as closed. <br>[E.164](https://en.wikipedia.org/wiki/E.164) number format except the plus sign (e.g. 14155553434). |
        | `targets`  | **array of strings (required if `target` does not exist)** <br>Array of phone numbers corresponding to the group MMS conversation to be marked as closed. <br>[E.164](https://en.wikipedia.org/wiki/E.164) number format except the plus sign (e.g. ["14155554343", "12158375180"]). |
        | `user_id`  | **integer (required)** <br>Unique identifier of the user to be marked as the conversation closer. |

        #### Example request body to close `conversations`
        ```json

            {
              "chat_id": 1,
              "inbox_id": 2,
              "target": "14151234567",
              "user_id": 3
            }

        ```
      requestBody:
        "$ref": "#/components/requestBodies/doc.OpenCloseConversationRequest"
      responses:
        '200':
          description: Marked as closed
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: ok
        '400':
          description: bad_data
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: bad_data
        '500':
          description: server_error
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: error
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Mark conversation as closed
      tags:
      - Conversations
      operationId: v1MarkConversationAsClosed
  "/v1/conversations/transfer":
    post:
      description: |
        Transfer Conversation to another inbox. Conversations currently cannot be transferred to an inbox where the conversation already exists. Only SMS chats are currently supported to be transferred.
        #### Request body attributes

        | Attributes | Description |
        | ---------- | ----------- |
        | `conversation_id` | **integer (required)** <br>Unique identifier of the conversation. |
        | `transfer_inbox_id` | **integer (required)** <br>Unique identifier of the inbox to transfer to. |

        #### Example request body to inbox transfer `conversations`
        ```json

            {
              "conversation_id": 1,
              "transfer_inbox_id": 2
            }

        ```
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.TransferConversationRequest"
        description: Transfer Conversations json
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: string
              examples:
                response:
                  value: ok
        '400':
          description: bad_data
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: error
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Transfer Conversation to another inbox
      tags:
      - Conversations
      operationId: v1TransferConversationToAnotherInbox
  "/v1/list":
    post:
      deprecated: true
      description: "**Deprecated**: This endpoint will be removed in a future release.
        \ \nUse `/v2/lists` instead.\n\nCreate a list in your team.\n\n#### Request
        body attributes\n\n| Attributes | Description |\n| --- | --- |\n| `title`
        | **string (required)** <br> List name. |\n| `members` | **object (required)**
        <br> Object of List targets. |\n| `archived` | **string (optional)** <br>
        If list is archived. |\n| `add_phone` | **string (optional)** <br> Ignored
        during creation, `members` should be provided. |\n| `remove_phone` | **string
        (optional)** <br> Ignored during creation, `members` should be provided.|\n|
        `local_id` | **string (optional)** <br> Client unique identifier for a list.
        \ |\n\n\n#### Example of Request body for `List` creation\nIn this example,
        we create a list with three members, with different fields for the List Targets.\n```json\n\n
        \   {\n      \"title\": \"My API List\",\n      \"local_id\": \"dc3deb9b-940a-444d-9d0e-8e3087a160e9\",\n
        \     \"members\": {\n        \"15105553344\": {\n          \"l\": \"api first\",\n
        \         \"f\": \"api last\"\n        },\n        \"14155555475\": {\n          \"l\":
        \"\",\n          \"f\": \"only first\"\n        },\n        \"15105551341\":
        {},\n      }\n    }\n\n```\n"
      requestBody:
        "$ref": "#/components/requestBodies/doc.V2ListReq"
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.V2CreateUpdateResp"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Create List
      tags:
      - Lists
      operationId: v1CreateList
  "/v1/list/{id}":
    delete:
      deprecated: true
      description: "**Deprecated**: This endpoint will be removed in a future release.
        \ \nUse `/v2/lists/{id}` instead.\n\nDelete a list based on its ID.\n"
      parameters:
      - description: List ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      responses:
        '200':
          description: ok
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: ok
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Delete List
      tags:
      - Lists
      operationId: v1DeleteList
    get:
      deprecated: true
      description: "**Deprecated**: This endpoint will be removed in a future release.
        \ \nThere is no single-list v2 read endpoint; use `/v2/lists/paginate`
        with `{\"filters\": {\"id\": [<id>]}}` instead.\n\nGet a list based on its ID.\n"
      parameters:
      - description: List ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.List"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Get List
      tags:
      - Lists
      operationId: v1GetList
    put:
      deprecated: true
      description: "**Deprecated**: This endpoint will be removed in a future release.
        \ \nUse `/v2/lists/{id}` instead.\n\nUpdate a list in your team based on its ID.\n\n####
        Request body attributes\n\n| Attributes | Description |\n| --- | --- |\n|
        `title` | **string (required)** <br> List name. |\n| `members` | **object
        (optional)** <br> Object of List targets. <br> During list update `members`
        will be ignored if `add_phone` or `remove_phone` is provided. |\n| `archived`
        | **string (optional)** <br> Boolean value for if list is archived. |\n| `add_phone`
        | **string (optional)** <br> Add a single phone to the list. |\n| `remove_phone`
        | **string (optional)** <br> Remove single phone from the list.|\n| `local_id`
        | **string (optional)** <br> Client unique identifier for a list.  |\n\n\n####
        Example of Request body for `List` update\nWe use `add_phone` and `remove_phone`
        attributes in this example to remove and add a single number to a list.\n```json\n\n
        \   {\n      \"title\": \"My API List\",\n      \"local_id\": \"dc3deb9b-940a-444d-9d0e-8e3087a160e9\",\n
        \     \"add_phone\": \"14155559999\",\n      \"remove_phone\": \"15105551341\",\n
        \   }\n\n```\n"
      parameters:
      - description: List ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      requestBody:
        "$ref": "#/components/requestBodies/doc.V2ListReq"
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.V2CreateUpdateResp"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Update List
      tags:
      - Lists
      operationId: v1UpdateList
  "/v1/lists":
    post:
      deprecated: true
      description: "**Deprecated**: This endpoint will be removed in a future release.
        \ \nUse `/v2/lists/paginate` instead.\n\nGet all lists in the team. Requires paginating
        to fetch all.\nSending a request with empty JSON body will return the most
        recent 30 Lists.\n\n#### Request body attributes\n\n| Attributes | Description
        |\n| --- | --- |\n| `date` | **string (optional)** <br> Last timestamp, usually
        `updated` attribute of last returned item from request. <br>[RFC 3339](http://www.faqs.org/rfcs/rfc3339.html)
        format.|\n"
      requestBody:
        "$ref": "#/components/requestBodies/pagination.V2PaginationParams"
      responses:
        '200':
          description: array of List objects in a team
          content:
            application/json:
              schema:
                items:
                  "$ref": "#/components/schemas/doc.List"
                type: array
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Paginate Lists
      tags:
      - Lists
      operationId: v1ListLists
  "/v1/lists/count":
    post:
      deprecated: true
      description: "**Deprecated**: This endpoint will be removed in a future release.
        \ \nUse `/v2/lists/count` instead.\n\nGet the count of all lists from a team that
        would be returned with the query parameters provided.\n\n#### Request body
        attributes\n\n| Attributes | Description |\n| --- | --- |\n| `date` | **string
        (optional)** <br> Last timestamp, usually `updated` attribute of last returned
        item from request.<br>[RFC 3339](http://www.faqs.org/rfcs/rfc3339.html) format.|\n"
      requestBody:
        "$ref": "#/components/requestBodies/pagination.V2PaginationParams"
      responses:
        '200':
          description: count of List objects in team
          content:
            application/json:
              schema:
                type: string
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: List count
      tags:
      - Lists
      operationId: v1ListCount
  "/v1/search/lists":
    post:
      deprecated: true
      description: |
        **Deprecated**: This endpoint will be removed in a future release.
        Use `/v2/lists/search` instead.

        Search the lists in your team by name.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `q` | **string (required)** <br> Keyword to match against list names. |
        | `user_id` | **integer (optional)** <br> Search as this team member; results are limited to lists visible to their user groups. Must belong to the team. Defaults to the team owner. |
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.ListSearchReq"
        description: Search parameters
        required: true
      responses:
        '200':
          description: Matching lists
          content:
            application/json:
              schema:
                type: object
                properties:
                  request:
                    "$ref": "#/components/schemas/doc.ListSearchReq"
                  results:
                    type: array
                    items:
                      "$ref": "#/components/schemas/doc.List"
        '400':
          description: bad_data
          content:
            text/plain:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Search Lists
      tags:
      - Lists
      operationId: v1SearchLists
  "/v1/messages":
    get:
      description: Get 50 recent messages from a conersation with a target phone number.
      parameters:
      - description: Phone Number
        in: query
        name: phoneNumber
        required: true
        schema:
          type: string
      - description: Inbox ID
        in: query
        name: inboxID
        required: true
        schema:
          type: integer
      - description: Latest time to search
        in: query
        name: timestamp
        required: false
        schema:
          type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  "$ref": "#/components/schemas/doc.Message"
                type: array
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Get message history from conversation
      tags:
      - Messages
      operationId: v1GetMessageHistoryFromConversation
  "/v1/messages/all":
    post:
      description: |
        Get all the messages for a team. Requires paginating to fetch all.
        Sending a request with empty JSON body will return the 30 most recent messages.
        To paginate the message send the last timestamp of the last message received in the `created_at` attribute.

        #### Request body attributes

        |  Attributes  | Description |
        | ------------ | ----------- |
        | `created_at` | **string  (required)** <br> Last timestamp fetch messages created on and after this date.<br>[RFC 3339](http://www.faqs.org/rfcs/rfc3339.html) format.|
        | `order`      | **string (optional)** <br> The field name on which sorting should be performed. Valid values are created_at or updated_at fields.|
        | `ascending`  | **boolean (optional)** <br> True if sort in ascending, default sort is descending order.|
        | `limit`      | **integer (optional)** <br>Messages to return per page, default is 30.|
      requestBody:
        "$ref": "#/components/requestBodies/pagination.V2MessagePaginationParams"
      responses:
        '200':
          description: array of messages in team
          content:
            application/json:
              schema:
                items:
                  "$ref": "#/components/schemas/doc.Message"
                type: array
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Paginate Message
      tags:
      - Messages
      operationId: v1ListMessage
  "/v1/messages/all/count":
    post:
      description: |
        Get the count of all messages for a team that would be returned with the query parameters provided.

        #### Request body attributes

        |  Attributes  | Description |
        | ------------ | ----------- |
        | `created_at` | **string (optional)** <br> Last timestamp, fetch count of messages created on and after this date.<br>[RFC 3339](http://www.faqs.org/rfcs/rfc3339.html)  format.|
      requestBody:
        "$ref": "#/components/requestBodies/pagination.V2MessagePaginationParams"
      responses:
        '200':
          description: count of messages in a team
          content:
            application/json:
              schema:
                type: string
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Paginate Message Count
      tags:
      - Messages
      operationId: v1ListMessageCount
  "/v1/message/send":
    post:
      description: |
        Send a message to an individual or a list.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `inbox_id` | **integer (required)** <br> Unique identifier for the inbox from which the message will be sent. |
        | `creator_id` | **integer (required)** <br> Unique identifier for the sender, same as the member ID returned by Get Inboxes. |
        | `creator_email` | **string (optional)** <br> Email address of a team member to use as the sender when valid. |
        | `phone_number` | **string (optional)** <br> [E.164](https://en.wikipedia.org/wiki/E.164) number format except the plus sign (e.g. 14155553434). <br> If the message is sent to an individual phone number then only `phone_number` or `chat_id` should be provided.  |
        | `targets` | **array of strings (optional)** <br> Array of phone numbers in [E.164](https://en.wikipedia.org/wiki/E.164) format except the plus sign (e.g. ["14155554343", "12158375180"]) to send group MMS message to. <br>Note that toll free numbers are not supported in sending group MMS messages.<br>|
        | `chat_id` | **integer (optional)** <br> Unique identifier for the Heymarket chat.  |
        | `list_id` | **integer (optional)** <br> Unique identifier for the Heymarket List. <br> If the message is sent as a broadcast then `list_id` must be provided. |
        | `text` | **string (optional)** <br> Message text body. |
        | `media_url` | **string (optional)** <br> Message media URL. |
        | `link_url` | **string (optional)** <br> Link URL to attach to the message. |
        | `custom` | **object (optional)** <br> Custom message content. |
        | `template_id` | **integer (optional)** <br> Unique identifier for the Heymarket template. |
        | `local_id` | **string (optional)** <br> Client unique identifier for the message. <br> If the message is sent as a broadcast then `local_id` must be provided. |
        | `activity_id` | **string (optional)** <br> Broadcast activity ID. |
        | `private` | **bool (optional)** <br> To create a private comment (memo) within a Heymarket conversation. |
        | `notify_all` | **bool (optional)** <br> Mention all inbox members when creating a private comment. |
        | `user_ids` | **array of integers (optional)** <br> User IDs to mention when creating a private comment. |
        | `author` | **string (optional)** <br> Display who the message was sent from. <br>This appears above the chat bubble within a Heymarket conversation. |
        | `gallery` | **array (optional)** <br> Image gallery entries to send with an individual message. |
        | `prefer_contact_owner` | **bool (optional)** <br> Send as the contact owner when one exists. |
        | `opt_in` | **bool (optional)** <br> Mark the message as an opt-in message. |
        | `survey_id` | **integer (optional)** <br> Send a Survey by passing in this ID. <br> The text of the message will be the opening question of the Survey.  |
        | `conv_name` | **string (optional)** <br> Conversation name when creating a new conversation. |
        | `signature_mode` | **string (optional)** <br> `user` (default) or `ai_agent`. <br> `ai_agent` appends the AI agent's signature instead of the sender's. Individual sends only: rejected together with `list_id`, `private`, or `author`. |
        | `ai_team_agent_id` | **integer (optional)** <br> The AI agent whose signature to append. <br> Required when `signature_mode` is `ai_agent`. |

        Examples are provided for individual, private, and broadcast message sends.
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/server.MessageSendReq"
            examples:
              individual_message:
                summary: Send an individual message
                value:
                  creator_id: 1
                  inbox_id: 1
                  phone_number: '16505551003'
                  text: My first api message
                  local_id: 6d8d0429-3f15-4293-8d0b-38bf1dd5ca06
              private_message:
                summary: Create a private message
                value:
                  creator_id: 1
                  inbox_id: 1
                  phone_number: '16505551003'
                  text: My first private api message
                  private: true
                  local_id: 6d8d0429-3f15-4293-8d0b-38bf1dd5ca06
              broadcast_message:
                summary: Send a broadcast message
                value:
                  creator_id: 1
                  inbox_id: 1
                  list_id: 5
                  text: My first api broadcast
                  local_id: 5d8d0429-3f15-4293-8d0b-38bf1dd5ca33
        description: Message send json
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/server.MessageSendResponse"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '403':
          description: |
            The send was refused: blocked by a team intercept rule or bad-word check
            (JSON body with an `error` code), or the sender lacks permission for the
            requested `signature_mode` (plain-text message).
          content:
            application/json:
              schema:
                type: string
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Send Message
      tags:
      - Messages
      operationId: v1SendMessage
  "/v1/message/hide_toggle/{id}":
    post:
      description: 'Toggle hidden flag for a message.'
      parameters:
      - description: Message ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.Message"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Toggle Hide Message
      tags:
      - Messages
      operationId: v1ToggleHideMessage
  "/v1/email/send":
    post:
      description: |
        Send a 1:1 email message to an individual contact or existing conversation.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `creator_id` | **integer (required)** <br> Unique identifier for the sender, same as the member ID returned by Get Inboxes. |
        | `inbox` | **integer (required)** <br> Unique identifier for the inbox from which the email will be sent. |
        | `text` | **string (required)** <br> Email message text body (HTML supported). |
        | `raw_text` | **string (optional)** <br> Plain text version of the email message. |
        | `local_id` | **string (optional)** <br> Client unique identifier for the message. |
        | `conv_title` | **string (optional)** <br> Title for new conversation if creating one. |
        | `targets` | **array (optional)** <br> Array of email addresses with `to:` or `cc:` prefixes for new conversations. Each array element can contain multiple comma-separated emails (e.g. ["to:sample+101@example.com,sample+102@example.com", "cc:sample+103@example.com,sample+104@example.com"]). |
        | `convo_id` | **integer (optional)** <br> Unique identifier for an existing conversation to send the email to the same thread. This can be retrieved from the response of sending the first email. |
        | `signature_mode` | **string (optional)** <br> `user` (default) or `ai_agent`. <br> `ai_agent` appends the AI agent's email signature (falling back to its SMS signature when no email signature is configured). Individual sends only: rejected together with `list` or `author`. |
        | `ai_team_agent_id` | **integer (optional)** <br> The AI agent whose signature to append. <br> Required when `signature_mode` is `ai_agent`. |

        #### Example for sending email to existing conversation
        ```json

            {
              "text": "<div>Hello from API</div>",
              "inbox": 123,
              "creator_id": 1234,
              "convo_id": 45362
            }

        ```

        #### Example for sending email to new conversation
        ```json

            {
              "text": "<div>Hello from API</div>",
              "inbox": 123,
              "creator_id": 1234,
              "conv_title": "New Message",
              "local_id": "f504779b-8547-a3123872f9aa",
              "targets": [
                "to:sample+101@example.com,sample+102@example.com",
                "cc:sample+103@example.com,sample+104@example.com"
              ],
              "convo_id": 45362
            }

        ```
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
              - creator_id
              properties:
                creator_id:
                  type: integer
                  description: Unique identifier for the sender
                inbox:
                  type: integer
                  description: Unique identifier for the inbox
                text:
                  type: string
                  description: Email message text (HTML supported)
                raw_text:
                  type: string
                  description: Plain text version of the email message
                local_id:
                  type: string
                  description: Client unique identifier for the message
                conv_title:
                  type: string
                  description: Title for new conversation if creating one
                targets:
                  type: array
                  description: Array of email addresses with 'to:' or 'cc:' prefixes
                    for new conversations. Each array element can contain multiple
                    comma-separated emails
                  items:
                    type: string
                convo_id:
                  type: integer
                  description: Unique identifier required for an existing conversation
                    to send the email to the same thread
                signature_mode:
                  type: string
                  enum:
                  - user
                  - ai_agent
                  description: Signature to append. 'ai_agent' appends the AI agent's
                    email signature; individual sends only
                ai_team_agent_id:
                  type: integer
                  description: The AI agent whose signature to append; required when
                    signature_mode is 'ai_agent'
        description: Email message json
        required: true
      responses:
        '200':
          description: Email sent successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                    description: Message ID
                  convo_id:
                    type: integer
                    description: Conversation ID
                  queued:
                    type: boolean
                    description: Whether the message was queued
                  date:
                    type: string
                    format: date-time
                    description: Send date in ISO format
                  compliance_results:
                    type: object
                    description: Compliance check results
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '403':
          description: denied
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Send Email Message
      tags:
      - Messages
      operationId: v1SendEmailMessage
  "/v1/schedule":
    post:
      description: |2

        Create a scheduled message to a contact or list.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `inbox_id` | **integer (required)** <br> Unique identifier for a inbox from which message will be sent. |
        | `execute_at` | **object (required)** <br> Time at which message will be sent.<br>Should be in 15 min intervals for minutes and at least 15 min from request initiation date. <br>[RFC 3339](http://www.faqs.org/rfcs/rfc3339.html) format.|
        | `phone_number` | **string (optional)** <br> [E.164](https://en.wikipedia.org/wiki/E.164) number format except the plus sign (e.g. 14155553434). <br> If message scheduled to individual phone number then only `phone_number` or `conversation_id` should be provided.  |
        | `targets` | **array of strings (optional)** <br> Array of phone numbers in [E.164](https://en.wikipedia.org/wiki/E.164) format except the plus sign (e.g. ["14155554343", "12158375180"]) to send group MMS message to. <br>Note that toll free numbers are not supported in sending group MMS messages.<br>|
        | `conversation_id` | **integer (optional)** <br> Unique identifier of Heymarket chat.  |
        | `super_id` | **integer (optional)** <br> Conversation super ID for scheduled conversation sends. |
        | `list_id` | **integer (optional)** <br> Unique identifier of Heymarket List. <br> If message scheduled as a broadcast than `list_id` should be provided. |
        | `content` | **object (required)** <br>  `ScheduledContent` Object. |
        | `local_id` | **string (optional)** <br> Client unique identifier for a scheduled message.  |
        | `user_id` | **integer (optional)** <br> User ID for the sender of the message. User must be a member of the specified Inbox. <br> Defaults the sender to the Team owner.|
        | `is_email` | **bool (optional)** <br> Schedule an email message. |

        #### Example of Request body for `Schedule` creation
        In this example we create a scheduled message to `14155553434` to be sent on July 24, 2019 at 11:30 am PST.
        ```json

            {
              "local_id": "f4bb74f2-7827-4a61-83c1-04164338fb6f",
              "content": {
                "text": "api scheduled message",
                "to": "14155553434"
              },
              "phone_number": "14155553434",
              "inbox_id": 413,
              "execute_at": "2019-07-24T11:30:00-07:00",
            }

        ```
      requestBody:
        "$ref": "#/components/requestBodies/doc.V2ScheduleReq"
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.V2CreateUpdateResp"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Create Schedule
      tags:
      - Schedule
      operationId: v1CreateSchedule
  "/v1/schedule/{id}":
    delete:
      description: Delete a schedule based on its ID.
      parameters:
      - description: Schedule ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      responses:
        '200':
          description: ok
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: ok
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Delete Schedule
      tags:
      - Schedule
      operationId: v1DeleteSchedule
    get:
      description: Get an existing schedule by its ID.
      parameters:
      - description: Schedule ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.Scheduled"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Get Schedule
      tags:
      - Schedule
      operationId: v1GetSchedule
    put:
      description: Update a schedule based on a schedule JSON.
      parameters:
      - description: Schedule ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      requestBody:
        "$ref": "#/components/requestBodies/doc.V2ScheduleReq"
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.V2CreateUpdateResp"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Schedule update
      tags:
      - Schedule
      operationId: v1ScheduleUpdate
  "/v1/template":
    post:
      description: |
        Create a template in the team.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `title` | **string (required)** <br> Template name. |
        | `content` | **object (required)** <br>  `GalleryContent` Object. |
        | `archived` | **string (optional)** <br> If the template is archived. |
        | `local_id` | **string (optional)** <br> Client unique identifier for a list.  |

        #### Example of Request body for `Template` creation
        In this example we create a template with the `first_name` and `last_name` merge tokens.
        ```json

            {
              "title": "My first template over the API",
              "local_id": "88ae517b-ff42-4ef2-958d-86b27db6b21c",
              "content": {
                "text": "Hey {{first_name}}  {{last_name}}, I created this template with the Heymarket API.\nYour name was populated from merge tokens."
              },
              "archived": false
            }

        ```
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.V2TemplateReq"
        description: Template json
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.V2CreateUpdateResp"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Create Template
      tags:
      - Templates
      operationId: v1CreateTemplate
  "/v1/template/{id}":
    delete:
      description: Delete a template by its ID.
      parameters:
      - description: Template ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      responses:
        '200':
          description: ok
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: ok
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Delete Template
      tags:
      - Templates
      operationId: v1DeleteTemplate
    get:
      description: Get a template by its ID.
      parameters:
      - description: Template ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.Template"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Get Template
      tags:
      - Templates
      operationId: v1GetTemplate
    put:
      description: |
        Update a template in your team based on its ID.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `title` | **string (required)** <br> Template name. |
        | `content` | **object (required)** <br>  `GalleryContent` Object. |
        | `archived` | **string (optional)** <br> If the template is archived. |
        | `local_id` | **string (optional)** <br> Client unique identifier for a template.  |


        #### Example of Request body for `Template` update
        In this example, we add an image to the template.
        ```json

            {
              "title": "My first template over api's",
              "local_id": "88ae517b-ff42-4ef2-958d-86b27db6b21c",
              "content": {
                "text": "Hey {{first_name}}  {{last_name}}, I created this template with Heymarket API's.\nYour name was populated from merge tokens.\n\n\nMight as well add an image",
                "gallery": [{
                  "url": "https://embrace-uploads-mms.s3.us-west-1.amazonaws.com/2ca6e36c-f04e-4ee8-8eb3-7026df4b2225/img.jpg",
                  "original_url": "https://embrace-uploads-mms.s3.us-west-1.amazonaws.com/2ca6e36c-f04e-4ee8-8eb3-7026df4b2225/img.jpg"
                }]
              },
              "archived": false
            }

        ```
      parameters:
      - description: Template ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.V2TemplateReq"
        description: Template json
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.V2CreateUpdateResp"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Update Template
      tags:
      - Templates
      operationId: v1UpdateTemplate
  "/v1/templates":
    post:
      description: |
        Get all the templates in the team. Requires paginating to fetch all.
        Sending a request with empty JSON body will return the most recent 30 Templates.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `date` | **string (optional)** <br> Last timestamp, usually `updated` attribute of last returned item from request.<br>[RFC 3339](http://www.faqs.org/rfcs/rfc3339.html) format.|
      requestBody:
        "$ref": "#/components/requestBodies/pagination.V2PaginationParams"
      responses:
        '200':
          description: array of Templates in a team
          content:
            application/json:
              schema:
                items:
                  "$ref": "#/components/schemas/doc.Template"
                type: array
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Paginate Templates
      tags:
      - Templates
      operationId: v1ListTemplates
  "/v1/survey/{id}":
    get:
      description: Get the Survey object for a given ID.
      parameters:
      - description: Survey ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      responses:
        '200':
          description: Survey Object
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.Survey"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Get Survey
      tags:
      - Surveys
      operationId: v1GetSurvey
  "/v1/surveys":
    post:
      description: |
        Get all the Surveys in the team, ordered by last created descending. Requires paginating to fetch all.
        Sending a request with empty JSON body will return the most recent 30 Surveys.
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/pagination.V2PageLimitParams"
        description: Pagination json
        required: true
      responses:
        '200':
          description: Array of Surveys in a team
          content:
            application/json:
              schema:
                items:
                  "$ref": "#/components/schemas/doc.Survey"
                type: array
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Paginate Surveys
      tags:
      - Surveys
      operationId: v1ListSurveys
  "/v1/tag":
    post:
      description: |
        Create a tag in the team.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `tag` | **string (required)** <br> Tag name. |

        #### Example of Request body for `Tag` creation
        ```json

            {
              "tag": "tag1"
            }

        ```
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.V2TagReq"
        description: Tag json
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.V2CreateUpdateResp"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Create Tag
      tags:
      - Tags
      operationId: v1CreateTag
  "/v1/tag/{id}":
    delete:
      description: Delete a tag by its ID.
      parameters:
      - description: Tag ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      responses:
        '200':
          description: ok
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: ok
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Delete Tag
      tags:
      - Tags
      operationId: v1DeleteTag
    get:
      description: Get a tag by its ID.
      parameters:
      - description: Tag ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.Tag"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Get Tag
      tags:
      - Tags
      operationId: v1GetTag
  "/v1/tags":
    post:
      description: |
        Get all the tags in the team. Requires paginating to fetch all.
        Sending a request with empty JSON body will return the most recent 30 Tags.
        #### Request body attributes
        | Attributes | Description |
        | --- | --- |
        | `date` | **string (optional)** <br> Last timestamp, usually `updated` attribute of last returned item from request.<br>[RFC 3339](http://www.faqs.org/rfcs/rfc3339.html)  format.|
      requestBody:
        "$ref": "#/components/requestBodies/pagination.V2PaginationParams"
      responses:
        '200':
          description: array of Tags in a team
          content:
            application/json:
              schema:
                items:
                  "$ref": "#/components/schemas/doc.Tag"
                type: array
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Paginate Tags
      tags:
      - Tags
      operationId: v1ListTags
  "/v2/lists":
    post:
      description: |
        Create a new list in your team. Supports both legacy phone-based lists and omnichannel contact-based lists.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `title` | **string (required)** <br> List name. |
        | `local_id` | **string (optional)** <br> Client-provided unique identifier for the list. |
        | `contacts` | **array of integers (optional)** <br> Array of contact IDs to replace/set the list membership. |
        | `phones` | **array of strings (optional)** <br> Phone numbers in [E.164](https://en.wikipedia.org/wiki/E.164) format (without the plus sign, e.g. 14155553434) to add as new contacts. |
        | `emails` | **array of strings (optional)** <br> Email addresses to add as new contacts. |

        #### Example of Request body for `List` creation
        ```json

            {
              "title": "My VIP Contacts",
              "type": "contacts",
              "local_id": "dc3deb9b-940a-444d-9d0e-8e3087a160e9",
              "contacts": [1001, 1002, 1003]
            }

        ```
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.OmnichannelListCreateReq"
        description: List json
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.V2CreateUpdateResp"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Create List (v2)
      tags:
      - Lists
      operationId: v2CreateList
  "/v2/lists/{id}":
    put:
      description: |
        Update an existing list in your team. Supports incremental add/remove of contacts.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `title` | **string (optional)** <br> List name. |
        | `add_contacts` | **array of integers (optional)** <br> Array of contact IDs to add to the list. |
        | `remove_contacts` | **array of integers (optional)** <br> Array of contact IDs to remove from the list. |
        | `add_phones` | **array of strings (optional)** <br> Phone numbers in [E.164](https://en.wikipedia.org/wiki/E.164) format (without the plus sign, e.g. 14155553434) to add as new contacts. |
        | `add_emails` | **array of strings (optional)** <br> Email addresses to add as new contacts. |

        #### Example of Request body for `List` update
        ```json

            {
              "title": "My Updated List",
              "add_contacts": [1004, 1005],
              "remove_contacts": [1001]
            }

        ```
      parameters:
      - description: List ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.OmnichannelListUpdateReq"
        description: List json
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.V2CreateUpdateResp"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Update List (v2)
      tags:
      - Lists
      operationId: v2UpdateList
    delete:
      description: Delete a list by its ID.
      parameters:
      - description: List ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.OmnichannelListDeleteReq"
        description: Delete request body
      responses:
        '200':
          description: deleted
          content:
            application/json:
              schema:
                type: object
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Delete List (v2)
      tags:
      - Lists
      operationId: v2DeleteList
  "/v2/lists/paginate":
    post:
      description: |
        Paginate the lists in your team. Replaces the deprecated `POST /v1/lists`: same request body, same response shape.
        Sending an empty JSON body returns the 30 most recently updated live lists.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `date` | **string (optional)** <br> Cursor. Returns lists updated before this timestamp (after it when `ascending` is `true`), usually the `updated` value of the last returned item. [RFC 3339](http://www.faqs.org/rfcs/rfc3339.html) format. Defaults to now. |
        | `limit` | **integer (optional)** <br> Results per page. Default 30, max 100. |
        | `page` | **integer (optional)** <br> Page offset (0-indexed) applied after the cursor. |
        | `order` | **string (optional)** <br> `name` or `usage`. Defaults to update time. |
        | `ascending` | **boolean (optional)** <br> Sort ascending if `true`. |
        | `archived` | **boolean (optional)** <br> Return archived lists instead of live ones. |
        | `user_id` | **integer (optional)** <br> Restrict results to lists visible to this user's user groups. |
        | `filters` | **object (optional)** <br> Only `id` is supported and it must be an array, e.g. `{"id": [21152, 21153]}`, at most 100 ids (`too_many_ids` above that). Any other key, or a non-array `id`, is rejected with `invalid_filter`. |

        #### Example request body
        ```json

            {
              "limit": 50,
              "filters": {"id": [21152, 21153]}
            }

        ```
      requestBody:
        "$ref": "#/components/requestBodies/pagination.V2PaginationParams"
      responses:
        '200':
          description: array of List objects in a team
          content:
            application/json:
              schema:
                items:
                  "$ref": "#/components/schemas/doc.List"
                type: array
        '400':
          description: invalid_body, invalid_filter, too_many_ids
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.V2ErrorResp"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Paginate Lists (v2)
      tags:
      - Lists
      operationId: v2PaginateLists
  "/v2/lists/count":
    post:
      description: |
        Count the lists that `POST /v2/lists/paginate` would return for the same request body. Replaces the deprecated `POST /v1/lists/count`.

        The count ignores `limit` and `page`, so it reports every matching list rather than one page of them.

        #### Request body attributes

        Identical to Paginate Lists (v2): `date`, `order`, `ascending`, `archived`, `user_id`, and `filters`. `limit` and `page` are accepted but do not affect the count.

        #### Example request body
        ```json

            {
              "filters": {"id": [21152, 21153]}
            }

        ```
      requestBody:
        "$ref": "#/components/requestBodies/pagination.V2PaginationParams"
      responses:
        '200':
          description: count of List objects matching the request
          content:
            application/json:
              schema:
                type: object
                properties:
                  count:
                    type: integer
                    format: int64
                    example: 42
        '400':
          description: invalid_body, invalid_filter, too_many_ids
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.V2ErrorResp"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Count Lists (v2)
      tags:
      - Lists
      operationId: v2CountLists
  "/v2/lists/search":
    post:
      description: |
        Search the lists in your team by name.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `q` | **string (required)** <br> Keyword to match against list names. An empty `q` is rejected with `invalid_query`. |
        | `user_id` | **integer (optional)** <br> Search as this team member; results are limited to lists visible to their user groups. A `user_id` outside your team is rejected with `403 not_team_member`. Defaults to the team owner. |
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.ListSearchReq"
        description: Search parameters
        required: true
      responses:
        '200':
          description: Matching lists
          content:
            application/json:
              schema:
                type: object
                properties:
                  request:
                    "$ref": "#/components/schemas/doc.ListSearchReq"
                  results:
                    type: array
                    items:
                      "$ref": "#/components/schemas/doc.List"
        '400':
          description: invalid_body, invalid_query
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.V2ErrorResp"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '403':
          description: not_team_member
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.V2ErrorResp"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Search Lists (v2)
      tags:
      - Lists
      operationId: v2SearchLists
  "/v2/contact":
    post:
      description: |
        Create a new contact. At least one contact channel must be provided.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `first` | **string (optional)** <br> First name. |
        | `last` | **string (optional)** <br> Last name. |
        | `display_name` | **string (optional)** <br> Display name. |
        | `contact_channels` | **array (required)** <br> One or more contact channels. Each entry requires `channel_type` and `channel_id`. |
        | `assignee_id` | **integer (optional)** <br> User ID to assign the contact to. |
        | `tags` | **array (optional)** <br> Tags to associate with the contact. |
        | `custom` | **object (optional)** <br> Custom field values. |
        | `is_opted_out` | **boolean (optional)** <br> Whether the contact is opted out. |
        | `email_suppressions` | **array (optional)** <br> Email suppression IDs to associate with the contact. |
        | `timezone` | **string (optional)** <br> IANA timezone for the contact. |
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.V2CreateContactReq"
        description: Contact json
        required: true
      responses:
        '200':
          description: Created contact
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.Contact"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Create Contact (V2)
      tags:
      - Contacts
      operationId: v2CreateContact
  "/v2/contact/{contactID}":
    get:
      description: Retrieve a contact by their UUID.
      parameters:
      - description: Contact UUID
        in: path
        name: contactID
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Contact
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.Contact"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Retrieve Contact (V2)
      tags:
      - Contacts
      operationId: v2GetContact
    put:
      description: Update an existing contact by their UUID.
      parameters:
      - description: Contact UUID
        in: path
        name: contactID
        required: true
        schema:
          type: string
      requestBody:
        "$ref": "#/components/requestBodies/doc.V2UpdateContactReq"
      responses:
        '200':
          description: Updated contact
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.Contact"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Update Contact (V2)
      tags:
      - Contacts
      operationId: v2UpdateContact
    delete:
      description: Delete a contact by their UUID.
      parameters:
      - description: Contact UUID
        in: path
        name: contactID
        required: true
        schema:
          type: string
      responses:
        '200':
          description: contact deleted
          content:
            application/json:
              schema:
                type: string
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Delete Contact (V2)
      tags:
      - Contacts
      operationId: v2DeleteContact
  "/v2/contacts":
    post:
      description: 'Bulk create or update contacts. Contacts with a `contact_id` will be updated; those without will be created.'
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.V2BulkContactReq"
        description: Bulk contact json
        required: true
      responses:
        '200':
          description: Bulk create response
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.V2BulkContactResp"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Bulk Create Contacts (V2)
      tags:
      - Contacts
      operationId: v2BulkCreateContacts
    delete:
      description: 'Bulk delete contacts by their IDs. Provide an `id` channel type in `contact_channels` with a list of contact UUIDs in `channel_ids`.'
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.V2BulkDeleteContactReq"
        description: Bulk delete request
        required: true
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                type: string
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Bulk Delete Contacts (V2)
      tags:
      - Contacts
      operationId: v2BulkDeleteContacts
  "/v2/contacts/list":
    post:
      description: |
        Fetch a paginated list of contacts.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `limit` | **integer (optional)** <br> Number of results per page. Default: 30, max: 100. |
        | `page` | **integer (optional)** <br> Page number (0-indexed). |
        | `date` | **string (optional)** <br> Return contacts updated since this timestamp. [RFC 3339](http://www.faqs.org/rfcs/rfc3339.html) format. |
        | `ascending` | **boolean (optional)** <br> Sort ascending if `true`. |
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.V2ContactListReq"
        description: List parameters
        required: true
      responses:
        '200':
          description: List of contacts
          content:
            application/json:
              schema:
                items:
                  "$ref": "#/components/schemas/doc.Contact"
                type: array
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: List Contacts (V2)
      tags:
      - Contacts
      operationId: v2ListContacts
  "/v2/contacts/search":
    post:
      description: |
        Search contacts by channel value (phone, email, etc.).

        #### Request body attributes

        | Attribute | Description |
        | --- | --- |
        | `contact_channels` | **array (required)** <br> One or more channel entries to search by. Each entry requires `channel_ids`. `channel_type` is optional — if omitted, the type is inferred from the ID format (E.164 → phone, email format → email, etc.). |
        | `contact_channels[].channel_type` | **string (optional)** <br> Channel type to search within (e.g. `phone`, `email`, `facebook`). |
        | `contact_channels[].channel_ids` | **array (required)** <br> List of channel values to look up. |
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/doc.V2ContactSearchReq"
        description: Search parameters
        required: true
      responses:
        '200':
          description: Matching contacts
          content:
            application/json:
              schema:
                items:
                  "$ref": "#/components/schemas/doc.Contact"
                type: array
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Search Contacts (V2)
      tags:
      - Contacts
      operationId: v2SearchContacts
  "/v2/contact/{contactID}/channel":
    post:
      description: Add a new channel (phone, email, etc.) to an existing contact.
      parameters:
      - description: Contact UUID
        in: path
        name: contactID
        required: true
        schema:
          type: string
      requestBody:
        "$ref": "#/components/requestBodies/doc.V2ContactChannelCreateReq"
      responses:
        '200':
          description: Updated list of contact channels
          content:
            application/json:
              schema:
                items:
                  "$ref": "#/components/schemas/doc.ContactChannel"
                type: array
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Create Contact Channel (V2)
      tags:
      - Contacts
      operationId: v2CreateContactChannel
    put:
      description: Update an existing channel on a contact.
      parameters:
      - description: Contact UUID
        in: path
        name: contactID
        required: true
        schema:
          type: string
      requestBody:
        "$ref": "#/components/requestBodies/doc.V2ContactChannelCreateReq"
      responses:
        '200':
          description: Updated list of contact channels
          content:
            application/json:
              schema:
                items:
                  "$ref": "#/components/schemas/doc.ContactChannel"
                type: array
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Update Contact Channel (V2)
      tags:
      - Contacts
      operationId: v2UpdateContactChannel
  "/v2/contact/{contactID}/channel/{channelType}":
    get:
      description: Retrieve a specific channel of a contact by channel type.
      parameters:
      - description: Contact UUID
        in: path
        name: contactID
        required: true
        schema:
          type: string
      - description: Channel type (e.g. phone, email, facebook)
        in: path
        name: channelType
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Contact channel
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/doc.ContactChannel"
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Retrieve Contact Channel (V2)
      tags:
      - Contacts
      operationId: v2GetContactChannel
    delete:
      description: Delete a specific channel from a contact by channel type.
      parameters:
      - description: Contact UUID
        in: path
        name: contactID
        required: true
        schema:
          type: string
      - description: Channel type (e.g. phone, email, facebook)
        in: path
        name: channelType
        required: true
        schema:
          type: string
      responses:
        '200':
          description: ok
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: ok
        '400':
          description: bad_data
          content:
            text/plain:
              schema:
                type: string
              examples:
                response:
                  value: error
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Delete Contact Channel (V2)
      tags:
      - Contacts
      operationId: v2DeleteContactChannel
  "/v2/contact/{contactID}/channels":
    get:
      description: Retrieve all channels associated with a contact.
      parameters:
      - description: Contact UUID
        in: path
        name: contactID
        required: true
        schema:
          type: string
      responses:
        '200':
          description: List of contact channels
          content:
            application/json:
              schema:
                items:
                  "$ref": "#/components/schemas/doc.ContactChannel"
                type: array
        '400':
          description: bad_data
          content:
            application/json:
              schema:
                type: string
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: List Contact Channels (V2)
      tags:
      - Contacts
      operationId: v2ListContactChannels
  "/v1/link":
    post:
      description: |
        Create a tracked short URL for a destination URL. The link is owned by the team identified
        by your API credentials.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `original_url` | **string (required)** <br> Destination URL. Must use the `http` or `https` scheme and be at most 2048 characters. URLs that are already Heymarket short links are rejected. |
        | `conversation_id` | **integer (optional)** <br> Conversation to attribute the link to. |
        | `broadcast_id` | **integer (optional)** <br> Broadcast to attribute the link to. |
        | `campaign_step_id` | **integer (optional)** <br> Campaign step to attribute the link to. |
      requestBody:
        "$ref": "#/components/requestBodies/link.ShortenLinkReq"
      responses:
        '200':
          description: The created Link
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.Link"
        '400':
          description: Missing or invalid original_url, or malformed request body.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '403':
          description: Team does not have the link tracking feature (link_tracking_not_available).
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        '503':
          description: Link service unavailable.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Create Link
      tags:
      - Links
      operationId: v1CreateLink
  "/v1/batch/links":
    post:
      description: |
        Create up to 5 tracked short URLs in one request. Validation is all-or-nothing: if any item
        is invalid, no links are created.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `links` | **array (required)** <br> 1 to 5 link items. Each item takes the same attributes as the Create Link request body. |
      requestBody:
        "$ref": "#/components/requestBodies/link.BulkShortenLinkReq"
      responses:
        '200':
          description: The created Links
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.BulkShortenLinksResp"
        '400':
          description: Empty links array, more than 5 links, or an invalid original_url.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '403':
          description: Team does not have the link tracking feature (link_tracking_not_available).
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        '503':
          description: Link service unavailable.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Bulk Create Links
      tags:
      - Links
      operationId: v1BulkCreateLinks
  "/v1/link/code/{code}":
    get:
      description: Fetch a link by its short code. Short codes belonging to other teams return `404`.
      parameters:
      - description: Code segment of the short URL (for example `AbCd12`).
        in: path
        name: code
        required: true
        schema:
          type: string
      responses:
        '200':
          description: The Link
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.Link"
        '400':
          description: The code path parameter is blank.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '403':
          description: Team does not have the link tracking feature (link_tracking_not_available).
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        '404':
          description: No link with this short code exists for your team.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        '503':
          description: Link service unavailable.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Get Link by Short Code
      tags:
      - Links
      operationId: v1GetLinkByShortCode
  "/v1/link/{id}":
    put:
      description: |
        Change the destination URL of an existing short link. The short URL keeps its code; future
        clicks redirect to the new destination.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `new_url` | **string (required)** <br> New destination URL. Must use the `https` scheme and be at most 2048 characters. |
      parameters:
      - description: ID of the link to update.
        in: path
        name: id
        required: true
        schema:
          type: integer
      requestBody:
        "$ref": "#/components/requestBodies/link.UpdateOriginalURLReq"
      responses:
        '200':
          description: The updated Link
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.Link"
        '400':
          description: Invalid id path parameter, missing or invalid new_url, or malformed request body.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '403':
          description: Team does not have the link tracking feature (link_tracking_not_available).
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        '404':
          description: No link with this ID exists for your team.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        '503':
          description: Link service unavailable.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Update Link URL
      tags:
      - Links
      operationId: v1UpdateLink
    delete:
      description: Delete a link by its ID. IDs belonging to other teams return `404`.
      parameters:
      - description: ID of the link to delete.
        in: path
        name: id
        required: true
        schema:
          type: integer
      responses:
        '200':
          description: Link deleted
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.DeleteLinksResp"
        '400':
          description: The id path parameter is not a valid integer.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '403':
          description: Team does not have the link tracking feature (link_tracking_not_available).
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        '404':
          description: No link with this ID exists for your team.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        '503':
          description: Link service unavailable.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Delete Link
      tags:
      - Links
      operationId: v1DeleteLink
    get:
      description: Fetch a link by its ID. IDs belonging to other teams return `404`.
      parameters:
      - description: Link ID
        in: path
        name: id
        required: true
        schema:
          type: integer
      responses:
        '200':
          description: The Link
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.Link"
        '400':
          description: The id path parameter is not a valid integer.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '403':
          description: Team does not have the link tracking feature (link_tracking_not_available).
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        '404':
          description: No link with this ID exists for your team.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        '503':
          description: Link service unavailable.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Get Link
      tags:
      - Links
      operationId: v1GetLinkByID
  "/v1/links":
    post:
      description: |
        Get your team's links, newest first by default. Requires paginating to fetch all.
        Sending a request with an empty body (or empty JSON object) returns the first page of 20 links.

        #### Request body attributes

        | Attributes | Description |
        | --- | --- |
        | `page` | **integer (optional)** <br> Page number to fetch. |
        | `limit` | **integer (optional)** <br> Links per page, between 1 and 100. Defaults to 20. Values above 100 are rejected. |
        | `order` | **string (optional)** <br> `asc` or `desc` by creation date. Defaults to `desc`. |
      requestBody:
        "$ref": "#/components/requestBodies/link.FetchLinksReq"
      responses:
        '200':
          description: Page of Links in team
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.LinkList"
        '400':
          description: Limit outside 1-100, or malformed request body.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
        '403':
          description: Team does not have the link tracking feature (link_tracking_not_available).
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        '429':
          "$ref": "#/components/responses/TooManyRequestsError"
        '503':
          description: Link service unavailable.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/link.ErrorResponse"
        default:
          "$ref": "#/components/responses/DefaultServerError"
      security:
      - ApiSecretJwtAuth: []
      - LegacyApiKeyAuth: []
      summary: Paginate Links
      tags:
      - Links
      operationId: v1ListLinks
servers:
- url: https://api.heymarket.com
components:
  requestBodies:
    doc.V2ContactReq:
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/doc.V2ContactReq"
      description: Contact json
      required: true
    doc.V2ContactGetReq:
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/doc.V2ContactGetReq"
      description: Contact lookup json
      required: true
    doc.V2ListReq:
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/doc.V2ListReq"
      description: List json
      required: true
    doc.V2ContactChannelCreateReq:
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/doc.V2ContactChannelCreateReq"
      description: Channel json
      required: true
    pagination.V2PaginationParams:
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/pagination.V2PaginationParams"
      description: Pagination json
      required: true
    pagination.V2MessagePaginationParams:
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/pagination.V2MessagePaginationParams"
      description: Message pagination json
      required: true
    doc.V2ContactUpdateReq:
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/doc.V2ContactUpdateReq"
      description: Contact json
      required: true
    doc.V2UpdateContactReq:
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/doc.V2UpdateContactReq"
      description: Contact json
      required: true
    doc.MarkMessageRequest:
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/doc.MarkMessageRequest"
      description: Conversations json
      required: true
    doc.OpenCloseConversationRequest:
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/doc.OpenCloseConversationRequest"
      description: Conversations json
      required: true
    doc.V2ScheduleReq:
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/doc.V2ScheduleReq"
      description: Schedule json
      required: true
    link.ShortenLinkReq:
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/link.ShortenLinkReq"
      description: Link json
      required: true
    link.BulkShortenLinkReq:
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/link.BulkShortenLinkReq"
      description: Bulk link json
      required: true
    link.UpdateOriginalURLReq:
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/link.UpdateOriginalURLReq"
      description: Link url update json
      required: true
    link.FetchLinksReq:
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/link.FetchLinksReq"
      description: Link pagination json. All attributes are optional; an empty body returns the first page.
      required: false
  securitySchemes:
    ApiSecretJwtAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |-
        Recommended authentication for new integrations. Generate a short-lived JWT from your API Secret ID and API Secret Key, then pass the signed JWT as a bearer token in the `Authorization` header.

        API Secret credentials are available in the Heymarket app under Settings > Integrations > API. The Secret Key is shown only when it is generated, so copy and store it securely before closing the dialog.

        Use the following JWT values:

        ```json
        {
          "alg": "HS256",
          "typ": "JWT"
        }
        ```

        ```text
        {
          "iss": "YOUR_API_SECRET_ID",
          "iat": CURRENT_UNIX_TIMESTAMP
        }
        ```

        Replace `CURRENT_UNIX_TIMESTAMP` with the current Unix timestamp in seconds when generating the token.

        Sign the JWT with HMAC-SHA256 using this signing secret:

        ```text
        YOUR_API_SECRET_ID||YOUR_API_SECRET_KEY
        ```

        Send the signed JWT as a bearer token:

        ```text
        Authorization: Bearer YOUR_SIGNED_JWT
        ```

        Tokens expire 5 minutes after the `iat` timestamp. Generate a new JWT per request, or cache it briefly for less than 5 minutes. Generate JWTs only from trusted server-side code; do not expose the API Secret Key in browser, mobile, or other client-side code.
    LegacyApiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: Team API key
      description: |-
        Legacy authentication for existing integrations. Provide the Heymarket team API key as a bearer token in the `Authorization` header.

        ```text
        Authorization: Bearer YOUR_API_KEY
        ```

        API key authentication is planned for deprecation. New integrations should use API Secret JWT authentication.
  schemas:
    doc.V2ContactGetReq:
      type: object
      properties:
        phone:
          example: '12345678900'
          format: phone
          type: string
        external_id:
          type: string
          description: External Contact ID
        type:
          type: string
          description: External Service
    doc.V2ContactReq:
      required:
      - phone
      properties:
        avatar:
          example: https://some.image.url.com/img.jpg
          format: url
          type: string
        custom:
          type: object
        display_name:
          example: John Smith
          type: string
        email:
          example: john.s@heymarket.com
          format: email
          type: string
        first:
          example: John
          type: string
        last:
          example: Smith
          type: string
        phone:
          example: '12345678900'
          format: phone
          type: string
        external_id:
          type: string
          description: External Contact ID
        type:
          type: string
          description: External Service
        timezone:
          example: America/New_York
          type: string
          description: IANA timezone for the contact (e.g., America/New_York)
        assignee_id:
          example: 42
          type: integer
        tags:
          example:
          - tag_id: 1
          - tag_id: 2
          type: array
          items:
            type: object
            properties:
              tag_id:
                type: integer
        is_opted_out:
          example: true
          type: boolean
      type: object
    doc.V2ContactUpdateReq:
      type: object
      required:
      - phone
      properties:
        avatar:
          example: https://some.image.url.com/img.jpg
          format: url
          type: string
        custom:
          type: object
        display_name:
          example: John Smith
          type: string
        email:
          example: john.s@heymarket.com
          format: email
          type: string
        first:
          example: John
          type: string
        last:
          example: Smith
          type: string
        phone:
          example: '12345678900'
          format: phone
          type: string
        external_id:
          type: string
          description: External Contact ID
        type:
          type: string
          description: External Service
        timezone:
          example: America/New_York
          type: string
          description: IANA timezone for the contact (e.g., America/New_York)
        assignee_id:
          example: 42
          type: integer
        tags:
          example:
          - tag_id: 1
          - tag_id: 2
          type: array
          items:
            type: object
            properties:
              tag_id:
                type: integer
        is_opted_out:
          example: true
          type: boolean
    doc.V2UpdateContactReq:
      type: object
      properties:
        avatar:
          example: https://some.image.url.com/img.jpg
          format: url
          type: string
        contact_channels:
          type: array
          items:
            "$ref": "#/components/schemas/doc.V2ContactChannelReq"
          description: Contact channel(s) to associate with the contact
        custom:
          type: object
        display_name:
          example: John Smith
          type: string
        first:
          example: John
          type: string
        last:
          example: Smith
          type: string
        assignee_id:
          example: 42
          type: integer
        tags:
          example:
          - tag_id: 1
          - tag_id: 2
          type: array
          items:
            type: object
            properties:
              tag_id:
                type: integer
        is_opted_out:
          example: true
          type: boolean
        email_suppressions:
          type: array
          items:
            type: integer
          description: List of email suppression IDs to associate with the contact
        timezone:
          example: America/New_York
          type: string
          description: IANA timezone for the contact (e.g., America/New_York)
    doc.V2CreateUpdateResp:
      properties:
        id:
          type: integer
        rev:
          type: integer
        uuid:
          type: string
      type: object
    doc.V2TemplateReq:
      required:
      - title
      - content
      properties:
        archived:
          type: boolean
        content:
          "$ref": "#/components/schemas/doc.GalleryContent"
        local_id:
          type: string
        title:
          type: string
      type: object
    doc.Attachment:
      properties:
        name:
          type: string
        type:
          type: string
        url:
          type: string
      type: object
    doc.Contact:
      properties:
        avatar:
          type: string
        inbox_opt_outs:
          description: >-
            Per-inbox opt-out breakdown, present on contact reads when the number has inbox-scoped
            opt-out rows. Omitted for contacts with no scoped rows.
          type: array
          items:
            "$ref": "#/components/schemas/doc.InboxOptOut"
        created:
          type: string
        creator_id:
          type: integer
        custom:
          type: object
        display_name:
          type: string
        email:
          type: string
        external_id:
          type: string
        external_ref:
          type: string
        first:
          type: string
        id:
          type: integer
        inbox_id:
          type: integer
        last:
          type: string
        note:
          items:
            "$ref": "#/components/schemas/doc.ContactNote"
          type: array
        op:
          type: string
        parent_id:
          type: integer
        phone:
          type: string
        rev:
          type: integer
        shared:
          type: boolean
        team_id:
          type: integer
        type:
          type: string
        updated:
          type: string
        assigned_user_id:
          type: integer
        tags:
          type: array
          items: {}
      type: object
    doc.ContactField:
      properties:
        id:
          type: integer
        op:
          type: string
        rev:
          type: integer
        tid:
          type: integer
        title:
          type: string
        uid:
          type: integer
      type: object
    doc.V2ContactFieldReq:
      required:
      - title
      properties:
        title:
          type: string
      type: object
    doc.CreateContactFieldResponse:
      properties:
        id:
          type: integer
        title:
          type: string
        duplicate:
          type: boolean
      type: object
    doc.ContactStatusResponse:
      properties:
        id:
          type: integer
          example: 10000
        phone:
          example: '12345678900'
          format: phone
          type: string
        unsubscribed:
          example: false
          type: boolean
        unsubscribed_contact:
          example: false
          type: boolean
        unsubscribed_admin:
          example: false
          type: boolean
        blocked:
          example: false
          type: boolean
        inbox_opt_outs:
          description: >-
            Inboxes this contact is opted out of via inbox-scoped opt-outs. Only
            present when the number has inbox-scoped opt-out rows; omitted otherwise and for
            global (team-wide) opt-outs. The `unsubscribed`, `unsubscribed_contact`,
            and `unsubscribed_admin` booleans above keep their existing any-row
            semantics and are unaffected by this field.
          type: array
          items:
            "$ref": "#/components/schemas/doc.InboxOptOut"
        global_opt_out:
          description: >-
            True when the number has a team-wide (non-inbox-scoped) opt-out row, independent of
            `inbox_opt_outs`. Always present, including when false. The two kinds coexist: with
            this true, the contact is blocked on every inbox, including inboxes absent from
            `inbox_opt_outs` — read both fields before treating an inbox as sendable.
          type: boolean
          example: false
      type: object
    doc.InboxOptOut:
      description: A single inbox-scoped opt-out entry. Only teams using per-inbox opt-out accumulate these rows.
      properties:
        inbox_id:
          description: The inbox the contact/number is opted out of.
          type: integer
          example: 2
        set_from_contact:
          description: >-
            True if the contact opted themselves out (e.g. replied STOP). False/absent = the
            opt-out was set by an agent or the API.
          type: boolean
          example: false
      type: object
    doc.UnsubscribeRequest:
      required:
      - number
      properties:
        number:
          description: Phone number to opt out.
          example: '12345678900'
          format: phone
          type: string
        inbox_ids:
          description: >-
            Optional inboxes to scope the opt-out to. Per-inbox opt-out mode only.
            Omit for a global (team-wide) opt-out, or pass user_id to cover that user's inboxes.
          type: array
          items:
            type: integer
          example:
          - 2
          - 5
        user_id:
          description: >-
            Optional acting user. On a team using per-inbox opt-out, supplying user_id WITHOUT
            inbox_ids scopes the opt-out to the inboxes that user belongs to (skipping inboxes with
            no phone number) — the same set the web app's "all inboxes" opt-out covers. Supplied
            together with inbox_ids it does not change the scope, but every requested inbox must be
            one the user can access. On a team using global opt-out the write stays team-wide and
            user_id is recorded as the actor. The user must be an active member of the authenticated
            team. Recorded on the created rows and surfaced as `user_id` on opt-out webhooks.
          type: integer
          example: 4242
      type: object
    doc.UnsubscribedNumberFeedRow:
      description: >-
        One opt-out row in the sync feed — either live (deleted_at absent: currently opted out)
        or a tombstone (deleted_at set: re-subscribed at that time). Key rows on
        (number + inbox_id/global scope), not id — an identical re-opt-out mints a new row id
        and hard-purges the superseded tombstone.
      properties:
        id:
          description: >-
            Row id. Not a stable identity for the opt-out — see the keying note above. Combined
            with updated_at it forms the pagination cursor.
          type: integer
          example: 981
        op:
          description: Always `get` in this feed.
          type: string
          example: get
        team_id:
          description: Team that owns the opt-out.
          type: integer
          example: 700
        user_id:
          description: User who created the opt-out. Omitted for customer-initiated opt-outs.
          type: integer
          example: 4242
        number:
          description: Opted-out phone number.
          type: string
          example: '12345678900'
        target:
          description: Same value as `number`.
          type: string
          example: '12345678900'
        set_from_contact:
          description: True if the contact opted themselves out (e.g. replied STOP).
          type: boolean
          example: false
        created_at:
          description: When the opt-out was created.
          format: date-time
          type: string
        updated_at:
          description: >-
            Last change time — opt-out creation or re-subscribe. Sync-feed cursor; pass it back
            as `date` together with `id` to fetch the next page. Every writer stamps it from the
            database clock at statement time, so rows arrive in cursor order under normal load
            and re-requesting the same cursor is always safe.

            Ordering is not a commit-order guarantee: a write inside a longer transaction is
            stamped before that transaction commits, so it can become visible after a later
            cursor position has already been served. Re-poll from a few minutes behind your last
            cursor rather than from it exactly. Rows are keyed by `number` plus scope and carry
            their own `updated_at`/`deleted_at`, so replaying overlap is idempotent — an empty
            page means caught up as of the lag you allow for.
          format: date-time
          type: string
        deleted_at:
          description: >-
            Absent/null = currently opted out. Set = the number was re-subscribed at that time
            (the row is a tombstone).
          format: date-time
          type: string
        double_optin_restricted:
          description: >-
            True when the opt-out came from a double-opt-in restriction. False/absent otherwise.
          type: boolean
          example: false
        inbox_id:
          description: Origin inbox of the opt-out. Omitted for legacy/no-inbox writers.
          type: integer
          example: 2
        inbox_scoped:
          description: >-
            True = the opt-out blocks only inbox_id (per-inbox scope). False/absent = the
            opt-out blocks every inbox (global scope).
          type: boolean
          example: true
      type: object
    doc.BulkUnsubscribeStatusRequest:
      properties:
        numbers:
          description: Phone numbers to check.
          type: array
          items:
            type: string
          example:
          - '12345678900'
        target:
          description: A group target whose member numbers are also checked.
          type: string
      type: object
    doc.BulkUnsubscribeStatusResponse:
      properties:
        unsubscribed_numbers:
          type: array
          items:
            "$ref": "#/components/schemas/doc.UnsubscribeStatus"
      type: object
    doc.BulkUnsubscribeRequest:
      required:
      - phone_numbers
      properties:
        phone_numbers:
          description: Phone numbers to opt out. Max 5000.
          type: array
          items:
            type: string
          example:
          - '12345678900'
          - '12345678901'
        inbox_ids:
          description: >-
            Optional inboxes to scope the opt-out to. Per-inbox opt-out mode only.
            Omit for a global (team-wide) opt-out, or pass user_id to cover that user's inboxes.
          type: array
          items:
            type: integer
          example:
          - 2
          - 5
        user_id:
          description: >-
            Optional acting user. On a team using per-inbox opt-out, supplying user_id WITHOUT
            inbox_ids scopes the opt-out to the inboxes that user belongs to (skipping inboxes with
            no phone number) — the same set the web app's "all inboxes" opt-out covers. Supplied
            together with inbox_ids it does not change the scope, but every requested inbox must be
            one the user can access. On a team using global opt-out the write stays team-wide and
            user_id is recorded as the actor. The user must be an active member of the authenticated
            team. Recorded on the created rows and surfaced as `user_id` on opt-out webhooks.
          type: integer
          example: 4242
      type: object
    doc.BulkUnsubscribeResponse:
      properties:
        total_requested:
          description: Number of unique, deduplicated phone numbers in the request.
          type: integer
          example: 2
        total_opted_out:
          description: Number of phone numbers that were opted out.
          type: integer
          example: 2
        total_skipped:
          description: >-
            Number of phone numbers that were valid but produced no opt-out row because they
            were already opted out — team-wide for a global request, or of every requested
            inbox for a scoped one.
          type: integer
          example: 0
        failed_phone_numbers:
          description: >-
            Phone numbers that failed validation, or that could not be opted out. Omitted when
            none failed. Numbers with no matching contact are still opted out and are not
            listed here.
          type: array
          items:
            type: string
        failed_inbox_ids:
          description: >-
            Scoped requests only. Inboxes whose opt-out write failed for at least one number, so a
            partially applied batch is visible. Omitted when every inbox succeeded.
          type: array
          items:
            type: integer
        partial:
          description: True when failed_inbox_ids is non-empty, i.e. the batch was only partially applied.
          type: boolean
          example: false
        applied_scope:
          description: >-
            Present as `team_wide` only when user_id was supplied and the batch was applied
            team-wide because the team is on global opt-out mode or lacks the inbox-level opt-out
            entitlement. Omitted when the requested scope was applied as asked.
          type: string
          enum: [team_wide]
      type: object
    doc.UnsubscribeStatus:
      properties:
        number:
          example: '12345678900'
          format: phone
          type: string
        is_unsubscribed:
          description: True if the number has any opt-out row (global or inbox-scoped).
          type: boolean
          example: true
        set_from_contact:
          description: True if a representative opt-out was set by the contact (e.g. replied STOP).
          type: boolean
          example: false
        inbox_opt_outs:
          description: >-
            Inboxes this number is opted out of via inbox-scoped opt-outs. Per-inbox
            opt-out mode only; omitted for global opt-outs and global-mode teams.
          type: array
          items:
            "$ref": "#/components/schemas/doc.InboxOptOut"
        global_opt_out:
          description: >-
            True if a team-wide (global) opt-out row exists for this number. Omitted
            when no global row exists.
          type: boolean
          example: false
      type: object
    doc.ContactStatusRequest:
      properties:
        id:
          example: 10000
          type: integer
        phone:
          example: '12345678900'
          format: phone
          type: string
      type: object
    doc.ContactSetStatusRequest:
      properties:
        id:
          example: 10000
          type: integer
        phone:
          example: '12345678900'
          format: phone
          type: string
        status:
          example: active
          type: string
        inbox_ids:
          description: >-
            Optional inbox ids to scope a `subscribed`/`active`/`unsubscribed`
            status change to specific inboxes instead of team-wide. Applies only
            to teams on per-inbox opt-out mode (requires the inbox-level opt-out
            entitlement). Omitting this field keeps the existing global (team-wide)
            behavior, or pass user_id to cover that user's inboxes. Ignored for the
            `blocked`/`unblocked` statuses.
          type: array
          items:
            type: integer
          example:
          - 2
          - 5
        user_id:
          description: >-
            Optional acting user, applied to `subscribed`/`active`/`unsubscribed`. On a team using
            per-inbox opt-out, supplying user_id WITHOUT inbox_ids scopes the change to the inboxes
            that user belongs to (skipping inboxes with no phone number). Supplied together with
            inbox_ids it does not change the scope, but every requested inbox must be one the user
            can access. On a team using global opt-out the change stays team-wide with user_id
            recorded as the actor. The user must be an active member of the authenticated team.
          type: integer
          example: 4242
      type: object
    doc.ContactNote:
      properties:
        date:
          type: string
        id:
          type: string
        name:
          type: string
        text:
          type: string
        user_id:
          type: integer
      type: object
    doc.Conversation:
      properties:
        assigned:
          type: integer
        blocked:
          type: boolean
        channel:
          type: string
        created:
          type: string
        creator:
          type: integer
        email_noti:
          type: boolean
        id:
          type: integer
        inbox:
          type: integer
        last_inbound:
          type: integer
        local_id:
          type: string
        members:
          items:
            "$ref": "#/components/schemas/doc.ConversationMember"
          type: array
        muted:
          type: boolean
        name:
          type: string
        noreply:
          type: string
        op:
          type: string
        read:
          type: integer
        replied:
          type: boolean
        snooze_till:
          type: string
        status:
          type: string
        super:
          type: integer
        support:
          type: boolean
        target:
          type: string
        type:
          type: string
        updated:
          type: string
      type: object
    doc.ConversationMember:
      properties:
        id:
          type: integer
        name:
          type: string
      type: object
    doc.CustomMessageContent:
      properties:
        data:
          items:
            type: integer
          type: array
        type:
          type: string
      type: object
    doc.GalleryContent:
      properties:
        files:
          items:
            "$ref": "#/components/schemas/doc.FileEntry"
          type: array
        gallery:
          items:
            "$ref": "#/components/schemas/doc.GalleryEntry"
          type: array
        text:
          type: string
      type: object
    doc.GalleryEntry:
      properties:
        annotation_position:
          type: string
        annotation_text:
          type: string
        original_url:
          type: string
        url:
          type: string
      type: object
    doc.FileEntry:
      properties:
        content_type:
          type: string
        url:
          type: string
      type: object
    doc.Inbox:
      properties:
        allowed_domains:
          items:
            type: string
          type: array
        auto_assignable:
          type: boolean
        bco_chats:
          type: boolean
        forward_number:
          type: string
        id:
          type: integer
        invited:
          items:
            type: string
          type: array
        members:
          items:
            type: integer
          type: array
        name:
          type: string
        op:
          type: string
        phone:
          type: string
        phones:
          items:
            type: string
          type: array
        rev:
          type: integer
        team:
          type: integer
        widget_code:
          type: string
        widget_settings:
          "$ref": "#/components/schemas/doc.WidgetSettings"
      type: object
    doc.TeamUser:
      properties:
        id:
          type: integer
        phone:
          type: string
        name:
          type: string
        email:
          type: string
        role_id:
          type: integer
        created_at:
          type: string
        updated_at:
          type: string
        user_created_at:
          type: string
        user_updated_at:
          type: string
        team_id:
          type: integer
      type: object
    doc.UserGroup:
      properties:
        id:
          type: integer
        name:
          type: string
        description:
          type: string
        member_ids:
          items:
            type: integer
          type: array
        members:
          items:
            "$ref": "#/components/schemas/doc.TeamUser"
          type: array
      type: object
    doc.UserGroupsResp:
      properties:
        user_groups:
          items:
            "$ref": "#/components/schemas/doc.UserGroup"
          type: array
      type: object
    doc.Team:
      properties:
        id:
          type: integer
        name:
          type: string
        list_size:
          type: integer
        members:
          items:
            "$ref": "#/components/schemas/doc.Member"
          type: array
      type: object
    doc.Member:
      properties:
        id:
          type: integer
        name:
          type: string
        role:
          type: string
      type: object
    doc.List:
      properties:
        archived:
          type: boolean
        created:
          type: string
        creator_id:
          type: integer
        id:
          type: integer
        local_id:
          type: string
        name:
          type: string
        op:
          type: string
        rev:
          type: integer
        shared:
          type: boolean
        targets:
          type: object
        team_id:
          type: integer
        updated:
          type: string
      type: object
    doc.Message:
      properties:
        author:
          type: string
        broadcast_id:
          type: integer
        conversation:
          "$ref": "#/components/schemas/doc.Conversation"
        custom:
          "$ref": "#/components/schemas/doc.CustomMessageContent"
        date:
          type: string
        flagged:
          type: boolean
        id:
          type: integer
        link_url:
          type: string
        local_id:
          type: string
        media:
          type: string
        phone:
          type: string
        raw_error:
          type: string
          description: Failure reason for an outbound message. Set when `status` is
            `failed`, `undelivered`, `dropped`, or `bounce`.
        status:
          type: string
        super:
          type: integer
        support:
          type: boolean
        target:
          type: string
        target_errors:
          type: object
          additionalProperties:
            type: string
          description: Failure detail for an outbound message. Keyed by target for
            carrier and group failures; some channels key it by provider metadata
            (`provider`, `message`, `status_code`) instead.
        text:
          type: string
        type:
          type: string
        updated:
          type: string
        user:
          type: integer
      type: object
    doc.Scheduled:
      properties:
        created:
          type: string
        execute:
          type: string
        id:
          type: integer
        inbox_id:
          type: integer
        local_id:
          type: string
        meta:
          "$ref": "#/components/schemas/doc.ScheduledMetadata"
        op:
          type: string
        rev:
          type: integer
        updated:
          type: string
      type: object
    doc.ScheduledContent:
      properties:
        attachments:
          items:
            "$ref": "#/components/schemas/doc.Attachment"
          type: array
        gallery:
          items:
            "$ref": "#/components/schemas/doc.GalleryEntry"
          type: array
        local_day:
          type: integer
        local_time:
          type: string
        offset:
          type: integer
        template:
          type: integer
        text:
          type: string
        to:
          type: string
      type: object
    doc.ScheduledMetadata:
      properties:
        campaign_id:
          type: integer
        campaign_step_id:
          type: integer
        campaign_target_phone:
          description: 'Only internal, shouldn''t be delivered to users as not on scheduled message objects.'
          type: string
        content:
          "$ref": "#/components/schemas/doc.ScheduledContent"
        conversation_id:
          type: integer
        list_id:
          type: integer
      type: object
    doc.Template:
      properties:
        archived:
          type: boolean
        content:
          "$ref": "#/components/schemas/doc.GalleryContent"
        created:
          type: string
        creator_id:
          type: integer
        id:
          type: integer
        local_id:
          type: string
        name:
          type: string
        op:
          type: string
        rev:
          type: integer
        shared:
          type: boolean
        team_id:
          type: integer
        updated:
          type: string
      type: object
    doc.V2ListReq:
      properties:
        add_phone:
          example: '14155550102'
          format: phone
          type: string
        archived:
          example: false
          type: boolean
        local_id:
          example: 05debcb7-ce28-482e-a04b-effaf6210b11
          type: string
        members:
          type: object
        patch:
          type: boolean
        remove_phone:
          example: '14155550102'
          format: phone
          type: string
        title:
          example: My VIP list
          type: string
      type: object
    doc.V2ScheduleReq:
      properties:
        _:
          type: integer
        content:
          "$ref": "#/components/schemas/doc.ScheduledContent"
        conversation_id:
          type: integer
        execute_at:
          type: string
        inbox_id:
          type: integer
        is_email:
          type: boolean
        list_id:
          type: integer
        local_id:
          type: string
        phone_number:
          type: string
        super_id:
          type: integer
        targets:
          items:
            type: string
          type: array
        user_id:
          type: integer
      type: object
    doc.WidgetSettings:
      properties:
        background_color:
          type: string
        button_color:
          type: string
        fab_background_color:
          type: string
        fab_text_color:
          type: string
        message:
          type: string
        name:
          type: string
        phone:
          type: string
        position:
          type: integer
        preview_enabled:
          type: boolean
        title_background_color:
          type: string
        title_text_color:
          type: string
        tos:
          type: string
      type: object
    doc.MarkMessageRequest:
      properties:
        inbox_id:
          type: integer
        user_id:
          type: integer
        target:
          type: string
        targets:
          items:
            type: string
          type: array
        reassign_id:
          type: integer
    doc.OpenCloseConversationRequest:
      properties:
        chat_id:
          type: integer
        inbox_id:
          type: integer
        target:
          type: string
        targets:
          items:
            type: string
          type: array
        user_id:
          type: integer
      type: object
      example:
        chat_id: 1
        inbox_id: 1
        target: '14151234567'
        user_id: 1
    doc.TransferConversationRequest:
      properties:
        conversation_id:
          type: integer
        transfer_inbox_id:
          type: integer
      type: object
      example:
        conversation_id: 1
        transfer_inbox_id: 1
    doc.Survey:
      properties:
        id:
          type: integer
        name:
          type: string
        type:
          type: string
        question:
          type: string
        created:
          type: string
        updated:
          type: string
      type: object
    doc.V2TagReq:
      properties:
        tag:
          type: string
      type: object
    doc.Tag:
      properties:
        id:
          type: integer
        team_id:
          type: integer
        tag:
          type: string
        color:
          type: string
        rev:
          type: integer
        created:
          type: string
        updated:
          type: string
      type: object
    doc.OmnichannelListCreateReq:
      required:
      - title
      properties:
        contacts:
          type: array
          items:
            type: integer
          description: Array of contact IDs to replace/set the list membership
        phones:
          type: array
          items:
            type: string
          description: Phone numbers in E.164 format (without plus sign) to add as
            new contacts
        emails:
          type: array
          items:
            type: string
          description: Email addresses to add as new contacts
        title:
          type: string
          description: List name
        local_id:
          type: string
          description: Client-provided unique identifier for the list
    doc.OmnichannelListUpdateReq:
      type: object
      properties:
        contacts:
          type: array
          items:
            type: integer
          description: Array of contact IDs to replace/set the list membership
        phones:
          type: array
          items:
            type: string
          description: Phone numbers in E.164 format (without plus sign) to add as
            new contacts
        emails:
          type: array
          items:
            type: string
          description: Email addresses to add as new contacts
        title:
          type: string
          description: List name
        add_contacts:
          type: array
          items:
            type: integer
          description: Array of contact IDs to add to the list
        remove_contacts:
          type: array
          items:
            type: integer
          description: Array of contact IDs to remove from the list
        add_phones:
          type: array
          items:
            type: string
          description: Phone numbers in E.164 format (without plus sign) to add as
            new contacts
        remove_phones:
          type: array
          items:
            type: string
          description: Phone numbers in E.164 format (without plus sign) to remove
            from the list
        add_emails:
          type: array
          items:
            type: string
          description: Email addresses to add as new contacts
        remove_emails:
          type: array
          items:
            type: string
          description: Email addresses to remove from the list
    doc.OmnichannelListDeleteReq:
      properties:
        contact_ids:
          type: array
          items:
            type: integer
          description: Optional list of contact IDs to remove from the list before
            deletion
      type: object
    pagination.V2PaginationParams:
      properties:
        archived:
          type: boolean
        ascending:
          type: boolean
        assigned:
          type: integer
        date:
          type: string
        filter:
          type: string
        filters:
          type: object
          description: Object-specific filters. For conversations this supports fields such as inboxes, closed, unread, ids, and unanswered.
        id:
          type: integer
        limit:
          type: integer
        order:
          type: string
        page:
          type: integer
        parent_id:
          type: integer
      type: object
    pagination.V2MessagePaginationParams:
      required:
      - created_at
      properties:
        ascending:
          description: True to sort in ascending order. Defaults to descending.
          type: boolean
        created_at:
          description: Last returned message timestamp. Fetch messages created on and after this RFC 3339 timestamp.
          format: date-time
          type: string
        inbox_id:
          description: Optional inbox ID to filter messages.
          type: integer
        limit:
          description: Messages to return per page. Defaults to 30.
          type: integer
        order:
          description: Field to sort by. Valid values are created_at and updated_at.
          type: string
      type: object
    pagination.V2PageLimitParams:
      properties:
        limit:
          type: integer
        page:
          type: integer
      type: object
    server.MessageSendReq:
      required:
      - inbox_id
      - creator_id
      properties:
        activity_id:
          type: string
        ai_team_agent_id:
          type: integer
        author:
          type: string
        chat_id:
          type: integer
        conv_name:
          type: string
        creator_email:
          type: string
        creator_id:
          type: integer
        custom:
          type: object
        gallery:
          type: array
          items:
            "$ref": "#/components/schemas/doc.GalleryEntry"
        inbox_id:
          type: integer
        link_url:
          type: string
        list_id:
          type: integer
        local_id:
          type: string
        media_url:
          type: string
        notify_all:
          type: boolean
        opt_in:
          type: boolean
        phone_number:
          type: string
        prefer_contact_owner:
          type: boolean
        private:
          type: boolean
        signature_mode:
          type: string
          enum:
          - user
          - ai_agent
        survey_id:
          type: integer
        targets:
          items:
            type: string
          type: array
        template_id:
          type: integer
        text:
          type: string
        to:
          type: string
        raw_text:
          type: string
        user_ids:
          type: array
          items:
            type: integer
      type: object
    server.MessageSendResponse:
      properties:
        date:
          type: string
        gallery_url:
          type: string
        id:
          type: integer
      type: object
    doc.ContactChannel:
      properties:
        id:
          type: integer
          description: contact channel ID
        team_id:
          type: integer
          description: team for contact channel
        contact_id:
          type: integer
          description: contact id
        channel_type:
          type: string
          description: channel type (e.g. phone, email, facebook)
        channel_id:
          type: string
          description: value for the channel type (phone number, email address, etc.)
        info_channel:
          type: string
          description: related integration channel
        channel_handle:
          type: string
          description: handle related to channel
        created:
          type: string
          description: created time
        updated:
          type: string
          description: updated time
      type: object
    doc.V2ContactChannelReq:
      required:
      - channel_type
      - channel_id
      properties:
        channel_type:
          type: string
          description: channel type (e.g. phone, email, facebook)
          example: phone
        channel_id:
          type: string
          description: value for the channel type (phone number, email address, etc.)
          example: '14155551234'
        channel_ids:
          type: array
          items:
            type: string
          description: list of channel values; used for bulk operations (e.g. bulk
            delete)
        channel_handle:
          type: string
          description: handle related to channel
        info_channel:
          type: string
          description: related external integration
      type: object
    doc.V2CreateContactReq:
      type: object
      required:
      - contact_channels
      properties:
        avatar:
          example: https://some.image.url.com/img.jpg
          format: url
          type: string
        custom:
          type: object
        display_name:
          example: John Smith
          type: string
        first:
          example: John
          type: string
        last:
          example: Smith
          type: string
        assignee_id:
          example: 42
          type: integer
        tags:
          example:
          - tag_id: 1
          - tag_id: 2
          type: array
          items:
            type: object
            properties:
              tag_id:
                type: integer
        is_opted_out:
          example: true
          type: boolean
        email_suppressions:
          type: array
          items:
            type: integer
          description: List of email suppression IDs to associate with the contact
        contact_channels:
          type: array
          items:
            "$ref": "#/components/schemas/doc.V2ContactChannelReq"
          description: Contact channel(s) to associate with the contact
        timezone:
          example: America/New_York
          type: string
          description: IANA timezone for the contact (e.g., America/New_York)
    doc.V2BulkContactReq:
      properties:
        contacts:
          type: array
          items:
            "$ref": "#/components/schemas/doc.V2CreateContactReq"
          description: List of contacts to create or update
      type: object
    doc.V2BulkContactFailResp:
      properties:
        contact:
          "$ref": "#/components/schemas/doc.V2CreateContactReq"
        error:
          type: string
          description: Error message
      type: object
    doc.V2BulkContactResp:
      properties:
        contacts:
          type: array
          items:
            "$ref": "#/components/schemas/doc.Contact"
          description: Successfully created or updated contacts
        failed_contacts:
          type: array
          items:
            "$ref": "#/components/schemas/doc.V2BulkContactFailResp"
          description: Contacts that failed to create or update
      type: object
    doc.V2ContactChannelCreateReq:
      type: object
      required:
      - channel_type
      - channel_id
      properties:
        channel_type:
          type: string
          description: Channel type (e.g. phone, email, facebook)
          example: phone
        channel_id:
          type: string
          description: Value for the channel type (phone number, email address, etc.)
          example: '14155551234'
    doc.V2ContactListReq:
      type: object
      properties:
        limit:
          type: integer
          description: Number of results per page. Default 30, max 100.
          example: 30
        page:
          type: integer
          description: Page number (0-indexed).
          example: 0
        date:
          type: string
          format: date-time
          description: Return contacts updated since this timestamp. RFC 3339 format.
          example: '2024-01-01T00:00:00Z'
        ascending:
          type: boolean
          description: Sort ascending if true, descending otherwise.
          example: false
    doc.V2ContactSearchReq:
      type: object
      required:
      - contact_channels
      properties:
        contact_channels:
          type: array
          description: |
            One or more channel entries to search by. Each entry specifies the channel type and a list of IDs to match.
            If `channel_type` is omitted, the type is inferred from the ID format (e.g. E.164 phone → phone, email format → email).
          items:
            type: object
            required:
            - channel_ids
            properties:
              channel_type:
                type: string
                description: Channel type to search within (e.g. phone, email, facebook).
                  Omit to auto-detect from ID format.
                example: phone
              channel_ids:
                type: array
                description: List of channel values to search for.
                items:
                  type: string
                example:
                - '14155551234'
                - '14155559876'
    doc.ListSearchReq:
      type: object
      required:
      - q
      properties:
        q:
          type: string
          description: Keyword to match against list names.
          example: VIP
        user_id:
          type: integer
          description: Team member to search as; results are limited to lists visible
            to their user groups. Defaults to the team owner.
          example: 42
    doc.V2ErrorResp:
      type: object
      required:
      - error
      - details
      properties:
        error:
          type: string
          description: Machine-readable snake_case error code. Branch on this field.
          example: invalid_filter
        details:
          type: string
          description: Human-readable explanation of the failure. Do not parse; it
            may change without notice.
          example: 'unsupported filter: name'
    doc.V2BulkDeleteContactReq:
      type: object
      required:
      - contact_channels
      properties:
        contact_channels:
          type: array
          description: Must contain a single entry with channel_type "id" and the
            list of contact UUIDs to delete
          items:
            type: object
            required:
            - channel_type
            - channel_ids
            properties:
              channel_type:
                type: string
                description: Must be "id"
                example: id
              channel_ids:
                type: array
                description: List of contact UUIDs to delete
                items:
                  type: string
                example:
                - uuid-1
                - uuid-2
    WebhookEventType:
      type: string
      description: Heymarket webhook event type. `message_recieved` preserves the
        current API spelling. `message_failed` is sent when an outbound message
        fails, either at send time or when the carrier reports a delivery failure;
        its payload carries `status` and `raw_error`.
      enum:
      - message_sent
      - message_recieved
      - message_failed
      - chat_reassigned
      - chat_closed
      - chat_opened
      - chat_pending
      - chat_transferred
      - target_opt_out
      - target_opt_in
      - target_double_opt_in
      - target_double_opt_restricted
      - incoming_call
    doc.UnSubscribed:
      type: object
      description: Webhook payload for contact opt-in and opt-out events.
      properties:
        id:
          type: integer
          description: Unsubscribed record ID.
        op:
          type: string
          description: Operation emitted through the message bus.
        team_id:
          type: integer
          description: Heymarket team ID.
        user_id:
          type: integer
          description: User who created the unsubscribe record.
        number:
          type: string
          description: Phone number associated with the opt-in or opt-out event.
        target:
          type: string
          description: Target address associated with the opt-in or opt-out event.
        set_from_contact:
          type: boolean
          description: Whether the value was set from a contact update.
        created_at:
          type: string
          format: date-time
          description: Creation timestamp.
        double_optin_restricted:
          type: boolean
          description: Whether the number is restricted by double opt-in state.
    WebhookEvent:
      type: object
      description: Payload Heymarket posts to configured webhook URLs.
      required:
      - type
      - id
      - token
      - event_data
      properties:
        type:
          "$ref": "#/components/schemas/WebhookEventType"
        id:
          type: integer
          description: Webhook configuration ID.
        token:
          type: string
          description: Webhook verification token generated for the webhook configuration.
        event_data:
          description: Event payload. Message events send a Message, chat events send
            a Conversation, and opt-in/opt-out events send an UnSubscribed payload.
          oneOf:
          - "$ref": "#/components/schemas/doc.Message"
          - "$ref": "#/components/schemas/doc.Conversation"
          - "$ref": "#/components/schemas/doc.UnSubscribed"
    link.Link:
      type: object
      properties:
        id:
          type: integer
          format: int64
          example: 42
          description: Unique identifier for the link.
        short_code:
          type: string
          example: AbCd12
          description: Code segment of the short URL.
        short_url:
          type: string
          format: url
          example: https://r.hey.mk/AbCd12
          description: Full short URL to include in messages.
        original_url:
          type: string
          format: url
          example: https://example.com/promo
          description: Destination URL the short link redirects to.
        team_id:
          type: integer
          format: int64
          description: Unique identifier for your team.
        target:
          type: string
          description: Target the link is attributed to.
        conversation_id:
          type: integer
          format: int64
          description: Conversation the link is attributed to.
        broadcast_id:
          type: integer
          format: int64
          description: Broadcast the link is attributed to.
        campaign_step_id:
          type: integer
          format: int64
          description: Campaign step the link is attributed to.
        template_id:
          type: integer
          format: int64
          description: Template the link is attributed to.
        last_visited_at:
          type: string
          format: date-time
          description: Timestamp of the most recent click. Omitted when the link has never been clicked.
        created_at:
          type: string
          format: date-time
          description: Creation date.
    link.ShortenLinkReq:
      type: object
      required:
      - original_url
      properties:
        original_url:
          type: string
          format: url
          example: https://example.com/promo
          description: Destination URL. Must use the http or https scheme and be at most 2048 characters.
        conversation_id:
          type: integer
          format: int64
          description: Conversation to attribute the link to.
        broadcast_id:
          type: integer
          format: int64
          description: Broadcast to attribute the link to.
        campaign_step_id:
          type: integer
          format: int64
          description: Campaign step to attribute the link to.
    link.BulkShortenLinkReq:
      type: object
      required:
      - links
      properties:
        links:
          type: array
          minItems: 1
          maxItems: 5
          items:
            "$ref": "#/components/schemas/link.ShortenLinkReq"
          description: Links to create. Between 1 and 5 per request.
    link.BulkShortenLinksResp:
      type: object
      properties:
        links:
          type: array
          items:
            "$ref": "#/components/schemas/link.Link"
          description: The created links.
        total:
          type: integer
          description: Number of links created.
    link.DeleteLinksResp:
      type: object
      properties:
        message:
          type: string
          example: Link deleted successfully
    link.UpdateOriginalURLReq:
      type: object
      required:
      - new_url
      properties:
        new_url:
          type: string
          format: url
          example: https://example.com/new-destination
          description: New destination URL. Must use the https scheme and be at most 2048 characters.
    link.FetchLinksReq:
      type: object
      properties:
        page:
          type: integer
          description: Page number to fetch.
        limit:
          type: integer
          minimum: 1
          maximum: 100
          default: 20
          description: Links per page. Values above 100 are rejected with 400.
        order:
          type: string
          enum:
          - asc
          - desc
          default: desc
          description: Sort order by creation date.
    link.LinkList:
      type: object
      properties:
        links:
          type: array
          items:
            "$ref": "#/components/schemas/link.Link"
        total:
          type: integer
          description: Total number of links for the team.
        page:
          type: integer
          description: Page returned.
        limit:
          type: integer
          description: Page size used.
    link.ErrorResponse:
      type: object
      required:
      - code
      properties:
        code:
          type: integer
          example: 400
          description: HTTP status code of the error.
        message:
          type: string
          example: original_url is required
          description: Human-readable error message.
        details:
          description: Additional error context. Usually omitted.
  responses:
    UnauthorizedError:
      description: Authentication credentials were missing or invalid.
      content:
        text/plain:
          schema:
            type: string
          examples:
            response:
              value: Unauthorized
    TooManyRequestsError:
      description: The request exceeded the API rate limit.
      content:
        text/plain:
          schema:
            type: string
          examples:
            response:
              value: too many requests
    DefaultServerError:
      description: Unexpected server error.
      content:
        text/plain:
          schema:
            type: string
          examples:
            response:
              value: error
webhooks:
  heymarketEvent:
    post:
      summary: Receive a Heymarket webhook event
      description: Heymarket sends webhook events as HTTPS POST requests to the configured
        webhook URL. Return a 2xx response after accepting the event.
      operationId: receiveHeymarketWebhookEvent
      security: []
      tags:
      - Webhooks
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/WebhookEvent"
      responses:
        '200':
          description: Webhook event accepted.
        default:
          description: Non-2xx responses may cause delivery to be treated as unsuccessful.
