diff --git a/include/api.ticket.php b/include/api.ticket.php
index 4fcae4b183925dfe32bb91c95275a986fc492ea6..4cb4eb02d59a8e0e9ef283abde909cc74628d4c6 100644
--- a/include/api.ticket.php
+++ b/include/api.ticket.php
@@ -22,7 +22,9 @@ class TicketController extends ApiController {
     }
 
     function create($format) {
-        $this->requireApiKey();
+
+        if(!($key=$this->getApiKey()) || !$key->canCreateTickets())
+            Http::response(401, 'API key not authorized');
 
         # Parse request body
         $data = $this->getRequest($format);
diff --git a/include/class.api.php b/include/class.api.php
index 1bd25463484a4e2f01a1d72050dde4e92d89d967..8a15c04a493c9a71cc3ae2c137ce1cc65b064fbc 100644
--- a/include/class.api.php
+++ b/include/class.api.php
@@ -17,102 +17,115 @@ class API {
 
     var $id;
 
-    var $info;
+    var $ht;
 
-    function API($id){
-        $this->id=0;
+    function API($id) {
+        $this->id = 0;
         $this->load($id);
     }
 
-    function load($id) {
+    function load($id=0) {
+
+        if(!$id && !($id=$this->getId()))
+            return false;
 
         $sql='SELECT * FROM '.API_KEY_TABLE.' WHERE id='.db_input($id);
-        if(($res=db_query($sql)) && db_num_rows($res)) {
-            $info=db_fetch_array($res);
-            $this->id=$info['id'];
-            $this->info=$info;
-            return true;
-        }
-        return false;
+        if(!($res=db_query($sql)) || !db_num_rows($res))
+            return false;
+        
+        $this->ht = db_fetch_array($res);
+        $this->id = $this->ht['id'];
+        
+        return true;
     }
 
     function reload() {
-        return $this->load($this->getId());
+        return $this->load();
     }
 
-    function getId(){
+    function getId() {
         return $this->id;
     }
 
-    function getKey(){
-        return $this->info['apikey'];
+    function getKey() {
+        return $this->ht['apikey'];
     }
 
-    function getIPAddr(){
-        return $this->info['ipaddr'];
+    function getIPAddr() {
+        return $this->ht['ipaddr'];
     }
         
-    function getNotes(){
-        return $this->info['notes'];
+    function getNotes() {
+        return $this->ht['notes'];
     }
 
-    function isActive(){
-        return ($this->info['isactive']);
+    function getHashtable() {
+        return $this->ht;
     }
 
-    function update($vars,&$errors){
-        if(API::save($this->getId(),$vars,$errors)){
-            $this->reload();
-            return true;
-        }
+    function isActive() {
+        return ($this->ht['isactive']);
+    }
+
+    function canCreateTickets() {
+        return ($this->ht['can_create_tickets']);
+    }
+
+    function update($vars, &$errors) {
+
+        if(!API::save($this->getId(), $vars, $errors))
+            return false;
         
-        return false;
+        $this->reload();
+
+        return true;
     }
 
-    function delete(){
+    function delete() {
         $sql='DELETE FROM '.API_KEY_TABLE.' WHERE id='.db_input($this->getId()).' LIMIT 1';
         return (db_query($sql) && ($num=db_affected_rows()));
     }
 
     /** Static functions **/
-    function add($vars,&$errors){
-        return API::save(0,$vars,$errors);
+    function add($vars, &$errors) {
+        return API::save(0, $vars, $errors);
     }
 
-    function validate($key,$ip){
-
-        $sql='SELECT id FROM '.API_KEY_TABLE.' WHERE ipaddr='.db_input($ip).' AND apikey='.db_input($key);
-        return (($res=db_query($sql)) && db_num_rows($res));
+    function validate($key, $ip) {
+        return ($key && $ip && self::getIdByKey($key, $ip));
     }
 
-    function getKeyByIPAddr($ip){
+    function getIdByKey($key, $ip='') {
 
-        $sql='SELECT apikey FROM '.API_KEY_TABLE.' WHERE ipaddr='.db_input($ip);
+        $sql='SELECT id FROM '.API_KEY_TABLE.' WHERE apikey='.db_input($key);
+        if($ip)
+            $sql.=' AND ipaddr='.db_input($ip);
+        
         if(($res=db_query($sql)) && db_num_rows($res))
-            list($key)=db_fetch_row($res);
+            list($id) = db_insert_id();
 
-        return $key;
+        return $id;
     }
 
-    function lookup($id){
-        return ($id && is_numeric($id) && ($k= new API($id)) && $k->getId()==$id)?$k:null;
+    function lookupByKey($key, $ip='') {
+        return self::lookup(self::getIdByKey($key, $ip));
     }
 
-    function save($id,$vars,&$errors){
+    function lookup($id) {
+        return ($id && is_numeric($id) && ($k= new API($id)) && $k->getId()==$id)?$k:null;
+    }
 
-        if(!$id) {
-            if(!$vars['ipaddr'] || !Validator::is_ip($vars['ipaddr']))
-                $errors['ipaddr']='Valid IP required';
-            elseif(API::getKeyByIPAddr($vars['ipaddr']))
-                $errors['ipaddr']='API key for the IP already exists';
-        }
+    function save($id, $vars, &$errors) {
 
+        if(!$id && (!$vars['ipaddr'] || !Validator::is_ip($vars['ipaddr'])))
+            $errors['ipaddr'] = 'Valid IP required';
+        
         if($errors) return false;
 
-
-        $sql=' updated=NOW() '.
-             ',isactive='.db_input($vars['isactive']).
-             ',notes='.db_input($vars['notes']);
+        $sql=' updated=NOW() '
+            .',isactive='.db_input($vars['isactive'])
+            .',can_create_tickets='.db_input($vars['can_create_tickets'])
+            .',notes='.db_input($vars['notes']);
 
         if($id) {
             $sql='UPDATE '.API_KEY_TABLE.' SET '.$sql.' WHERE id='.db_input($id);
@@ -120,14 +133,17 @@ class API {
                 return true;
 
             $errors['err']='Unable to update API key. Internal error occurred';
-        }else{
-            $sql='INSERT INTO '.API_KEY_TABLE.' SET '.$sql.',created=NOW() '.
-                 ',ipaddr='.db_input($vars['ipaddr']).
-                 ',apikey='.db_input(strtoupper(md5(time().$vars['ipaddr'].md5(Misc::randcode(16)))));
+
+        } else {
+            $sql='INSERT INTO '.API_KEY_TABLE.' SET '.$sql
+                .',created=NOW() '
+                .',ipaddr='.db_input($vars['ipaddr'])
+                .',apikey='.db_input(strtoupper(md5(time().$vars['ipaddr'].md5(Misc::randcode(16)))));
+
             if(db_query($sql) && ($id=db_insert_id()))
                 return $id;
 
-            $errors['err']='Unable to add API key. Internal error';
+            $errors['err']='Unable to add API key. Try again!';
         }
 
         return false;
@@ -141,16 +157,24 @@ class API {
  * API request.
  */
 class ApiController {
+
     function requireApiKey() {
         # Validate the API key -- required to be sent via the X-API-Key
         # header
-        if (!isset($_SERVER['HTTP_X_API_KEY']))
+        if (!isset($_SERVER['HTTP_X_API_KEY']) || !isset($_SERVER['REMOTE_ADDR']))
             Http::response(403, "API key required");
-        else if (!Api::validate($_SERVER['HTTP_X_API_KEY'],
-                $_SERVER['REMOTE_ADDR']))
-            Http::response(401,
-                "API key not found or source IP not authorized");
+        elseif (!($key=API::lookupByKey($_SERVER['HTTP_X_API_KEY'], $_SERVER['REMOTE_ADDR']))
+                || !$key->isActive() 
+                || $key->getIP()!=$_SERVER['REMOTE_ADDR'])
+            Http::response(401, "API key not found/active or source IP not authorized");
+
+        return $key;
     }
+
+    function getApiKey() {
+        return $this->requireApiKey();
+    }
+
     /**
      * Retrieves the body of the API request and converts it to a common
      * hashtable. For JSON formats, this is mostly a noop, the conversion
@@ -243,7 +267,7 @@ class ApiJsonDataParser extends JsonDataParser {
             return $current;
         foreach ($current as $key=>&$value) {
             if ($key == "phone") {
-                list($value,$current["phone_ext"])
+                list($value, $current["phone_ext"])
                     = explode("X", strtoupper($value), 2); 
             } else if ($key == "alert") {
                 $value = (bool)$value;
diff --git a/include/staff/apikey.inc.php b/include/staff/apikey.inc.php
index ff5592b46fd57b374bb0a2d0825f99d3156bceef..6e2ffe6e6d8fdc4d41134726e6853162f3bfe0da 100644
--- a/include/staff/apikey.inc.php
+++ b/include/staff/apikey.inc.php
@@ -6,9 +6,7 @@ if($api && $_REQUEST['a']!='add'){
     $title='Update API Key';
     $action='update';
     $submit_text='Save Changes';
-    $info['id']=$api->getId();
-    $info['isactive']=$api->isActive()?1:0;
-    $info['notes']=$api->getNotes();
+    $info=$api->getHashtable();
     $qstr.='&id='.$api->getId();
 }else {
     $title='Add New API Key';
@@ -30,13 +28,13 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
-                <em>API Key is autogenerated and unique per IP address.</em>
+                <em>API Key is auto-generated. Delete and re-add to change the key.</em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
-            <td width="180" class="required">
+            <td width="150" class="required">
                 Status:
             </td>
             <td>
@@ -47,7 +45,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <?php if($api){ ?>
         <tr>
-            <td width="180">
+            <td width="150">
                 IP Address:
             </td>
             <td>
@@ -55,14 +53,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
             </td>
         </tr>
         <tr>
-            <td width="180">
+            <td width="150">
                 API Key:
             </td>
-            <td><?php echo $api->getKey(); ?> &nbsp;&nbsp;<em>(Delete and re-add to change the key)</em></td>
+            <td><?php echo $api->getKey(); ?> &nbsp;</td>
         </tr>
         <?php }else{ ?>
         <tr>
-            <td width="180" class="required">
+            <td width="150" class="required">
                IP Address:
             </td>
             <td>
@@ -73,16 +71,13 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         <?php } ?>
         <tr>
             <th colspan="2">
-                <em><strong>Enabled Services:</strong>: Check applicable API services.</em>
+                <em><strong>Enabled Services:</strong>: Check applicable API services. All active keys can make cron call.</em>
             </th>
         </tr>
         <tr>
-            <td width="180">
-                Email piping:
-            </td>
-            <td>
-                <input type="checkbox" name="email_piping" value="1" checked="checked" disabled="disabled" >
-                    <strong>Enable</strong> remote email piping.
+            <td colspan=2 style="padding-left:5px">
+                <input type="checkbox" name="can_create_tickets" value="1" <?php echo $info['can_create_tickets']?'checked="checked"':''; ?> >
+                Can Create Tickets. <em>(XML/JSON/PIPE)</em>
             </td>
         </tr>
         <tr>
diff --git a/include/staff/apikeys.inc.php b/include/staff/apikeys.inc.php
index b085329343d101fe69de9b4628ed13ace6d6586b..90b4e31eef10af37b71ba2ebef6edffc9dc63d7b 100644
--- a/include/staff/apikeys.inc.php
+++ b/include/staff/apikeys.inc.php
@@ -5,7 +5,7 @@ $qstr='';
 $sql='SELECT * FROM '.API_KEY_TABLE.' WHERE 1';
 $sortOptions=array('key'=>'apikey','status'=>'isactive','ip'=>'ipaddr','date'=>'created','created'=>'created','updated'=>'updated');
 $orderWays=array('DESC'=>'DESC','ASC'=>'ASC');
-$sort=($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])])?strtolower($_REQUEST['sort']):'date';
+$sort=($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])])?strtolower($_REQUEST['sort']):'key';
 //Sorting options...
 if($sort && $sortOptions[$sort]) {
     $order_column =$sortOptions[$sort];
@@ -54,10 +54,10 @@ else
     <thead>
         <tr>
             <th width="7">&nbsp;</th>        
-            <th width="150" nowrap><a  <?php echo $date_sort; ?>href="apikeys.php?<?php echo $qstr; ?>&sort=date">Date Added</a></th>
             <th width="320"><a <?php echo $key_sort; ?> href="apikeys.php?<?php echo $qstr; ?>&sort=key">API Key</a></th>
-            <th width="100"><a  <?php echo $status_sort; ?> href="apikeys.php?<?php echo $qstr; ?>&sort=status">Status</a></th>
             <th width="120"><a  <?php echo $ip_sort; ?> href="apikeys.php?<?php echo $qstr; ?>&sort=ip">IP Addr.</a></th>
+            <th width="100"><a  <?php echo $status_sort; ?> href="apikeys.php?<?php echo $qstr; ?>&sort=status">Status</a></th>
+            <th width="150" nowrap><a  <?php echo $date_sort; ?>href="apikeys.php?<?php echo $qstr; ?>&sort=date">Date Added</a></th>
             <th width="150" nowrap><a  <?php echo $updated_sort; ?>href="apikeys.php?<?php echo $qstr; ?>&sort=updated">Last Updated</a></th>
         </tr>
     </thead>
@@ -75,10 +75,10 @@ else
                 <td width=7px>
                   <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['id']; ?>" 
                             <?php echo $sel?'checked="checked"':''; ?>> </td>
-                <td>&nbsp;<?php echo Format::db_date($row['created']); ?></td>
                 <td>&nbsp;<a href="apikeys.php?id=<?php echo $row['id']; ?>"><?php echo Format::htmlchars($row['apikey']); ?></a></td>
-                <td><?php echo $row['isactive']?'Active':'<b>Disabled</b>'; ?></td>
                 <td><?php echo $row['ipaddr']; ?></td>
+                <td><?php echo $row['isactive']?'Active':'<b>Disabled</b>'; ?></td>
+                <td>&nbsp;<?php echo Format::db_date($row['created']); ?></td>
                 <td>&nbsp;<?php echo Format::db_datetime($row['updated']); ?></td>
             </tr>
             <?php