The Zendesk Help Center already provides end users with a web form to submit support requests. Still, you might want your users to submit tickets from your website or web application rather than having them jump out to the Help Center site. This article describes how to build a custom ticket form that lets users submit support requests from your website.

The plan is to build a ticket form that lets end users submit tickets from one or more pages on your website. You can break down the project into three parts:

  • Build an HTML form that lets end users submit tickets
  • Develop a component that creates tickets using the Zendesk API
  • Integrate the ticket form in one or more of your website's pages

Note: An alternative to building a custom ticket form is to use the Zendesk Support Web Widget. See Using Web Widget to embed customer service in your website.

The tutorial uses Python as the web programming language. Python is a powerful but beginner-friendly language with a clear and readable syntax. If you work in another language, you should still be able to follow the code logic and adapt it to your application. The logic is similar in all languages.

Disclaimer: Zendesk provides this article for instructional purposes only. Zendesk does not support or guarantee the code. Zendesk also can't provide support for third-party technologies such as Python and the Bottle framework. Please post any issue in the comments section or search for a solution online.

Choosing the right API

You can use one of the following APIs to create tickets:

The main difference between the two is that the Requests API can be used by authenticated end users while the Tickets API can only be used by authenticated agents.

In other words, the Requests API is designed for building externally facing apps for end users. The Tickets API is designed for building internal apps for your support team.

In this tutorial, you'll build a ticket form for an externally facing web page so you'll use the Requests API.

Create the ticket form

Your ticket form should perform the following tasks:

  • Let the user enter a subject and description of the problem

  • Ask for the user's email address so your support team can reply with a solution to their problem

  • Submit the form data to a web application that you'll build later to create tickets with the Zendesk API

Another important consideration is protecting your form from attack. The topic is outside the scope of this tutorial but one option is to use CAPTCHA. For an example, see Google reCAPTCHA.

Let the user describe the problem

This isn't a web design tutorial so we'll keep the form simple. You can customize it as much as you want later.

  1. If you don't already have one, create a project folder named ticket_project or something along those lines.

  2. In a text editor, create a text file named ticket_form.html and save it in the new folder.

  3. Paste the following HTML in the file and save it:

    <link rel="stylesheet" href="form_styles.css"><div id="ticket_form"><div class="title">Submit a ticket</div><form method="post"><ul>  <li><input type="text" placeholder="ticket subject"    name="subject" class="field" required></li>
      <li><textarea placeholder="what's the problem?"    name="description" rows="6" class="field" required></textarea></li>
      <li><input type="submit" value="Submit"></li></ul></form></div>

    The HTML creates a form containing a standard text box for entering the ticket subject, a larger text box for entering a problem description, and a Submit button.

    The API requires both a subject and description to create a ticket, so the subject and description form fields have a required attribute to help validate the form. If a user leaves one field empty, they'll be prompted to enter a value when they try to submit the form.

    To save space, the form uses placeholder text to label the fields. Placeholder text is displayed inside the empty fields and disappears once the user starts typing in the field.

    The form's look and feel is defined by the linked CSS file named form_styles.css, which you'll create next.

  4. Create a file named form_styles.css and save it in the project folder.

  5. Paste the following CSS rules in the file and save it:

    #ticket_form {    font-family: Verdana, Helvetica, sans-serif;    font-size: 10pt;    color: dimgray;    width: 240px;    border: 1px solid lightgray;    padding: 0 8px;}
    .title {    font-family: Arial, Helvetica, sans-serif;    font-weight: bold;    padding: 12px 0 0;}
    form ul {    list-style-type: none;    padding-left: 0;}
    form li {    margin: 0 0 6px 0;}
    textarea {    vertical-align: top;    border-color: lightgray;}
    .field {    width: 100%;}
  6. Open the ticket_form.html file in a browser to review the design.

Keep tweaking the CSS if you want.

Ask for the user's email address

You need the user's email address so that your support team can contact the person with a solution to their problem. You also need it to authenticate calls to the Requests API.

