Side conversations allow agents to send messages via email, Slack, or Microsoft Teams to individuals outside the primary discussion of a ticket while keeping those messages organized within the ticket. See About side conversations. The messages that make up a side conversation are logged as events. See Side conversation events.

This article shows an example of using the Zendesk side conversations API to send an email to a recipient and link it to a ticket.

What you need

To use Zendesk side conversations, you'll need a Zendesk account with a Zendesk Suite plan professional or above. You'll also need to be assigned the role of admin in the account.

To run this script, you'll need Python and the requests third-party library.

Creating email side conversations

The following script prompts for the subject and body of the conversation, the ticket number to associate with this conversation, and the recipient email addresses, which can be entered as a comma-separated list. Unlike side conversations in Admin Center, you cannot CC or BCC recipients with the API.

import osimport requestsimport jsonimport logging
# Configure logginglogging.basicConfig(level=logging.INFO)
# Fetch environment variablesZENDESK_USER_EMAIL = os.getenv('ZENDESK_USER_EMAIL')ZENDESK_API_TOKEN = os.getenv('ZENDESK_API_TOKEN')ZENDESK_SUBDOMAIN = os.getenv('ZENDESK_SUBDOMAIN')
# Set authenticationAUTH = f'{ZENDESK_USER_EMAIL}/token', ZENDESK_API_TOKEN
def create_side_conversation(subdomain: str, ticket_id: int, subject: str, body: str, recipient_emails: list) -> None:    # Create a side conversation in a Zendesk ticket    url = f'https://{subdomain}.zendesk.com/api/v2/tickets/{ticket_id}/side_conversations.json'
    headers = {'Content-Type': 'application/json'}    # Construct the 'to' field with multiple recipients    to_recipients = [{"email": email} for email in recipient_emails]
    payload = {        "message": {            "subject": subject,            "body": body,            "to": to_recipients        }    }
    response = requests.post(url, headers=headers, json=payload, auth=AUTH)
    if response.status_code == 201:        logging.info("Side conversation created successfully!")        logging.info("Response:\n%s", json.dumps(response.json(), indent=4))    else:        logging.error("Failed to create side conversation. Status Code: %s, Response: %s", response.status_code, response.text)
def main() -> None:    # Main function to prompt user input and create a side conversation.    subject = input("Enter the subject of the side conversation: ")    body = input("Enter the body of the side conversation: ")
    recipient_emails = input("Enter the recipient email addresses (comma-separated): ")    recipient_emails = [email.strip() for email in recipient_emails.split(',')]  # Split and strip whitespace
    try:        ticket_id = int(input("Enter the ticket ID: "))    except ValueError:        logging.error("Error: Ticket ID must be an integer.")        return
    create_side_conversation(ZENDESK_SUBDOMAIN, ticket_id, subject, body, recipient_emails)
if __name__ == "__main__":    main()

How it works

