⚡️ Integrate SignalWire AI Gateway (SWAIG) with Zendesk Support API
LIVEWire Implementation Guide
Introduction
The AI Agent creates, retrieves, updates, and closes tickets, adds comments, and authenticates users via phone using a support PIN.
This empowers users with the ability to securely manage support tickets in a phone call with conversational AI.
Architecture Overview
- AI Agent: Interfaces with users via phone calls.
- SignalWire AI Gateway (SWAIG): Handles AI Agent functions and maps them to Zendesk API endpoints.
- Zendesk Support API: Manages support tickets and user data.

GitHub Repository
View the project and clone it to your development environment.
Prerequisites
- A Zendesk account with admin access
- A free SignalWire account
- Python and basic Python knowledge
Step 1: Set Up Zendesk API Access
Create an API Token
- Log in to Zendesk.
- Navigate to Admin Center > Apps and integrations > Zendesk API > Zendesk API Settings.
- Enable Token Access.
- Click Add API Token.
- Enter a description and click Create.
- Copy the generated token and store it securely.
Create Custom User Fields (Support PIN)
- Navigate to Admin Center > People > Configuration > User Fields.
- Click Add Field.
- Configure the field:
- Type: Text (or Numeric if the PIN is digits only).
- Title:
Support PIN. - Key:
support_pin.
- Save the new custom field.
Step 2: Set Up SWML Script and SWAIG Functions
- Navigate to the SWML Scripts pane of the RELAY/SWML tab of your SignalWire Dashboard.
- Paste the SWML script into the browser SWML editor.
Full SWML script
View the full JSON SWML script in the GitHub repository
Dashboard editor
The SWML script editor in the SignalWire Dashboard
Step 3: Set up your Python environment
- Clone the GitHub repository.
- Install the required Python libraries using
pip:
pip install -r requirements.txt
-
Enter your credentials in the
.envfile.- Rename
env.exampleto.envand open it in a text editor. - Enter your Zendesk subdomain, email, API token, and HTTP basic auth credentials, following the below pattern:
- Rename
| Variable | Description |
|---|---|
ZENDESK_SUBDOMAIN | Your Zendesk subdomain |
ZENDESK_EMAIL | Your Zendesk account email address |
ZENDESK_API_TOKEN | Your Zendesk API token |
HTTP_USERNAME | Username for HTTP Basic Authentication |
HTTP_PASSWORD | Password for HTTP Basic Authentication |
Step 5: Implement Support PIN Authentication
Generate and Assign Support PINs
Use a secure method to generate unique PINs for users.
Example in Python:
import secrets
def generate_support_pin(length=6):
return ''.join([str(secrets.randbelow(10)) for _ in range(length)])
Assign the PIN to the user during registration or onboarding.
Update the user's profile with the support PIN using the Zendesk API.
API Endpoint: PUT /api/v2/users/{user_id}.json
Python Function:
def update_user_support_pin(user_id, support_pin):
url = f'https://{ZENDESK_SUBDOMAIN}.zendesk.com/api/v2/users/{user_id}.json'
data = {
"user": {
"user_fields": {
"support_pin": support_pin
}
}
}
response = requests.put(
url,
json=data,
auth=HTTPBasicAuth(f'{ZENDESK_EMAIL}/token', ZENDESK_API_TOKEN)
)
return response.json()
Step 6: Complete Argument Mappings
Ensure all SWAIG function arguments map correctly to Zendesk API parameters.
Example: create_ticket Argument Mapping
-
SWAIG Function Arguments:
subjectcomment_bodyrequester_namerequester_emailpriority
-
Zendesk API Payload:
{
"ticket": {
"subject": "subject",
"comment": {
"body": "comment_body"
},
"requester": {
"name": "requester_name",
"email": "requester_email"
},
"priority": "priority"
}
}
Example: update_ticket Argument Mapping
-
SWAIG Function Arguments:
ticket_idstatusprioritycomment_bodypublic
-
Zendesk API Payload:
{
"ticket": {
"status": "status",
"priority": "priority",
"comment": {
"body": "comment_body",
"public": "public"
}
}
}
Example: close_ticket Argument Mapping
-
SWAIG Function Arguments:
ticket_id
-
Zendesk API Payload:
{
"ticket": {
"status": "closed"
}
}
Example: add_comment Argument Mapping
-
SWAIG Function Arguments:
ticket_idcomment_bodypublic
-
Zendesk API Payload:
{
"ticket": {
"comment": {
"body": "comment_body",
"public": "public"
}
}
}
Example: get_ticket Argument Mapping
-
SWAIG Function Arguments:
ticket_id
-
Zendesk API Endpoint:
GET /api/v2/tickets/{ticket_id}.json
Example: verify_support_pin Argument Mapping
-
SWAIG Function Arguments:
caller_phone_numberentered_pin
-
Zendesk API Endpoint:
GET /api/v2/search.json
Example: get_current_user_tickets Argument Mapping
-
SWAIG Function Arguments:
caller_phone_numberstatuspriority
-
Zendesk API Endpoint:
GET /api/v2/users/{user_id}/tickets/requested.json
How it works
PIN Authentication
SWAIG verifies the caller's support PIN before executing any ticket operations.
Example Workflow:
- User Calls Support.
- AI Agent Prompts for PIN.
- User Enters PIN.
- SWAIG Calls the
verify_support_pinfunction.- If verification succeeds, proceed.
- If verification fails, handle accordingly.
- Execute Requested SWAIG Function.
SWAIG functions
SignalWire AI Gateway (SWAIG) functions are defined by the SWAIG method within the SWML script. Each SWAIG function maps to a Zendesk API endpoint.
| Function name | Description | Code |
|---|---|---|
create_ticket | Opens a new support ticket in Zendesk using the POST /api/v2/tickets.json Zendesk API endpoint. |
create_ticket
Opens a new support ticket in Zendesk using the POST /api/v2/tickets.json Zendesk API endpoint.
create_ticketPOST /api/v2/tickets.json Zendesk API endpoint.{
"name": "create_ticket",
"description": "Creates a new support ticket in Zendesk.",
"parameters": {
"type": "object",
"properties": {
"subject": {
"type": "string",
"description": "The subject or title of the ticket."
},
"comment_body": {
"type": "string",
"description": "The initial comment or description of the ticket."
},
"requester_name": {
"type": "string",
"description": "Name of the requester."
},
"requester_email": {
"type": "string",
"description": "Email of the requester."
},
"priority": {
"type": "string",
"description": "Priority level of the ticket.",
"enum": ["low", "normal", "high", "urgent"],
"nullable": true
}
},
"required": ["subject", "comment_body", "requester_name", "requester_email"]
}
}
update_ticket
Updates an existing support ticket in Zendesk using the PUT /api/v2/tickets/{ticket_id}.json Zendesk API endpoint.
update_ticketPUT /api/v2/tickets/{ticket_id}.json Zendesk API endpoint.{
"name": "update_ticket",
"description": "Updates an existing support ticket in Zendesk.",
"parameters": {
"type": "object",
"properties": {
"ticket_id": {
"type": "integer",
"description": "The ID of the ticket to update."
},
"status": {
"type": "string",
"description": "The status to update the ticket to.",
"enum": ["new", "open", "pending", "hold", "solved", "closed"],
"nullable": true
},
"priority": {
"type": "string",
"description": "Priority level of the ticket.",
"enum": ["low", "normal", "high", "urgent"],
"nullable": true
},
"comment_body": {
"type": "string",
"description": "A comment to add to the ticket.",
"nullable": true
},
"public": {
"type": "boolean",
"description": "Whether the comment is public (visible to the requester).",
"default": true,
"nullable": true
}
},
"required": ["ticket_id"]
}
}
close_ticket
Closes a support ticket in Zendesk by updating its status to "closed" using the PUT /api/v2/tickets/{ticket_id}.json Zendesk API endpoint.
close_ticketPUT /api/v2/tickets/{ticket_id}.json Zendesk API endpoint.{
"name": "close_ticket",
"description": "Closes a support ticket in Zendesk by setting its status to 'closed'.",
"parameters": {
"type": "object",
"properties": {
"ticket_id": {
"type": "integer",
"description": "The ID of the ticket to close."
}
},
"required": ["ticket_id"]
}
}
add_comment
Adds a comment to an existing support ticket in Zendesk using the PUT /api/v2/tickets/{ticket_id}.json Zendesk API endpoint.
add_commentPUT /api/v2/tickets/{ticket_id}.json Zendesk API endpoint.{
"name": "add_comment",
"description": "Adds a comment to an existing support ticket in Zendesk.",
"parameters": {
"type": "object",
"properties": {
"ticket_id": {
"type": "integer",
"description": "The ID of the ticket to add a comment to."
},
"public": {
"type": "boolean",
"description": "Whether the comment is public (visible to the requester).",
"default": true,
"nullable": true
}
},
"required": ["ticket_id"]
}
}
get_ticket
Retrieves information about a specific ticket in Zendesk using the GET /api/v2/tickets/{ticket_id}.json Zendesk API endpoint.
get_ticketGET /api/v2/tickets/{ticket_id}.json Zendesk API endpoint.{
"name": "get_ticket",
"description": "Retrieves information about a specific ticket in Zendesk.",
"parameters": {
"type": "object",
"properties": {
"ticket_id": {
"type": "integer",
"description": "The ID of the ticket to retrieve."
}
},
"required": ["ticket_id"]
}
}
verify_support_pin
Verifies the caller's support PIN against the stored PIN in Zendesk using the GET /api/v2/search.json Zendesk API endpoint.
verify_support_pinGET /api/v2/search.json Zendesk API endpoint.{
"name": "verify_support_pin",
"description": "Verifies the caller's support PIN against the stored PIN in Zendesk.",
"parameters": {
"type": "object",
"properties": {
"caller_phone_number": {
"type": "string",
"description": "The caller's phone number."
},
"entered_pin": {
"type": "integer",
"description": "The support PIN entered by the caller."
}
},
"required": ["caller_phone_number", "entered_pin"]
}
}
get_current_user_tickets
Retrieves ticket numbers for the authenticated user using the GET /api/v2/users/{user_id}/tickets/requested.json Zendesk API endpoint.
get_current_user_ticketsGET /api/v2/users/{user_id}/tickets/requested.json Zendesk API endpoint.{
"name": "get_current_user_tickets",
"description": "Retrieves ticket numbers for the authenticated user.",
"parameters": {
"type": "object",
"properties": {
"caller_phone_number": {
"type": "string",
"description": "The caller's phone number."
},
"status": {
"type": "string",
"description": "Filter tickets by status.",
"enum": ["new", "open", "pending", "hold", "solved", "closed"],
"nullable": true
},
"priority": {
"type": "string",
"description": "Filter tickets by priority.",
"enum": ["low", "normal", "high", "urgent"],
"nullable": true
}
},
"required": ["caller_phone_number"]
}
}
Python functions
create_ticket
Creates a new support ticket in Zendesk using the POST /api/v2/tickets.json Zendesk API endpoint.
create_ticketPOST /api/v2/tickets.json Zendesk API endpoint.import os
import requests
from requests.auth import HTTPBasicAuth
# Retrieve environment variables
ZENDESK_SUBDOMAIN = os.getenv('ZENDESK_SUBDOMAIN')
ZENDESK_EMAIL = os.getenv('ZENDESK_EMAIL')
ZENDESK_API_TOKEN = os.getenv('ZENDESK_API_TOKEN')
def create_ticket(
subject,
comment_body,
requester_name=None,
requester_email=None,
priority=None,
):
url = f'https://{ZENDESK_SUBDOMAIN}.zendesk.com/api/v2/tickets.json'
ticket_data = {
"ticket": {
"subject": subject,
"comment": {
"body": comment_body
}
}
}
if requester_name or requester_email:
ticket_data["ticket"]["requester"] = {}
if requester_name:
ticket_data["ticket"]["requester"]["name"] = requester_name
if requester_email:
ticket_data["ticket"]["requester"]["email"] = requester_email
if priority:
ticket_data["ticket"]["priority"] = priority
response = requests.post(
url,
json=ticket_data,
auth=HTTPBasicAuth(f'{ZENDESK_EMAIL}/token', ZENDESK_API_TOKEN)
)
return response.json()
update_ticket
Updates an existing support ticket in Zendesk using the PUT /api/v2/tickets/{ticket_id}.json Zendesk API endpoint.
update_ticketPUT /api/v2/tickets/{ticket_id}.json Zendesk API endpoint.def update_ticket(
ticket_id,
status=None,
priority=None,
comment_body=None,
public=True,
):
url = f'https://{ZENDESK_SUBDOMAIN}.zendesk.com/api/v2/tickets/{ticket_id}.json'
ticket_data = {"ticket": {}}
if status:
ticket_data["ticket"]["status"] = status
if priority:
ticket_data["ticket"]["priority"] = priority
if comment_body:
ticket_data["ticket"]["comment"] = {
"body": comment_body,
"public": public
}
response = requests.put(
url,
json=ticket_data,
auth=HTTPBasicAuth(f'{ZENDESK_EMAIL}/token', ZENDESK_API_TOKEN)
)
return response.json()
close_ticket
Closes a support ticket in Zendesk by using update_ticket to set the status to "closed".
close_ticketupdate_ticket to set the status to "closed".def close_ticket(ticket_id, comment_body=None, public=True):
return update_ticket(
ticket_id,
status='closed',
comment_body=comment_body,
public=public
)
add_comment
Adds a comment to an existing support ticket by using update_ticket.
add_commentupdate_ticket.def add_comment(ticket_id, comment_body, public=True):
return update_ticket(
ticket_id,
comment_body=comment_body,
public=public
)
get_ticket
Retrieves information about a specific ticket in Zendesk using the GET /api/v2/tickets/{ticket_id}.json Zendesk API endpoint.
get_ticketGET /api/v2/tickets/{ticket_id}.json Zendesk API endpoint.def get_ticket(ticket_id):
url = f'https://{ZENDESK_SUBDOMAIN}.zendesk.com/api/v2/tickets/{ticket_id}.json'
response = requests.get(
url,
auth=HTTPBasicAuth(f'{ZENDESK_EMAIL}/token', ZENDESK_API_TOKEN)
)
return response.json()
verify_support_pin
Verifies the caller's support PIN against the stored PIN in Zendesk using the GET /api/v2/search.json Zendesk API endpoint.
verify_support_pinGET /api/v2/search.json Zendesk API endpoint.def verify_support_pin(caller_phone_number, entered_pin):
user = find_user_by_phone(caller_phone_number)
if user:
stored_pin = user.get('user_fields', {}).get('support_pin')
if stored_pin == entered_pin:
return True
return False
def find_user_by_phone(caller_phone_number):
url = f'https://{ZENDESK_SUBDOMAIN}.zendesk.com/api/v2/search.json'
params = {
'query': f'type:user phone:"{caller_phone_number}"'
}
response = requests.get(
url,
params=params,
auth=HTTPBasicAuth(f'{ZENDESK_EMAIL}/token', ZENDESK_API_TOKEN)
)
results = response.json().get('results', [])
return results[0] if results else None
get_current_user_tickets
Retrieves ticket numbers for the authenticated user using the GET /api/v2/users/{user_id}/tickets/requested.json Zendesk API endpoint.
get_current_user_ticketsGET /api/v2/users/{user_id}/tickets/requested.json Zendesk API endpoint.def get_current_user_tickets(caller_phone_number, status=None, priority=None):
user = find_user_by_phone(caller_phone_number)
if user:
user_id = user['id']
tickets = list_user_tickets(user_id, status=status, priority=priority)
if tickets:
ticket_numbers = [ticket['id'] for ticket in tickets.get('tickets', [])]
return {
'ticket_numbers': ticket_numbers,
'tickets': tickets.get('tickets', [])
}
else:
return {
'message': "No tickets found for your account."
}
else:
return {
'message': "User not found."
}
Recommendations
If implementing this in a production environment, consider the following recommendations:
- Error handling: Implement error handling to manage and log errors appropriately.
- Authentication Failures: Handle incorrect PIN entries gracefully.
- Store Credentials Securely: Use environment variables or a secrets manager.
- Encrypt Sensitive Data: Consider encrypting support PINs.
- Compliance: Adhere to GDPR and other data protection regulations.
- Logging: Avoid logging sensitive information.
Conclusion
The final product of this example is an AI Agent that interacts with Zendesk through SWAIG (the SignalWire AI Gateway). This empowers users with the ability to securely manage support tickets in a phone call with conversational AI.