Important: The email has to be verified to reduce the potential for spam tickets and ensure users are who they say they are. See Verifying a user's email address in the Support Help Center.

In this tutorial, you direct new users to your Zendesk Support registration page. If you have a closed Zendesk Support instance, such as with a company's IT department, you can import the users instead. See Bulk importing users and organizations.

The end user only needs to enter their email address once. After confirming that the email is registered in Zendesk Support, you can save it in a cookie so the user doesn't have to enter it again.

To ask for the user's email address

  1. In ticket_form.html, insert the following email input field after the textarea field:

    <li><input type="email" placeholder="your email address"       name="email" class="field" required></li>

    The email input type provides basic validation depending on the browser. For example, if the email address doesn't have a @ character, the user may be prompted to fix the problem when they try to submit the form.

  2. Immediately before the field's closing </li> tag, insert the following div asking new users to register:

    <div class="register"><a href="https://your_subdomain.zendesk.com/auth/v2/login/registration" target="_blank">Register</a> so our support team can email you to solve your problem.</div>...

    When you're done, the list item should look like this:

    <li><input type="email" placeholder="your email address" name="email" class="field" required><div class="register"><a href="https://your_subdomain.zendesk.com/auth/v2/login/registration" target="_blank">Register</a> so our support team can email you to solve your problem.</div></li>
  3. Replace your_subdomain with your Zendesk Support subdomain.

    The URL takes users to the standard registration page in your Help Center.

  4. Switch to form_styles.css and add the following CSS rule:

    .register {    font-size: 8pt;    color: gray;    padding: 8px 0;}
  5. Save the files.

  6. Open the ticket_form.html file in a browser.

Submit the form data

When the user clicks the Submit button, the form should send the data to a script that can create a ticket in Zendesk Support with it.

To submit the form data

  1. In ticket_form.html, add the following action attribute to the form tag:

    <form action="create_ticket" method="post">

    The attribute instructs the form to submit the data to a resource named create_ticket, which you'll build later.

  2. Save the file.

The completed form should look as follows:

<link rel="stylesheet" href="form_styles.css"><div id="ticket_form"><div class="title">Submit a ticket</div><form action="create_ticket" method="post"><ul>  <li><input type="text" placeholder="ticket subject"    name="subject" class="field" required></li>
  <li><textarea placeholder="what's the problem?"    name="description" rows="6" class="field" required></textarea></li>
  <li><input type="email" placeholder="your email address" name="email"    class="field" required>    <div class="register"><a href="https://your_subdomain.zendesk.com/auth/v2/login/registration" target="_blank">Register</a> so our support team can email you to solve your problem.</div>  </li>
  <li><input type="submit" value="Submit"></li></ul></form></div>

Create the tickets

After completing the form, you need a separate component to perform the following tasks:

  • Create a ticket in Zendesk Support after the end user submits the ticket form

  • Notify the end user if something went wrong with the request

  • Reuse the end user's email address to authenticate future requests

A server-side web application is required to perform these tasks. Security features built into browsers prevent client-side JavaScript from accessing the Zendesk API.

To keep things simple, the tutorial shows you how to build the web application using a micro web framework called Bottle. The framework helps keep technical details to a minimum so you can focus on the big picture. You only need about 40 lines of code (not counting comments and blank lines) to build a Bottle application that can do the three jobs above.

The first step is to set up your development environment, as described next.

Set up your dev environment

Before you start, you'll need a command-line interface like the command prompt in Windows or Terminal on the Mac.

To set up your development environment

  1. Get version 3 of Python if you don't already have it. To download and install it, see http://www.python.org/download/.

  2. If you have Python 3.3 or earlier, download and install pip if you don't already have it. pip is a simple tool for installing and managing Python packages. See these instructions.

    Note: You already have pip if you have Python 3.4 or later.
  3. Install the Requests library, which makes HTTP requests easy. Use the following pip command in your command-line interface to download and install it:

    $ pip3 install requests
    Note: The dollar sign ($) represents the command prompt. Don't enter it.
  4. Install Bottle, the micro web framework mentioned in the previous section. Use the following pip command to download and install it:

    $ pip3 install bottle

    The Bottle framework includes a built-in development server that you can use to test your changes locally.

