diff --git a/api/.htaccess b/api/.htaccess
index e73e2eb3b9c3f1204223b4426274817c0200e279..f460420d6d2f55f362fb420788b6751afe38bbba 100644
--- a/api/.htaccess
+++ b/api/.htaccess
@@ -1,8 +1,11 @@
-RewriteEngine On
+<IfModule mod_rewrite.c>
 
-RewriteBase /api/
+RewriteEngine On
 
 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteCond %{REQUEST_FILENAME} !-d
+RewriteCond %{REQUEST_URI} (.*/api)
+
+RewriteRule ^(.*)$ %1/http.php/$1 [L]
 
-RewriteRule ^(.*)$ http.php/$1 [L]
+</IfModule>
diff --git a/api/pipe.php b/api/pipe.php
index 29dfcff1d1aa10a20386589927830ffed8211911..699e7400001c8f497a5bbd30b5d921ca353abe2b 100644
--- a/api/pipe.php
+++ b/api/pipe.php
@@ -90,31 +90,38 @@ $var['header']=$parser->getHeader();
 $var['priorityId']=$cfg->useEmailPriority()?$parser->getPriority():0;
 
 $ticket=null;
-if(preg_match ("[[#][0-9]{1,10}]",$var['subject'],$regs)) {
+if(preg_match ("[[#][0-9]{1,10}]", $var['subject'], $regs)) {
     $extid=trim(preg_replace("/[^0-9]/", "", $regs[0]));
-    $ticket= new Ticket(Ticket::getIdByExtId($extid));
-    //Allow mismatched emails?? For now hell NO.
-    if(!is_object($ticket) || strcasecmp($ticket->getEmail(),$var['email']))
-        $ticket=null;
+    if(!($ticket=Ticket::lookupByExtId($extid, $var['email'])) || strcasecmp($ticket->getEmail(), $var['email']))
+       $ticket = null;
 }        
+
 $errors=array();
 $msgid=0;
-if(!$ticket) { //New tickets...
-    $ticket=Ticket::create($var,$errors,'email');
-    if(!is_object($ticket) || $errors) {
-        api_exit(EX_DATAERR,'Ticket create Failed '.implode("\n",$errors)."\n\n");
-    }
+if($ticket) {
+    //post message....postMessage does the cleanup.
+    if(!($msgid=$ticket->postMessage($var['message'], 'Email',$var['mid'],$var['header'])))
+        api_exit(EX_DATAERR, 'Unable to post message');
 
+} elseif(($ticket=Ticket::create($var, $errors, 'email'))) { // create new ticket.
     $msgid=$ticket->getLastMsgId();
+} else { // failure....
 
-} else {
-    //post message....postMessage does the cleanup.
-    if(!($msgid=$ticket->postMessage($var['message'], 'Email',$var['mid'],$var['header']))) {
-        api_exit(EX_DATAERR, 'Unable to post message');
+    // report success on hard rejection
+    if(isset($errors['errno']) && $errors['errno'] == 403)
+        api_exit(EX_SUCCESS);
+
+    // check if it's a bounce!
+    if($var['header'] && TicketFilter::isAutoBounce($var['header'])) {
+        $ost->logWarning('Bounced email', $var['message'], false);
+        api_exit(EX_SUCCESS); 
     }
+    
+    api_exit(EX_DATAERR, 'Ticket create Failed '.implode("\n",$errors)."\n\n");
 }
+
 //Ticket created...save attachments if enabled.
-if($cfg->allowEmailAttachments() && ($attachments=$parser->getAttachments())) {
+if($ticket && $cfg->allowEmailAttachments() && ($attachments=$parser->getAttachments())) {
     foreach($attachments as $attachment) {
         if($attachment['filename'] && $ost->isFileTypeAllowed($attachment['filename']))
             $ticket->saveAttachment(array('name' => $attachment['filename'], 'data' => $attachment['body']), $msgid, 'M');
diff --git a/include/api.ticket.php b/include/api.ticket.php
index 4cb4eb02d59a8e0e9ef283abde909cc74628d4c6..7fd5ba713c8237aad483c4a44b08989cf9b17c3f 100644
--- a/include/api.ticket.php
+++ b/include/api.ticket.php
@@ -15,7 +15,7 @@ class TicketController extends ApiController {
             "attachments" => array("*" => 
                 array("name", "type", "data", "encoding")
             ), 
-            "message", "ip"
+            "message", "ip", "priorityId"
         );
         if ($format == "xml") return array("ticket" => $supported);
         else return $supported;
@@ -45,7 +45,7 @@ class TicketController extends ApiController {
                 if (!($info["data"] = base64_decode($info["data"], true)))
                     Http::response(400, sprintf(
                         "%s: Poorly encoded base64 data",
-                        $filename));
+                        $info['name']));
             }
             $info['size'] = strlen($info['data']);
         }
diff --git a/include/class.api.php b/include/class.api.php
index 8a15c04a493c9a71cc3ae2c137ce1cc65b064fbc..15b54ebeff14eaa5240c9c81fa09436d990379c1 100644
--- a/include/class.api.php
+++ b/include/class.api.php
@@ -208,7 +208,7 @@ class ApiController {
     function validate($data, $structure, $prefix="") {
         foreach ($data as $key=>$info) {
             if (is_array($structure) and is_array($info)) {
-                $search = isset($structure[$key]) ? $key : "*"; 
+                $search = (isset($structure[$key]) && !is_numeric($key)) ? $key : "*"; 
                 if (isset($structure[$search])) {
                     $this->validate($info, $structure[$search], "$prefix$key/");
                     continue;
@@ -243,16 +243,21 @@ class ApiXmlDataParser extends XmlDataParser {
             } else if ($key == "autorespond") {
                 $value = (bool)$value;
             } else if ($key == "attachments") {
-                foreach ($value as &$info) { 
-                    $info["data"] = $info[":text"]; 
-                    unset($info[":text"]);
+                if(!isset($value['file'][':text']))
+                    $value = $value['file'];
+
+                if($value && is_array($value)) {
+                    foreach ($value as &$info) { 
+                        $info["data"] = $info[":text"]; 
+                        unset($info[":text"]);
+                    }
+                    unset($info);
                 }
-                unset($info);
-            }
-            if (is_array($value)) {
+            } else if(is_array($value)) {
                 $value = $this->fixup($value);
             }
         }
+
         return $current;
     }
 }
@@ -279,7 +284,7 @@ class ApiJsonDataParser extends JsonDataParser {
                     # PHP5: fopen("data://$data[5:]");
                     if (substr($data, 0, 5) != "data:") {
                         $info = array(
-                            "data" => $data, 
+                            "data" => $data,
                             "type" => "text/plain",
                             "name" => key($info));
                     } else {
@@ -288,11 +293,17 @@ class ApiJsonDataParser extends JsonDataParser {
                         list($type, $extra) = explode(";", $meta);
                         $info = array(
                             "data" => $contents,
-                            "type" => $type,
+                            "type" => ($type) ? $type : "text/plain",
                             "name" => key($info));
                         if (substr($extra, -6) == "base64")
                             $info["encoding"] = "base64";
-                        # TODO: Handle 'charset' hint in $extra
+                        # Handle 'charset' hint in $extra, such as
+                        # data:text/plain;charset=iso-8859-1,Blah
+                        # Convert to utf-8 since it's the encoding scheme
+                        # for the database. Otherwise, assume utf-8
+                        list($param,$charset) = explode('=', $extra);
+                        if ($param == 'charset' && function_exists('iconv'))
+                            $contents = iconv($charset, "UTF-8", $contents);
                     }
                 }
                 unset($value);
diff --git a/include/class.filter.php b/include/class.filter.php
index c7f1d70c015405a322a9bd3ed17de21d77cf4981..b6f5305959ab64c9be3df7789c7fe2cb6bd9ae2a 100644
--- a/include/class.filter.php
+++ b/include/class.filter.php
@@ -870,6 +870,10 @@ class TicketFilter {
      *    http://msdn.microsoft.com/en-us/library/ee219609(v=exchg.80).aspx
      */
     /* static */ function isAutoResponse($headers) {
+
+        if($headers && !is_array($headers))
+            $headers = Mail_Parse::splitHeaders($headers);
+
         $auto_headers = array(
             'Auto-Submitted'    => 'AUTO-REPLIED',
             'Precedence'        => array('AUTO_REPLY', 'BULK', 'JUNK', 'LIST'),
@@ -879,21 +883,56 @@ class TicketFilter {
             'X-Autoresponse'    => '',
             'X-Auto-Reply-From' => ''
         );
+
         foreach ($auto_headers as $header=>$find) {
-            if ($value = strtoupper($headers[$header])) {
-                # Search text must be found at the beginning of the header
-                # value. This is especially import for something like the
-                # subject line, where something like an autoreponse may
-                # appear somewhere else in the value.
-                if (is_array($find)) {
-                    foreach ($find as $f)
-                        if (strpos($value, $f) === 0)
-                            return true;
-                } elseif (strpos($value, $find) === 0) {
-                    return true;
-                }
+            if(!isset($headers[$header])) continue;
+
+            $value = strtoupper($headers[$header]);
+            # Search text must be found at the beginning of the header
+            # value. This is especially import for something like the
+            # subject line, where something like an autoreponse may
+            # appear somewhere else in the value.
+
+            if (is_array($find)) {
+                foreach ($find as $f)
+                    if (strpos($value, $f) === 0)
+                        return true;
+            } elseif (strpos($value, $find) === 0) {
+                return true;
             }
         }
+
+        # Bounces also counts as auto-responses.
+        if(self::isAutoBounce($headers))
+            return true;
+
+        return false;
+    }
+
+    function isAutoBounce($headers) {
+
+        if($headers && !is_array($headers))
+            $headers = Mail_Parse::splitHeaders($headers);
+
+        $bounce_headers = array(
+            'From'          => array('<MAILER-DAEMON@MAILER-DAEMON>', 'MAILER-DAEMON', '<>'),
+            'Subject'       => array('DELIVERY FAILURE', 'DELIVERY STATUS', 'UNDELIVERABLE:'),
+        );
+
+        foreach ($bounce_headers as $header => $find) {
+            if(!isset($headers[$header])) continue;
+
+            $value = strtoupper($headers[$header]);
+
+            if (is_array($find)) {
+                foreach ($find as $f)
+                    if (strpos($value, $f) === 0)
+                        return true;
+            } elseif (strpos($value, $find) === 0) {
+                return true;
+            }
+        }
+
         return false;
     }
 
diff --git a/include/class.mailer.php b/include/class.mailer.php
index 2a965eb371cf3d3cf952e48be313f533bdd084a5..a227b8fc52951c563d56ccd4d59684149ae15336 100644
--- a/include/class.mailer.php
+++ b/include/class.mailer.php
@@ -92,9 +92,9 @@ class Mailer {
         require_once (PEAR_DIR.'Mail/mime.php'); // PEAR Mail_Mime packge
 
         //do some cleanup
-        $to=preg_replace("/(\r\n|\r|\n)/s",'', trim($to));
-        $subject=stripslashes(preg_replace("/(\r\n|\r|\n)/s",'', trim($subject)));
-        $body = stripslashes(preg_replace("/(\r\n|\r)/s", "\n", trim($message)));
+        $to = preg_replace("/(\r\n|\r|\n)/s",'', trim($to));
+        $subject = preg_replace("/(\r\n|\r|\n)/s",'', trim($subject));
+        $body = preg_replace("/(\r\n|\r)/s", "\n", trim($message));
 
         /* Message ID - generated for each outgoing email */
         $messageId = sprintf('<%s%d-%s>', Misc::randCode(6), time(),
diff --git a/include/class.mailfetch.php b/include/class.mailfetch.php
index aa43ce89e960231bdf2e25bf0f622a24d2b44741..fce15f537eb6125b5689ebef37879d267e90f850 100644
--- a/include/class.mailfetch.php
+++ b/include/class.mailfetch.php
@@ -378,7 +378,7 @@ class MailFetcher {
 	    //Is the email address banned?
         if($mailinfo['email'] && TicketFilter::isBanned($mailinfo['email'])) {
 	        //We need to let admin know...
-            $ost->logWarning('Ticket denied', 'Banned email - '.$mailinfo['email']);
+            $ost->logWarning('Ticket denied', 'Banned email - '.$mailinfo['email'], false);
 	        return true; //Report success (moved or delete)
         }
 
@@ -417,6 +417,16 @@ class MailFetcher {
         } elseif (($ticket=Ticket::create($var, $errors, 'Email'))) {
             $msgid = $ticket->getLastMsgId();
         } else {
+            //Report success if the email was absolutely rejected.
+            if(isset($errors['errno']) && $errors['errno'] == 403)
+                return true;
+
+            # check if it's a bounce!
+            if($var['header'] && TicketFilter::isAutoBounce($var['header'])) {
+                $ost->logWarning('Bounced email', $var['message'], false);
+                return true;
+            }
+
             //TODO: Log error..
             return null;
         }
diff --git a/include/class.ticket.php b/include/class.ticket.php
index c8d0e8c640f1b918a70166934320ddd431fec1ac..b4b7952b5f5740ab71301f324059a6eb7730eb98 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -976,7 +976,7 @@ class Ticket {
             $autorespond=$dept->autoRespONNewMessage();
 
 
-        if(!$autorespond && !$cfg->autoRespONNewMessage()) return;  //no autoresp or alerts.
+        if(!$autorespond || !$cfg->autoRespONNewMessage()) return;  //no autoresp or alerts.
 
         $this->reload();
 
@@ -2019,6 +2019,7 @@ class Ticket {
             //Make sure the email address is not banned
             if(TicketFilter::isBanned($vars['email'])) {
                 $errors['err']='Ticket denied. Error #403';
+                $errors['errno'] = 403;
                 $ost->logWarning('Ticket denied', 'Banned email - '.$vars['email']);
                 return 0;
             }
@@ -2044,6 +2045,7 @@ class Ticket {
         if($ticket_filter 
                 && ($filter=$ticket_filter->shouldReject())) {
             $errors['err']='Ticket denied. Error #403';
+            $errors['errno'] = 403;
             $ost->logWarning('Ticket denied', 
                     sprintf('Ticket rejected ( %s) by filter "%s"', 
                         $vars['email'], $filter->getName()));
diff --git a/include/class.variable.php b/include/class.variable.php
index f83ba61be9063d385943b4fe4f4fdc751a58f422..7d49ce592855e3d65bb5d41165bcbad6b5346bc9 100644
--- a/include/class.variable.php
+++ b/include/class.variable.php
@@ -98,7 +98,7 @@ class VariableReplacer {
         if(!($vars=$this->_parse($input)))
             return $input;
 
-        return preg_replace($this->_delimit(array_keys($vars)), array_values($vars), $input);
+        return str_replace(array_keys($vars), array_values($vars), $input);
     }
 
     function _resolveVar($var) {
@@ -134,14 +134,5 @@ class VariableReplacer {
 
         return $vars;
     }
-
-    //Helper function - will be replaced by a lambda function (PHP 5.3+)
-    function _delimit($val, $d='/') {
-
-        if($val && is_array($val))
-            return array_map(array($this, '_delimit'), $val);
-
-        return $d.$val.$d;
-    }
 }
 ?>
diff --git a/include/class.xml.php b/include/class.xml.php
index 854f182372c94f57338b8b45a94d155f69f5bd33..56baf4fbccaf65985921e081afd8e5f475c95c07 100644
--- a/include/class.xml.php
+++ b/include/class.xml.php
@@ -77,10 +77,14 @@ class XmlDataParser {
         $this->content = array_pop($this->stack);
         $i = 1;
         if (array_key_exists($name, $this->content)) {
-            while (array_key_exists("$name$i", $this->content)) $i++;
-            $name = "$name$i";
-        }
-        $this->content[$name] = $prev;
+            if(!isset($this->content[$name][0])) {
+                $current = $this->content[$name];
+                unset($this->content[$name]);
+                $this->content[$name][0] = $current;
+            }
+            $this->content[$name][] = $prev;
+        } else
+            $this->content[$name] = $prev;
     }
 
     function content($parser, $data) {
diff --git a/include/client/open.inc.php b/include/client/open.inc.php
index 2fd076100388888765970d1a561dc3c5235a8b90..275e856dea07f8726f06b3fa6641392e459bb2e4 100644
--- a/include/client/open.inc.php
+++ b/include/client/open.inc.php
@@ -57,7 +57,7 @@ $info=($_POST && $errors)?Format::htmlchars($_POST):$info;
         <td class="required">Help Topic:</td>
         <td>
             <select id="topicId" name="topicId">
-                <option value="" selected="selected">&mdash; Select a Help Topics &mdash;</option>
+                <option value="" selected="selected">&mdash; Select a Help Topic &mdash;</option>
                 <?php
                 if($topics=Topic::getPublicHelpTopics()) {
                     foreach($topics as $id =>$name) {
@@ -82,7 +82,7 @@ $info=($_POST && $errors)?Format::htmlchars($_POST):$info;
     <tr>
         <td class="required">Message:</td>
         <td>
-            <div><em>Please provide as much details as possible so we can best assist you.</em> <font class="error">*&nbsp;<?php echo $errors['message']; ?></font></div>
+            <div><em>Please provide as much detail as possible so we can best assist you.</em> <font class="error">*&nbsp;<?php echo $errors['message']; ?></font></div>
             <textarea id="message" cols="60" rows="8" name="message"><?php echo $info['message']; ?></textarea>
         </td>
     </tr>
diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php
index 534a358bfcc01bbb771340f78db2b5fcd7924cbd..341867b3ea2c9829d9c4a0e8e1f489c369a4d7f8 100644
--- a/include/staff/tickets.inc.php
+++ b/include/staff/tickets.inc.php
@@ -195,6 +195,8 @@ if($_REQUEST['sort'] && $sortOptions[$_REQUEST['sort']])
     $order_by =$sortOptions[$_REQUEST['sort']];
 elseif(!strcasecmp($status, 'open') && !$showanswered && $sortOptions[$_SESSION['tickets']['sort']]) {
     $_REQUEST['sort'] = $_SESSION['tickets']['sort'];
+    $_REQUEST['order'] = $_SESSION['tickets']['order'];
+
     $order_by = $sortOptions[$_SESSION['tickets']['sort']];
     $order = $_SESSION['tickets']['order'];
 }
diff --git a/l.php b/l.php
index 5e605c73cb3d48c32409c8ff89f727199bcbbba3..286a17299cd1e51850b3760ed4e6d264244f7974 100644
--- a/l.php
+++ b/l.php
@@ -21,7 +21,7 @@ if (!$url || !Validator::is_url($url)) exit('Invalid url');
 <html>
 <head>
     <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
-    <meta http-equiv="refresh" content="0;<?php echo $url; ?>"/>
+    <meta http-equiv="refresh" content="0;URL=<?php echo $url; ?>"/>
 </head>
 <body/>
 </html>
diff --git a/scp/css/scp.css b/scp/css/scp.css
index 59964d4e61561ba78bc704e3e6ddb2d1a73af964..67e6212512c0bf617d934fbf1feabd369784fd80 100644
--- a/scp/css/scp.css
+++ b/scp/css/scp.css
@@ -311,6 +311,7 @@ a.Icon:hover {
 .Icon.webTicket { background:url(../images/icons/ticket_source_web.gif) 0 0 no-repeat; }
 .Icon.emailTicket { background:url(../images/icons/ticket_source_email.gif) 0 0 no-repeat; }
 .Icon.phoneTicket { background:url(../images/icons/ticket_source_phone.gif) 0 0 no-repeat; }
+.Icon.apiTicket { background:url(../images/icons/ticket_source_other.gif) 0 0 no-repeat; }
 .Icon.otherTicket { background:url(../images/icons/ticket_source_other.gif) 0 0 no-repeat; }
 .Icon.overdueTicket { background:url(../images/icons/overdue_ticket.gif) 0 0 no-repeat; }
 .Icon.assignedTicket { background:url(../images/icons/assigned_ticket.gif) 0 0 no-repeat; }
diff --git a/scp/l.php b/scp/l.php
index 93fff3a24894612017f53ff0bb0a119b656f8b9d..dec8c0a6a52dd62c0d02f3edebfeeeaa79c0c8fa 100644
--- a/scp/l.php
+++ b/scp/l.php
@@ -21,7 +21,7 @@ if (!$url || !Validator::is_url($url)) exit('Invalid url');
 <html>
 <head>
     <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
-    <meta http-equiv="refresh" content="0;<?php echo $url; ?>"/>
+    <meta http-equiv="refresh" content="0; URL=<?php echo $url; ?>"/>
 </head>
 <body/>
 </html>
diff --git a/setup/doc/api.md b/setup/doc/api.md
new file mode 100644
index 0000000000000000000000000000000000000000..7509616aa2406b842f56cacd7a6c0693b23415ca
--- /dev/null
+++ b/setup/doc/api.md
@@ -0,0 +1,29 @@
+osTicket API
+============
+
+The osTicket API is implemented as (somewhat) simple XML or JSON over HTTP.
+For now, only ticket creation is supported, but eventually, all resources
+inside osTicket will be accessible and modifiable via the API.
+
+Authentication
+--------------
+
+Authentication via the API is done via API keys configured inside the
+osTicket admin panel. API keys are created and tied to a source IP address,
+which will be checked against the source IP of requests to the HTTP API.
+
+API keys can be created and managed via the admin panel. Navigate to Manage
+-> API keys. Use *Add New API Key* to create a new API key. Currently, no
+special configuration is required to allow the API key to be used for the
+HTTP API. All API keys are valid for the HTTP API.
+
+Wrappers
+--------
+
+Currently, there are no wrappers for the API. If you've written one and
+would like it on the list, submit a pull request to add your wrapper.
+
+Resources
+---------
+
+- [Tickets](api/tickets.md)
diff --git a/setup/doc/api/tickets.md b/setup/doc/api/tickets.md
new file mode 100644
index 0000000000000000000000000000000000000000..fc79b1aead833a9f4cf9fd3a214635dcba9e52fc
--- /dev/null
+++ b/setup/doc/api/tickets.md
@@ -0,0 +1,128 @@
+Tickets
+=======
+The API supports ticket creation via the HTTP API (as well as via email,
+etc.). Currently, the API support creation of tickets only -- so no
+modifications and deletions of existing tickets is possible via the API for
+now.
+
+Create a Ticket
+---------------
+
+Tickets can be created in the osTicket system by sending an HTTP POST to
+`api/tickets.xml` or `api/tickets.json` depending on the format of the
+request content.
+
+### Fields ######
+
+*   __email__:   *required* Email address of the submitter
+*   __name__:    *required* Name of the submitter
+*   __subject__: *required* Subject of the ticket
+*   __message__: *required* Initial message for the ticket thread
+*   __alert__:       If unset, disable alerts to staff. Default is `true`
+*   __autorespond__: If unset, disable autoresponses. Default is `true`
+*   __ip__:          IP address of the submitter
+*   __phone__:       Phone number of the submitter
+*   __phone_ext__:   Phone number extension -- can also be embedded in *phone*
+*   __priorityId__:  Priority *id* for the new ticket to assume
+*   __source__:      Source of the ticket, default is `API`
+*   __topicId__:     Help topic *id* associated with the ticket
+*   __attachments__: An array of files to attach to the initial message.
+                     Each attachment must have some content and also the
+                     following fields:
+    *   __name__:     *required* name of the file to be attached. Multiple files
+                      with the same name are allowable
+    *   __type__:     Mime type of the file. Default is `text/plain`
+    *   __encoding__: Set to `base64` if content is base64 encoded 
+
+### XML Payload Example ######
+
+* `POST /api/tickets.xml`
+
+The XML data format is extremely lax. Content can be either sent as an
+attribute or a named element if it has no sub-content.
+
+In the example below, the simple element could also be replaced as
+attributes on the root `<ticket>` element; however, if a `CDATA` element is
+necessary to hold special content, or difficult content such as double
+quotes is to be embedded, simple sub-elements are also supported.
+
+Notice that the phone extension can be sent as the `@ext` attribute of the
+`phone` sub-element.
+
+``` xml
+<?xml version="1.0" encoding="UTF-8"?>
+<ticket alert="true" autorespond="true" source="API">
+    <name>Angry User</name>
+    <email>api@osticket.com</email>
+    <subject>Testing API</subject>
+    <phone ext="123">318-555-8634</phone>
+    <message><![CDATA[Message content here]]></message>
+    <attachments>
+        <file name="file.txt" type="text/plain"><![CDATA[
+            File content is here and is automatically trimmed
+        ]]></file>
+        <file name="image.gif" type="image/gif" encoding="base64">
+            R0lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAwAAAC8IyPqcvt3wCcDkiLc7C0qwy
+            GHhSWpjQu5yqmCYsapyuvUUlvONmOZtfzgFzByTB10QgxOR0TqBQejhRNzOfkVJ
+            +5YiUqrXF5Y5lKh/DeuNcP5yLWGsEbtLiOSpa/TPg7JpJHxyendzWTBfX0cxOnK
+            PjgBzi4diinWGdkF8kjdfnycQZXZeYGejmJlZeGl9i2icVqaNVailT6F5iJ90m6
+            mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uisF81M1OIcR7lEewwcLp7tuNNkM3u
+            Nna3F2JQFo97Vriy/Xl4/f1cf5VWzXyym7PHhhx4dbgYKAAA7
+        </file>
+    </attachments>
+    <ip>123.211.233.122</ip>
+</ticket>
+```
+
+### JSON Payload Example ###
+
+* `POST /api/tickets.json`
+
+Attachment data for the JSON content uses the [RFC 2397][] data URL format.
+As described above, the content-type and base64 encoding hints are optional.
+Furthermore, a character set can be optionally declared for each attachment
+and will be automatically converted to UTF-8 for database storage.
+
+Notice that the phone number extension can be embedded in the `phone` value
+denoted with a capital `X`
+
+Do also note that the JSON format forbids a comma after the last element in
+an object or array definition, and newlines are not allowed inside strings.
+
+``` json
+{
+    "alert": true,
+    "autorespond": true,
+    "source": "API",
+    "name": "Angry User",
+    "email": "api@osticket.com",
+    "phone": "3185558634X123",
+    "subject": "Testing API",
+    "ip": "123.211.233.122",
+    "message": "MESSAGE HERE",
+    "attachments": [
+        {"file.txt": "data:text/plain;charset=utf-8,content"},
+        {"image.png": "data:image/png;base64,R0lGODdhMAA..."},
+    ]
+}
+```
+
+[rfc 2397]:     http://www.ietf.org/rfc/rfc2397.txt     "Data URLs"
+
+### Response ######
+
+If successful, the server will send `HTTP/201 Created`. Otherwise, it will
+send an appropriate HTTP status with the content being the error
+description. Most likely offenders are
+
+* Required field not included
+* Data type mismatch (text send for numeric field)
+* Incorrectly encoded base64 data
+* Unsupported field sent
+* Incorrectly formatted content (bad JSON or XML)
+
+Upon success, the content of the response will be the external ticket id of
+the newly-created ticket.
+
+    Status: 201 Created
+    123456