The script does the following:

  1. Imports the required libraries.

    • os for accessing environment variables.
    • requests for sending HTTP requests.
    • json for formatting JSON data.
    • logging for logging messages.
  2. Retrieves the Zendesk API credentials (ZENDESK_USER_EMAIL and ZENDESK_API_TOKEN) and subdomain (ZENDESK_SUBDOMAIN) from environment variables.

    Environment variables are stored outside the source code, reducing the risk of exposing sensitive information in version control systems. This isolation helps prevent credentials from being accidentally committed to public repositories.

    ZENDESK_USER_EMAIL = os.getenv('ZENDESK_USER_EMAIL')ZENDESK_API_TOKEN = os.getenv('ZENDESK_API_TOKEN')ZENDESK_SUBDOMAIN = os.getenv('ZENDESK_SUBDOMAIN')
  3. Sets up HTTP authentication using the Zendesk email and API token.

    AUTH = f'{ZENDESK_USER_EMAIL}/token', ZENDESK_API_TOKEN
  4. Defines a create_side_conversation function that sends an email to the designated recipients and associates that conversation with a particular ticket. See Side Conversations and Messages.

    def create_side_conversation(subdomain: str, ticket_id: int, subject: str, body: str, recipient_emails: list) -> None:    # This URL points to the specific ticket's side conversations endpoint    url = f'https://{subdomain}.zendesk.com/api/v2/tickets/{ticket_id}/side_conversations.json'
        # Define the headers for the HTTP request, indicating that the content type is JSON    headers = {'Content-Type': 'application/json'}
        # Construct the 'to' field with multiple recipients    to_recipients = [{"email": email} for email in recipient_emails]
        # Create a payload dictionary, containing the message structure which includes subject, body, and recipient's email address    payload = {        "message": {            "subject": subject,            "body": body,            "to": to_recipients        }    }
       # Send a POST request using the requests library   response = requests.post(url, headers=headers, json=payload, auth=AUTH)
       # Check the response status code. If 201, the side conversation was created successfully   if response.status_code == 201:       logging.info("Side conversation created successfully!")       logging.info("Response:\n%s", json.dumps(response.json(), indent=4))
       # If the request is not 201, the request failed. Log an error message that includes the status code and response   else:       logging.error("Failed to create side conversation. Status Code: %s, Response: %s", response.status_code, response.text)
  5. Creates the main function that prompts the user for input and calls the create_side_conversation function.

    def main() -> None:    # Main function to prompt user input and create a side conversation    subject = input("Enter the subject of the side conversation: ")    body = input("Enter the body of the side conversation: ")
        recipient_emails = input("Enter the recipient email addresses (comma-separated): ")    recipient_emails = [email.strip() for email in recipient_emails.split(',')]  # Split and strip whitespace
        # Prompt for the ticket number and log an error if the value entered is not an integer    try:        ticket_id = int(input("Enter the ticket ID: "))    except ValueError:        logging.error("Error: Ticket ID must be an integer.")        return
        create_side_conversation(ZENDESK_SUBDOMAIN, ticket_id, subject, body, recipient_emails)
  6. Adds a conditional statement to execute the main function when the script is run directly.

    if __name__ == "__main__":   main()

Example output

INFO:root:Side conversation created successfully!INFO:root:Response:{    "side_conversation": {        "url": "https://z3n8025.zendesk.com/api/v2/tickets/90/side_conversations/80d17404-e4b8-11ef-9f3b-397460190d64",        "id": "80d17404-e4b8-11ef-9f3b-397460190d64",        "ticket_id": 90,        "subject": "printer not working",        "preview_text": "Try unplugging the printer, wait 30 seconds, and then plug it back in",        "state": "open",        "participants": [            {                "user_id": 1509756491122,                "name": "Customer Care Bear",                "email": "[email protected]"            },            {                "user_id": 27011374436631,                "name": "Chris P Bacon",                "email": "[email protected]"            }        ],        "created_at": "2025-02-06T18:30:55.809Z",        "updated_at": "2025-02-06T18:30:55.809Z",        "message_added_at": "2025-02-06T18:30:55.809Z",        "state_updated_at": "2025-02-06T18:30:55.809Z",        "external_ids": {}    },    "event": {        "id": "80d17404-e4b8-11ef-9f3b-397460190d64",        "side_conversation_id": "80d17404-e4b8-11ef-9f3b-397460190d64",        "actor": {            "user_id": 1509756491122,            "name": "Customer Care Bear",            "email": "[email protected]"        },        "type": "create",        "via": "api",        "created_at": "2025-02-06T18:30:55.809Z",        "message": {            "subject": "printer not working",            "preview_text": "Try unplugging the printer, wait 30 seconds, and then plug it back in",            "from": {                "user_id": 1509756491122,                "name": "Customer Care Bear",                "email": "[email protected]"            },            "to": [                {                    "user_id": 27011374436631,                    "name": "Chris P Bacon",                    "email": "[email protected]"                }            ],            "body": "Try unplugging the printer, wait 30 seconds, and then plug it back in",            "html_body": "<div class=\"zd-comment\">\n<p>Try unplugging the printer, wait 30 seconds, and then plug it back in</p>\n</div>",            "external_ids": {},            "attachments": []        },        "updates": {},        "ticket_id": 90    }}

Troubleshooting

If the side conversation fails to create, check the returned error message. The logging statement will display the status code and response text. Common status codes are:

  • 401 Unauthorized: Indicates an issue with authentication.
  • 404 Not Found: Indicates that the specified ticket does not exist.
  • 400 Bad Request: Indicates that the request payload is invalid.