Python notes

Indentation and line breaks matter in Python. When copying the sample Python code in this section, ignore unintended line wraps caused by the right margin. Also make sure to indent lines with four spaces (not tabs) exactly as shown.

Set up the Bottle application

A basic Bottle app has the following folder structure:

/web_app    app.py    /static    /views

The app.py file is the web app's nerve center. It takes incoming HTTP requests, performs some work on them, and sends back HTML and other information in HTTP responses.

The static folder is for files such as images, CSS stylesheets, and JavaScript files. The views folder is for templates.

To set up your Bottle application

  1. In a text editor, create a plain text file and save it as app.py in your project folder.

  2. Create the following folders in the project folder:

    • static/css
    • views
  3. Move your ticket_form.html file into the views folder.

  4. Change the file's extension from ticket_form.html to ticket_form.tpl.

    tpl stands for template. The framework renders templates into HTML to include in outgoing HTTP responses.

  5. Move your form_styles.css file to its new home in the static/css folder.

Your project folder structure should now look like this:

/ticket_project    app.py    /static        /css            form_styles.css    /views        ticket_form.tpl

Create a route for your form

Later in the tutorial, you'll embed the ticket form in pages on your website. When an end user opens one of the pages, the browser will request your ticket form. You must create a route in your Bottle application to send the form in response to these browser requests.

  1. Open the app.py file in a text editor and add the following route to the file:

    @route('/create_ticket')def handle_form():    return template('ticket_form')

    A route maps an incoming HTTP request to a Python function. The function works on the request and returns a result, if any, in the outgoing HTTP response.

    In this case, the route maps requests for the create_ticket resource to the handle_form() function. The handle_form() function returns the results of the Bottle framework's template() function in the HTTP response:

    return template('ticket_form')

    The template() function renders your ticket_form.tpl template as HTML. It only contains HTML right now but you'll soon add non-HTML code to show and hide the email textbox at render time.

  2. Import the framework's route() and template() functions by adding the following line at the top of the file:

    from bottle import route, template

    You need to import the functions from the Bottle framework to use them in your script. The route() function is called a decorator function because it decorates -- or extends -- the handle_form() function in some way. In this case, it provides your custom handle_form() function with the ability to work with HTTP requests and responses.

  3. Save the app.py file.

Test and debug

You can test to make sure the application serves the ticket_form template in response to a browser request. The Bottle framework provides a local application server you can use to run the web application on your computer.

  1. In your app.py file, add a space and the following line after the route:

    run(host='localhost', port=8080, debug=True)
  2. Add the run() function to the bottle import statement at the top of the file:

    from bottle import route, template, run
  3. Save the file.

  4. In a command-line interface like Terminal on the Mac or the command prompt in Windows, navigate to your project folder and enter the following command to start a local app server:

    $ python3 app.py

    After a moment, Bottle's built-in local server starts. You'll see something like this:

  5. In your browser, enter the following URL and press enter:

    http://localhost:8080/create_ticket

    The ticket form should open in the browser:

Oops, the CSS didn't load. After taking a closer look, you discover that the page HTML tells the browser to get the CSS file in the root folder (href="form_styles.css"), but the file isn't there anymore. You moved it to the static/css folder.

To fix the bug:

  1. In ticket_form.tpl, include the css folder in the stylesheet link:

    <link rel="stylesheet" href="/css/form_styles.css">

    Don't include the static folder. You'll see why next.

  2. In app.py, add a blank line and the following route after the create_ticket route:

    @route('/css/<filename>')def send_css(filename):    return static_file(filename, root='static/css')

    <filename> is a wildcard that lets the route handle requests for any file in the static folder. This feature is especially useful for sites with lots of graphics. It saves you from having to create a route for every image link.

    The static_file() function serves files from the app's static folder. You don't have to specify the folder in your HTML links or route URLs.

  3. Add the static_file() function to the bottle import statement at the top of the file:

    from bottle import route, template, run, static_file
  4. Save the files.

You're ready to run the test again. Before you do, restart the server. If you make changes to the app.py file while the local server is running, the changes won't take effect until you restart the server. You don't have to restart if you change a static file like a template or a CSS stylesheet.

  1. Switch to your command-line tool and press Control+C to stop the server.

  2. Restart it:

    $ python3 app.py
  3. Reload http://localhost:8080/create_ticket in your browser.

    You should get the styled form this time:

Pause to reflect that you created a route-based website with 8 lines of code:

from bottle import route, template, run, static_file
@route('/create_ticket')def handle_form():    return template('ticket_form')
@route('/css/<filename>')def send_css(filename):    return static_file(filename, root='static/css')
run(host='localhost', port=8080, debug=True)

Receive the form data

Earlier in the tutorial, you added the attribute action="create_ticket" to the ticket form:

<form action="create_ticket" method="post">

The attribute instructs the form to send the form data to the create_ticket resource on the server when the user clicks Submit. Accordingly, your web application needs to capture the incoming POST request.

Notice that you already created a route named create_data to handle browser requests for the form. You'll use the same route to handle submitted form data. A common framework pattern for web forms is to submit the data to the same route used to request the form. The result of both requests is to display the form. This kind of dual-purpose route is typically structured as follows (in pseudo-code):

if the request is a POST request:    process the form dataelse the request is a GET request:    do something, if anything, to prepare the form before sending itsend the form

Modify the create_ticket route to adopt this pattern.

  1. In app.py, add a method=['GET', 'POST'] argument to the route signature:

    @route('/create_ticket', method=['GET', 'POST'])def handle_form():    # function code...

    Bottle routes accept GET requests by default. If you want the route to accept POST requests too, you have to specify both HTTP methods.

  2. Add the following if block before the return statement:

    @route('/create_ticket', method=['GET', 'POST'])def handle_form():    if request.POST:        # Get the form data        # Package the data for the API        # Make the API request    return template('ticket_form')

    This creates a skeleton for handling the incoming POST request. The block lists the tasks it needs to perform but doesn't do very much yet. Start by getting the form data as follows.

  3. Add the following statements after and aligned with the comment # Get the form data:

    # Get the form data    subject = request.forms.get('subject')    description = request.forms.get('description')    email = request.forms.get('email')

    The route retrieves the form values from the Bottle request object, which represents the incoming HTTP request and its contents.

  4. Add the request object to the bottle import statement at the top of the file:

    from bottle import route, template, run, static_file, request

Package the ticket data for the API

The next step is to package the ticket data for the API. The Requests API docs specifies that your data should be JSON formatted and structured as follows:

{"request": {"subject": "Help!", "comment": {"body": "My printer is on fire!"}}}

It's best practice to package the data in an equivalent key/value data type in your programming language of choice, then convert it to JSON. The key/value data type in Python is called a dictionary. It's called a hash in Ruby and Perl, an associative array in PHP, and an object literal in JavaScript.

  1. In app.py, add the following statements after and aligned with # Package the data for the API:

    # Package the data for the API    data = {'request': {'subject': subject, 'comment': {'body': description}}}    ticket = json.dumps(data)

    The first statement after the comment inserts the subject and description variables into a Python dictionary that matches the structure expected by the API. The second statement uses the json.dumps() function to convert the dictionary to JSON.

  2. Import the json module on its own line at the top of the file:

    import json

Make the API request

The next step is to make the API request with your freshly encoded JSON data. First, you need to set the authentication credentials. As mentioned in Choosing the right API above, the credentials for the Requests API consist of a user's verified email and an API token.

  1. Get an API token by signing in to Admin Center as an admin and going to Apps and integrations > APIs > Zendesk API. Copy an existing token or create one specifically for the ticket form.

  2. In app.py, add the following code block after and aligned with # Make the API request:

    # Make the API request    user = email + '/token'    api_token = 'your_api_token'    url = 'https://your_subdomain.zendesk.com/api/v2/requests.json'    headers = {'content-type': 'application/json'}    r = requests.post(        url,        data=ticket,        auth=(user, api_token),        headers=headers    )

    Replace your_api_token and your_subdomain with your information.

    The first two lines after the comment assign the authentication credentials to variables. The API docs specify that you need to append the string '/token' to the email address when authenticating with an API token.

    The next line creates a variable that points to the API resource for creating tickets. The URL is specified in the API docs.

    The next line creates a variable that contains a required HTTP header. The API docs specify that POST and PUT requests must include a Content-Type: application/json header.

    The last statement uses the URL, ticket data, credentials, and header to make the request.

    The response from the API is assigned to the r (for response) variable. In the next step, the route checks r to make sure the request was successful.

  3. Add the following code block to test the result:

    if r.status_code != 201:    if r.status_code == 401 or 422:        status = 'Could not authenticate you. Check your email address or register.'    else:        status = 'Problem with the request. Status ' + str(r.status_code)    return status

    The API returns a 201 status code when a ticket is created successfully. If the response returns anything but a status code of 201, then an error occurred.

    If the API returns a 401 or 422 code, then there was an authentication problem. It probably means the user entered an unverified email or the email contained a typo. The route returns an error message to the user with information on how to fix it.

    If there was any other kind of error, the route returns a generic error message with the status code as a hint.

    In both cases, the error message is returned in the HTTP response as plain text with no HTML formatting:

    return status
  4. Import the requests library on its own line at the top of the file:

    import requests

    You installed the requests library in Set up your dev environment above.

  5. Save the file.

The route should look like this:

@route('/create_ticket', method=['GET', 'POST'])def handle_form():    if request.POST:        # Get the form data        subject = request.forms.get('subject')        description = request.forms.get('description')        email = request.forms.get('email')
        # Package the data for the API        data = {'request': {'subject': subject, 'comment': {'body': description}}}        ticket = json.dumps(data)
        # Make the API request        user = email + '/token'        api_token = 'your_api_token'        url = 'https://your_subdomain.zendesk.com/api/v2/requests.json'        headers = {'content-type': 'application/json'}        r = requests.post(            url,            data=ticket,            auth=(user, api_token),            headers=headers        )        if r.status_code != 201:            if r.status_code == 401 or 422:                status = 'Could not authenticate you. Check your email address or register.'            else:                status = 'Problem with the request. Status ' + str(r.status_code)            return status
    return template('ticket_form')
Test and debug

Time to check if the route works by creating a few test tickets.

  1. Switch to your command-line tool and press Control+C to stop the server if it's still running.

  2. Restart it:

    $ python3 app.py
  3. Reload http://localhost:8080/create_ticket.

  4. Enter a subject, description, and the email address of a verified end user, then click Submit.

  5. Check your Zendesk Support instance for the new ticket. The best place to look is in the "Unassigned tickets" view in your Views (Admin > Manage > Views).

  6. Try creating a ticket with an unverified email address. Do you get the expected error message?

Minimum viable product

At this point you could pack it up and consider the job done. The ticket form gives you the basic functionality. It creates a ticket in Zendesk Support. If there's a problem creating the ticket, it shows the user an error message.

After trying it a few times, you probably noticed it could still be improved in two important ways:

  • It should provide better feedback to the user
  • It should save the user's email address so they don't have to enter it again

Enhancement: Provide better feedback to the user

After creating a ticket, the user gets no notification except for maybe getting an email notification from a trigger in Zendesk Support. The user gets no feedback directly from the ticket form. If there's an error creating the ticket, the error message is displayed as plain text in a blank window.

You can provide the feedback directly in the ticket form. As the definition of the word template implies, the Bottle framework lets you change portions of a template each time it's rendered. You can pass the feedback to the template at render-time to display it in the form for the user.

  1. In app.py, delete the return status statement.

    You'll pass the error message, if any, to the form itself.

  2. On the following line and one indent to the left, add an else clause to close the top-level if clause:

    else:    status = 'Ticket was created. Look for an email notification.'

    If the response didn't return anything else but a status code of 201, then the ticket was created successfully. The code saves the feedback in the same status variable used for the error messages. You pass the variable to the form in the next step.

  3. Add a feedback parameter to the template() function and assign it the value of your status variable, as follows:

    return template('ticket_form', feedback=status)

    The expression passes the feedback parameter to the ticket_form template.

    After making the changes, the last lines of your route should look as follows:

    if r.status_code != 201:        if r.status_code == 401 or 422:            status = 'Could not authenticate you. Check your email address or register.'        else:            status = 'Problem with the request. Status ' + str(r.status_code)    else:        status = 'Ticket was created. Look for an email notification.'
    return template('ticket_form', feedback=status)
  4. Switch to your ticket_form.tpl template and add the following lines after the title:

    <div class='title">Submit a ticket</div>% if feedback:    <div class="feedback">{{feedback}}</div>% end

    If the feedback parameter is true (if it contains any non-empty string), then the value of the parameter is interpolated, or inserted, in the template. Otherwise the instruction is ignored.

  5. Switch to your form_styles.css stylesheet and add a CSS rule to format the feedback:

    .feedback {    font-size: 8pt;    padding: 8px 0 0;}
  6. Save the files.

Test and debug

Check to see if the feedback works by creating a few test tickets.

  1. Switch to your command-line tool and press Control+C to stop the server if it's still running.

  2. Restart it:

    $ python3 app.py
  3. Reload http://localhost:8080/create_ticket.

Whoa -- server error "local variable 'status' referenced before assignment." Can you figure out what happened and fix it?

Answer: The status variable is never defined the first time the form is requested. The route receives a GET request so it skips the POST block where the variable is defined. The return statement then tries to assign the nonexistent variable to the feedback parameter.

To fix the bug:

  • Initialize the status variable on the first line of the handle_form() function, before the POST block:

    def handle_form():    status = ''    # rest of code

    The string is empty because you don't want to display any status message when the form loads for the first time. An empty string is considered "falsy" in Python, which ensures the feedback block in the template is skipped.

Restart the Bottle server and resume testing:

  1. Reload http://localhost:8080/create_ticket.

  2. Enter a subject, description, and the email address of a verified end user, then click Submit. Does the ticket form display a success message?

  3. Try creating a ticket with an unverified email address. Does the ticket form display the expected error message?

Enchancement: Reuse the user's email address

The ticket form currently asks the user to enter their email every time they submit a ticket. A better solution is to store the user's email after the first ticket so they don't have to enter it again.

You can store the email in a cookie on the user's computer and then read the cookie when you need to authenticate the API request. You can also hide the email form field from the user because they don't need it anymore.

The logic is as follows:

  • Check to see if the end user's email address was previously stored in a cookie and, if so, use it in the new request and hide the email form field

  • If the cookie doesn't already exist, set it when a ticket is created

To reuse the user's email address

  1. In the ticket_form.tpl template, enclose the list item (<li>) containing the email field and registration message in the following if clause:

    % if no_email:    <li>...</li>% end

    The list item is only rendered if the no_email parameter is true (if a cookie with a valid email wasn't found). If the email was found, the list item isn't rendered.

  2. Pass the information to the template by switching to app.py and adding a no_email parameter to the template() function:

    return template('ticket_form', feedback=status, no_email=ask_email)

    The parameter takes the value of a variable named ask_email, which can be true or false depending on whether a cookie with the email address exists. You'll check for the cookie next.

  3. Add the following code block at the beginning of the handle_form() function:

    def handle_form():    if 'verified_email' in request.cookies:        ask_email = False    else:        ask_email = True

    The code checks for a cookie named verified_email as soon as a request for the form is received. If the cookie exists, it assigns False to ask_mail. In other words, you don't have to ask the user for their email address, you already have it. If the cookie doesn't exist, you have to ask the user for it.

  4. Assign the cookie's value to the email variable used to make the API request by replacing the following line:

    email = request.forms.get('email')

    with the following code block:

    if 'verified_email' in request.cookies:    email = request.get_cookie('verified_email')else:    email = request.forms.get('email')

    If the verified_email cookie exists, then the route gets the email address from the cookie. Otherwise, it gets it from the form field.

  5. Set the cookie the first time a ticket is created by replacing the following else statement in the API response block:

    else:    status = 'Ticket was created. Look for an email notification.'

    with the following code block:

    else:    status = 'Ticket was created. Look for an email notification.'    if 'verified_email' not in request.cookies:        response.set_cookie('verified_email', email, max_age=364*24*3600)        ask_email = False

    If the ticket was created, then the email the user supplied must be valid. The route checks to make sure the cookie wasn't previously set before setting a cookie that won't expire for one year. The route then sets ask_email to False to hide the email field in the form.

  6. Make sure to show the email form field if the user gets an authentication error:

    if r.status_code == 401 or 422:    status = 'Could not authenticate you. Check your email address or register.'    ask_email = True
  7. Add the response object you used to set the cookie to the bottle import statement at the top of the file:

    from bottle import route, template, run, static_file, request, response
  8. Save the files.

Restart the local server and test your changes.

Code complete

To download the complete Bottle application, including the form template and CSS, click ticket_project.zip.

Your completed app.py file should look as follows. Make sure to replace your_api_token and your_subdomain with your values.

import json
import requestsfrom bottle import route, template, run, static_file, request, response

@route('/create_ticket', method=['GET', 'POST'])def handle_form():    if 'verified_email' in request.cookies:        ask_email = False    else:        ask_email = True    status = ''
    if request.POST:        # Get the form data        subject = request.forms.get('subject')        description = request.forms.get('description')        if 'verified_email' in request.cookies:            email = request.get_cookie('verified_email')        else:            email = request.forms.get('email')
        # Package the data for the API        data = {'request': {'subject': subject, 'comment': {'body': description}}}        ticket = json.dumps(data)
        # Make the API request        user = email + '/token'        api_token = 'your_api_token'        url = 'https://your_subdomain.zendesk.com/api/v2/requests.json'        headers = {'content-type': 'application/json'}        r = requests.post(url, data=ticket, auth=(user, api_token), headers=headers)        if r.status_code != 201:            if r.status_code == 401 or 422:                status = 'Could not authenticate you. Check your email address or register.'                ask_email = True            else:                status = 'Problem with the request. Status ' + str(r.status_code)        else:            status = 'Ticket was created. Look for an email notification.'            if 'verified_email' not in request.cookies:                response.set_cookie('verified_email', email, max_age=364*24*3600)                ask_email = False
    return template('ticket_form', feedback=status, no_email=ask_email)
@route('/css/<filename>')def send_css(filename):    return static_file(filename, root='static/css')
run(host='localhost', port=8080, debug=True)

Integrate the ticket form

The last step is to integrate the custom ticket form into one or more of your website's pages. You can use an iframe to do the job. An iframe works like the picture-in-picture feature of some TVs, except it displays a web page within a web page.

  1. Deploy your Bottle application to a web server so your web pages have access to it. If you don't have access to a server at work, you can use a PaaS (platform as a service) option like Heroku. Depending on your volume of tickets, you might be able to deploy it at little or no cost. For step-by-step instructions, see Deploying a Bottle web app on Heroku on my Github page.

  2. Work with your design team to find the best solution for integrating the ticket form in the design of your website.

  3. Embed the ticket form in the page HTML with the following iframe tag:

    <iframe id="ticketform" src="https://your_app_host_domain/create_ticket"></iframe>

    Replace your_app_host_domain with your information.

  4. Add the following rule to the site's stylesheet:

    iframe#ticketform {    width: 274px;    height: 306px;    border: none;}

    Modify the CSS to your liking or requirements.

Whenever a user loads the page in a browser, the iframe makes a request to your web app for the ticket form and displays it in the page.