<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
		<id>http://wiki.x2crm.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Demitri</id>
		<title>X2Engine - User contributions [en]</title>
		<link rel="self" type="application/atom+xml" href="http://wiki.x2crm.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Demitri"/>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/wiki/Special:Contributions/Demitri"/>
		<updated>2026-04-03T23:25:45Z</updated>
		<subtitle>User contributions</subtitle>
		<generator>MediaWiki 1.28.0</generator>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=MediaWiki:Sidebar&amp;diff=1926</id>
		<title>MediaWiki:Sidebar</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=MediaWiki:Sidebar&amp;diff=1926"/>
				<updated>2014-08-07T21:56:16Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Navigation&lt;br /&gt;
** mainpage|mainpage-description&lt;br /&gt;
** http://x2community.com | Forums&lt;br /&gt;
** http://www.x2engine.com | X2Engine Main Site&lt;br /&gt;
* Get Help&lt;br /&gt;
** Frequently Asked Questions | FAQ&lt;br /&gt;
** http://www.x2engine.com/reference_guide/ | User Guide&lt;br /&gt;
** http://x2community.com | Community Forum&lt;br /&gt;
* Reference Guides&lt;br /&gt;
** Preparing a Linux Server Environment | Prepare a Webserver&lt;br /&gt;
** Installation | Install&lt;br /&gt;
** Software Updates and Upgrades | Update&lt;br /&gt;
** E-Mail Configuration | Configure E-Mail&lt;br /&gt;
** Google Integration | Integrate w/Google&lt;br /&gt;
** Manually Resetting Passwords | Reset Passwords&lt;br /&gt;
** Known Issues | Troubleshooting&lt;br /&gt;
* Development Guide&lt;br /&gt;
** Introduction To Development | Introduction&lt;br /&gt;
** Customization_Framework | Custom Development&lt;br /&gt;
** REST API Reference | REST API&lt;br /&gt;
** Category:Development | All Pages&lt;br /&gt;
** http://doc.x2engine.com | Class Reference&lt;br /&gt;
* TOOLBOX&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=MediaWiki:Sidebar&amp;diff=1925</id>
		<title>MediaWiki:Sidebar</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=MediaWiki:Sidebar&amp;diff=1925"/>
				<updated>2014-08-07T21:56:02Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Navigation&lt;br /&gt;
** mainpage|mainpage-description&lt;br /&gt;
** http://x2community.com | Forums&lt;br /&gt;
** http://www.x2engine.com | X2Engine Main Site&lt;br /&gt;
* Get Help&lt;br /&gt;
** Frequently Asked Questions | FAQ&lt;br /&gt;
** http://www.x2engine.com/reference_guide/ | User Guide&lt;br /&gt;
** http://x2community.com | Community Forum&lt;br /&gt;
* Reference Guides&lt;br /&gt;
** Preparing a Linux Server Environment | Prepare a Webserver&lt;br /&gt;
** Installation | Install&lt;br /&gt;
** Software Updates and Upgrades | Update&lt;br /&gt;
** E-Mail Configuration | Configure E-Mail&lt;br /&gt;
** Google Integration | Integrate w/Google&lt;br /&gt;
** Manually Resetting Passwords | Reset Passwords&lt;br /&gt;
** Known Issues | Troubleshooting&lt;br /&gt;
* Development Guide&lt;br /&gt;
** Introduction To Development | Introduction&lt;br /&gt;
** Customization_Framework | Custom Development&lt;br /&gt;
** REST API Reference | REST API&lt;br /&gt;
** Category:Development | All in Category&lt;br /&gt;
** http://doc.x2engine.com | Class Reference&lt;br /&gt;
* TOOLBOX&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=MediaWiki:Sidebar&amp;diff=1924</id>
		<title>MediaWiki:Sidebar</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=MediaWiki:Sidebar&amp;diff=1924"/>
				<updated>2014-08-07T21:55:15Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Navigation&lt;br /&gt;
** mainpage|mainpage-description&lt;br /&gt;
** http://x2community.com | Forums&lt;br /&gt;
** http://www.x2engine.com | X2Engine Main Site&lt;br /&gt;
* Get Help&lt;br /&gt;
** Frequently Asked Questions | FAQ&lt;br /&gt;
** http://www.x2engine.com/reference_guide/ | User Guide&lt;br /&gt;
** http://x2community.com | Community Forum&lt;br /&gt;
* Reference Guides&lt;br /&gt;
** Preparing a Linux Server Environment | Prepare a Webserver&lt;br /&gt;
** Installation | Install&lt;br /&gt;
** Software Updates and Upgrades | Update&lt;br /&gt;
** E-Mail Configuration | Configure E-Mail&lt;br /&gt;
** Google Integration | Integrate w/Google&lt;br /&gt;
** Manually Resetting Passwords | Reset Passwords&lt;br /&gt;
** Known Issues | Troubleshooting&lt;br /&gt;
* Development Guide&lt;br /&gt;
** Introduction To Development | Introduction&lt;br /&gt;
** Category:Development | Development Topics&lt;br /&gt;
** Customization_Framework | Custom Development&lt;br /&gt;
** REST API Reference | REST API&lt;br /&gt;
** http://doc.x2engine.com | Class Reference&lt;br /&gt;
* TOOLBOX&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=MediaWiki:Sidebar&amp;diff=1923</id>
		<title>MediaWiki:Sidebar</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=MediaWiki:Sidebar&amp;diff=1923"/>
				<updated>2014-08-07T21:54:56Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Navigation&lt;br /&gt;
** mainpage|mainpage-description&lt;br /&gt;
** http://x2community.com | Forums&lt;br /&gt;
** http://www.x2engine.com | X2Engine Main Site&lt;br /&gt;
* Get Help&lt;br /&gt;
** Frequently Asked Questions | FAQ&lt;br /&gt;
** http://www.x2engine.com/reference_guide/ | User Guide&lt;br /&gt;
** http://x2community.com | Community Forum&lt;br /&gt;
* Reference Guides&lt;br /&gt;
** Preparing a Linux Server Environment | Prepare a Webserver&lt;br /&gt;
** Installation | Install&lt;br /&gt;
** Software Updates and Upgrades | Update&lt;br /&gt;
** E-Mail Configuration | Configure E-Mail&lt;br /&gt;
** Google Integration | Integrate w/Google&lt;br /&gt;
** Manually Resetting Passwords | Reset Passwords&lt;br /&gt;
** Known Issues | Troubleshooting&lt;br /&gt;
* Development Guide&lt;br /&gt;
** Introduction To Development | Introduction&lt;br /&gt;
** Category:Development | Development Topics&lt;br /&gt;
** REST API Reference | API&lt;br /&gt;
** Customization_Framework | Custom Coding&lt;br /&gt;
** http://doc.x2engine.com | Class Reference&lt;br /&gt;
* TOOLBOX&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1922</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1922"/>
				<updated>2014-08-07T19:47:34Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API. &lt;br /&gt;
&lt;br /&gt;
The source code of the main components used in this API are:&lt;br /&gt;
* [https://github.com/X2Engine/X2Engine/blob/master/x2engine/protected/controllers/Api2Controller.php protected/controllers/Api2Controller.php]&lt;br /&gt;
* [https://github.com/X2Engine/X2Engine/blob/master/x2engine/protected/components/util/ResponseUtil.php protected/components/ResponseBehavior.php]&lt;br /&gt;
* [https://github.com/X2Engine/X2Engine/blob/master/x2engine/protected/components/util/ResponseUtil.php protected/components/util/ResponseUtil.php]&lt;br /&gt;
Functional tests for the API can be found alongside the tests for the legacy API, in [https://github.com/X2Engine/X2Engine/tree/master/x2engine/protected/tests/api protected/tests/api].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module, by going to the edit page for that user. To summarize:&lt;br /&gt;
# As the administrator, go to the user management module. You'll find it under &amp;quot;Users&amp;quot;&lt;br /&gt;
# Click on the desired user&lt;br /&gt;
# Click &amp;quot;Update User&amp;quot;&lt;br /&gt;
# See the &amp;quot;API Key&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Caveats ===&lt;br /&gt;
'''Note, firstly:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
Furthermore, if you have password-protected any web directories that contain X2Engine, you will need to make a &amp;quot;Satisfy any&amp;quot; exception for URIs within the API, or clients may not be able to authenticate.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow; effectively, the comparison in the query is being performed on a &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt; column in a joined table&lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2Leads&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type can be fully accessed and manipulated, but there is not yet any built-in action available for directly converting a lead to an opportunity.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Direct Manipulation by Attributes ==&lt;br /&gt;
Given a set of uniquely-identifying attribute names and values, it is possible to directly access and manipulate an existing X2Model-based record by without first querying it. The direct URI for this use case is:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/{_class}/by:{_findBy}.json&lt;br /&gt;
&lt;br /&gt;
where the query parameter &amp;lt;tt&amp;gt;{_findBy}&amp;lt;/tt&amp;gt; is a list of key and value pairs formatted as &amp;lt;tt&amp;gt;key1=value1;key2=value2&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
For example: provided the email address &amp;quot;james@example.com&amp;quot; and first name &amp;quot;James&amp;quot; uniquely identify the contact with ID of 457, the following two direct access URIs are equivalent:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/Contacts/by:email=james@example.com;firstName=James.json&lt;br /&gt;
  index.php/api2/Contacts/457.json&lt;br /&gt;
&lt;br /&gt;
Furthermore, note what happens in the following scenarios:&lt;br /&gt;
&lt;br /&gt;
'''If the set of attributes (i.e. email address) is not unique to a single record:''' the find-by-attributes direct URI will return with a 300 status and an [[#Error Objects|error object]] containing the two special properties: ''queryUri'', the URI to query for records by the attributes given, and: ''directUris'', a list containing the direct URI of each matching record.&lt;br /&gt;
&lt;br /&gt;
''' If no contact matches the attributes given:''' the URI will respond with a 404 status.&lt;br /&gt;
&lt;br /&gt;
=== Using the first matching record ===&lt;br /&gt;
If one wants to forcefully select and use the first record matching the attributes, regardless of how many match, append the &amp;lt;tt&amp;gt;_useFirst&amp;lt;/tt&amp;gt; parameter and set equal to 1, i.e.&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/Contacts/by:email=james@example.com;firstName=James.json?_useFirst=1&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;300&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When using the &amp;quot;[[#Direct Manipulation by Attributes|find by attributes]]&amp;quot; direct access URI&lt;br /&gt;
|''Multiple choices.'' There criteria specified for direct access (as opposed to querying) match more than one record.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1921</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1921"/>
				<updated>2014-08-07T19:44:25Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Direct Manipulation by Attributes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API. &lt;br /&gt;
&lt;br /&gt;
The source code of the main components used in this API are:&lt;br /&gt;
* [https://github.com/X2Engine/X2Engine/blob/master/x2engine/protected/controllers/Api2Controller.php protected/controllers/Api2Controller.php]&lt;br /&gt;
* [https://github.com/X2Engine/X2Engine/blob/master/x2engine/protected/components/util/ResponseUtil.php protected/components/ResponseBehavior.php]&lt;br /&gt;
* [https://github.com/X2Engine/X2Engine/blob/master/x2engine/protected/components/util/ResponseUtil.php protected/components/util/ResponseUtil.php]&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module, by going to the edit page for that user. To summarize:&lt;br /&gt;
# As the administrator, go to the user management module. You'll find it under &amp;quot;Users&amp;quot;&lt;br /&gt;
# Click on the desired user&lt;br /&gt;
# Click &amp;quot;Update User&amp;quot;&lt;br /&gt;
# See the &amp;quot;API Key&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Caveats ===&lt;br /&gt;
'''Note, firstly:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
Furthermore, if you have password-protected any web directories that contain X2Engine, you will need to make a &amp;quot;Satisfy any&amp;quot; exception for URIs within the API, or clients may not be able to authenticate.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow; effectively, the comparison in the query is being performed on a &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt; column in a joined table&lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2Leads&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type can be fully accessed and manipulated, but there is not yet any built-in action available for directly converting a lead to an opportunity.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Direct Manipulation by Attributes ==&lt;br /&gt;
Given a set of uniquely-identifying attribute names and values, it is possible to directly access and manipulate an existing X2Model-based record by without first querying it. The direct URI for this use case is:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/{_class}/by:{_findBy}.json&lt;br /&gt;
&lt;br /&gt;
where the query parameter &amp;lt;tt&amp;gt;{_findBy}&amp;lt;/tt&amp;gt; is a list of key and value pairs formatted as &amp;lt;tt&amp;gt;key1=value1;key2=value2&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
For example: provided the email address &amp;quot;james@example.com&amp;quot; and first name &amp;quot;James&amp;quot; uniquely identify the contact with ID of 457, the following two direct access URIs are equivalent:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/Contacts/by:email=james@example.com;firstName=James.json&lt;br /&gt;
  index.php/api2/Contacts/457.json&lt;br /&gt;
&lt;br /&gt;
Furthermore, note what happens in the following scenarios:&lt;br /&gt;
&lt;br /&gt;
'''If the set of attributes (i.e. email address) is not unique to a single record:''' the find-by-attributes direct URI will return with a 300 status and an [[#Error Objects|error object]] containing the two special properties: ''queryUri'', the URI to query for records by the attributes given, and: ''directUris'', a list containing the direct URI of each matching record.&lt;br /&gt;
&lt;br /&gt;
''' If no contact matches the attributes given:''' the URI will respond with a 404 status.&lt;br /&gt;
&lt;br /&gt;
=== Using the first matching record ===&lt;br /&gt;
If one wants to forcefully select and use the first record matching the attributes, regardless of how many match, append the &amp;lt;tt&amp;gt;_useFirst&amp;lt;/tt&amp;gt; parameter and set equal to 1, i.e.&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/Contacts/by:email=james@example.com;firstName=James.json?_useFirst=1&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;300&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When using the &amp;quot;[[#Direct Manipulation by Attributes|find by attributes]]&amp;quot; direct access URI&lt;br /&gt;
|''Multiple choices.'' There criteria specified for direct access (as opposed to querying) match more than one record.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1920</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1920"/>
				<updated>2014-08-07T19:30:53Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API. &lt;br /&gt;
&lt;br /&gt;
The source code of the main components used in this API are:&lt;br /&gt;
* [https://github.com/X2Engine/X2Engine/blob/master/x2engine/protected/controllers/Api2Controller.php protected/controllers/Api2Controller.php]&lt;br /&gt;
* [https://github.com/X2Engine/X2Engine/blob/master/x2engine/protected/components/util/ResponseUtil.php protected/components/ResponseBehavior.php]&lt;br /&gt;
* [https://github.com/X2Engine/X2Engine/blob/master/x2engine/protected/components/util/ResponseUtil.php protected/components/util/ResponseUtil.php]&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module, by going to the edit page for that user. To summarize:&lt;br /&gt;
# As the administrator, go to the user management module. You'll find it under &amp;quot;Users&amp;quot;&lt;br /&gt;
# Click on the desired user&lt;br /&gt;
# Click &amp;quot;Update User&amp;quot;&lt;br /&gt;
# See the &amp;quot;API Key&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Caveats ===&lt;br /&gt;
'''Note, firstly:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
Furthermore, if you have password-protected any web directories that contain X2Engine, you will need to make a &amp;quot;Satisfy any&amp;quot; exception for URIs within the API, or clients may not be able to authenticate.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow; effectively, the comparison in the query is being performed on a &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt; column in a joined table&lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2Leads&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type can be fully accessed and manipulated, but there is not yet any built-in action available for directly converting a lead to an opportunity.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Direct Manipulation by Attributes ==&lt;br /&gt;
(Coming in 4.2)&lt;br /&gt;
&lt;br /&gt;
Given a set of uniquely-identifying attribute names and values, it is possible to directly access and manipulate an existing X2Model-based record by without first querying it. The direct URI for this use case is:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/{_class}/by:{_findBy}.json&lt;br /&gt;
&lt;br /&gt;
where the query parameter &amp;lt;tt&amp;gt;{_findBy}&amp;lt;/tt&amp;gt; is a list of key and value pairs formatted as &amp;lt;tt&amp;gt;key1=value1;key2=value2&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
For example: provided the email address &amp;quot;james@example.com&amp;quot; and first name &amp;quot;James&amp;quot; uniquely identify the contact with ID of 457, the following two direct access URIs are equivalent:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/Contacts/by:email=james@example.com;firstName=James.json&lt;br /&gt;
  index.php/api2/Contacts/457.json&lt;br /&gt;
&lt;br /&gt;
Furthermore, note what happens in the following scenarios:&lt;br /&gt;
&lt;br /&gt;
'''If the set of attributes (i.e. email address) is not unique to a single record:''' the find-by-attributes direct URI will return with a 300 status and an [[#Error Objects|error object]] containing the two special properties: ''queryUri'', the URI to query for records by the attributes given, and: ''directUris'', a list containing the direct URI of each matching record.&lt;br /&gt;
&lt;br /&gt;
''' If no contact matches the attributes given:''' the URI will respond with a 404 status.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;300&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When using the &amp;quot;[[#Direct Manipulation by Attributes|find by attributes]]&amp;quot; direct access URI&lt;br /&gt;
|''Multiple choices.'' There criteria specified for direct access (as opposed to querying) match more than one record.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=VoIP_Integration&amp;diff=1919</id>
		<title>VoIP Integration</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=VoIP_Integration&amp;diff=1919"/>
				<updated>2014-08-07T19:27:03Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
To a limited extent, X2Engine currently supports integration with VoIP phone systems (such as Asterisk servers) in the form of inbound call notifications and &amp;quot;screen pops&amp;quot;. While this is still somewhat a proof-of-concept, setting it up to work is fully within the reach of the technically-inclined user with administrative access to the VoIP system and X2Engine installation, and it is very useful in expediting the retrieval of customer data when taking a call.&lt;br /&gt;
&lt;br /&gt;
Note, far more in terms of integration with Asterisk may be possible via the [[REST API Reference|REST API]]. That would, however require custom coding by the implementer; no out-of-box solutions are yet included with X2Engine's source code.&lt;br /&gt;
&lt;br /&gt;
= What it does =&lt;br /&gt;
A customer, whose phone number is saved in X2Engine, calls the organization. As soon as the call session is initiated with the VoIP system, the VoIP system makes a web request to X2Engine's [[Web_API_Reference_%28Legacy%29#Inbound_VoIP_call_notifications|web API]] with the caller ID. The call is then recorded, and shows up inside X2Engine, in three places: &lt;br /&gt;
# The activity feed, with a link to the contact;&lt;br /&gt;
# As a notification for the user who has been assigned that contact, or for all users if the contact is unassigned;&lt;br /&gt;
# In the action history on that contact record.&lt;br /&gt;
Additionally, if the contact is not assigned to &amp;quot;anyone&amp;quot;, and the user to whom the contact is assigned does not have pop-up blocking enabled, a pop-up window will appear on the assignee's screen displaying the contact record, thus immediately displaying to that representative the full history of interaction with the customer.&lt;br /&gt;
&lt;br /&gt;
= What it requires in order to work =&lt;br /&gt;
In essence, at the most basic level, the phone system must be connected to the internet, and must be configurable such that it makes an appropriate HTTP/HTTPS request (of type &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;) to X2Engine at the right time. The procedure for implementing this customization can vary widely between phone systems, but how the essential API call must be made is the same in all cases and is described in [[Web_API_Reference_%28Legacy%29#Inbound_VoIP_call_notifications|Remote API: Inbound VoIP call notifications]].&lt;br /&gt;
&lt;br /&gt;
If the VoIP system is Asterisk-based, supports AGI interaction, and has PHP installed on it, a script included in the source code of X2Engine Professional Edition can be used instead of having to write a script from scratch. The rest of this article will cover the usage of this script.&lt;br /&gt;
&lt;br /&gt;
= Using the included script for Asterisk integration =&lt;br /&gt;
The script for Asterisk integration, named &amp;lt;tt&amp;gt;callX2Engine.php&amp;lt;/tt&amp;gt;, is included in the X2Engine source code (in Professional Edition) inside the directory &amp;lt;tt&amp;gt;protected/integration/FreePBX&amp;lt;/tt&amp;gt;. It requires the following two files be present in the same directory where it will run:&lt;br /&gt;
# The [http://phpagi.sourceforge.net/ PHPAGI] class (and with the filename &amp;lt;tt&amp;gt;phpagi.php&amp;lt;/tt&amp;gt;)&lt;br /&gt;
# A copy of the standalone APIModel class from X2Engine, which can be found in &amp;lt;tt&amp;gt;protected/models&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All three of these files (&amp;lt;tt&amp;gt;callX2Engine.php&amp;lt;/tt&amp;gt; and its dependencies) must then be placed in the &amp;lt;tt&amp;gt;agi-bin&amp;lt;/tt&amp;gt; directory on the Asterisk server to allow easily inserting it as an application call into the dialplan. &lt;br /&gt;
&lt;br /&gt;
Finally, the script itself must be configured appropriately. Near the top of the file, one will find the following global string variables that must be properly defined:&lt;br /&gt;
;&amp;lt;tt&amp;gt;$baseUrl&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The URL to the entry script (&amp;lt;tt&amp;gt;index.php&amp;lt;/tt&amp;gt;) of X2Engine. For example, if your CRM is hosted in http://example.com/crm, you will put for the value of this variable &amp;lt;tt&amp;gt;http://example.com/crm/index.php&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;$user&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The user of X2Engine to use for API authentication, when necessary.&lt;br /&gt;
;&amp;lt;tt&amp;gt;$userKey&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The API key of the X2Engine user for API authentication.&lt;br /&gt;
&lt;br /&gt;
= Case example: FreePBX integration =&lt;br /&gt;
FreePBX can be configured to use the AGI script easily, by performing the following:&lt;br /&gt;
# Upload the dialplan file &amp;lt;tt&amp;gt;extensions_custom.conf&amp;lt;/tt&amp;gt; (you'll find it in the same directory as &amp;lt;tt&amp;gt;callX2Engine.php&amp;lt;/tt&amp;gt;) to replace the existing file in the Asterisk configuration directory on the phone system. In FreePBX distro, this directory will be &amp;lt;tt&amp;gt;/etc/asterisk&amp;lt;/tt&amp;gt;.&lt;br /&gt;
# As for the PHP scripts: the &amp;lt;tt&amp;gt;agi-bin&amp;lt;/tt&amp;gt; directory, in FreePBX distro, is located at &amp;lt;tt&amp;gt;/var/lib/asterisk/agi-bin&amp;lt;/tt&amp;gt;. Note, if using FreePBX distro, you should exclude &amp;lt;tt&amp;gt;phpagi.php&amp;lt;/tt&amp;gt;, because it is already present and available in the &amp;lt;tt&amp;gt;agi-bin&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
# Change the permissions on &amp;lt;tt&amp;gt;callX2Engine.php&amp;lt;/tt&amp;gt; to be executable and readable by the system user &amp;lt;tt&amp;gt;asterisk&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== What this all does ==&lt;br /&gt;
The file &amp;lt;tt&amp;gt;extensions_custom.conf&amp;lt;/tt&amp;gt; contains the following lines:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
;; This file can be put in /etc/asterisk to configure a FreePBX-managed Asterisk system for&lt;br /&gt;
;; creating inbound call notifications in X2Engine.&lt;br /&gt;
[from-pstn-custom]&lt;br /&gt;
exten =&amp;gt; _.,1,AGI(callX2Engine.php)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
When the file is included by in FreePBX's default dialplan file (&amp;lt;tt&amp;gt;extensions.conf&amp;lt;/tt&amp;gt;), this &amp;lt;tt&amp;gt;[from-pstn-custom]&amp;lt;/tt&amp;gt; context will be &amp;quot;imported&amp;quot; into the corresponding non-custom context, &amp;lt;tt&amp;gt;[from-pstn]&amp;lt;/tt&amp;gt;. This context is entered by callers coming in from the PSTN (Public Switched Telephone Network). Thus, putting the extension that makes the AGI call in from-pstn-custom will make it so that whenever a call comes in through the SIP trunk from a &amp;quot;traditional&amp;quot; phone network, the AGI call will be made.&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=VoIP_Integration&amp;diff=1918</id>
		<title>VoIP Integration</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=VoIP_Integration&amp;diff=1918"/>
				<updated>2014-08-07T19:26:34Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
To a limited extent, X2Engine currently supports integration with VoIP phone systems (such as Asterisk servers) in the form of inbound call notifications and &amp;quot;screen pops&amp;quot;. While this is still somewhat a proof-of-concept, setting it up to work is fully within the reach of the technically-inclined user with administrative access to the VoIP system and X2Engine installation, and it is very useful in expediting the retrieval of customer data when taking a call.&lt;br /&gt;
&lt;br /&gt;
Note, far more in terms of integration with Asterisk may be possible via the [[REST API Reference|REST API]], but that would require custom coding by the implementer, and no out-of-box solutions are yet included with X2Engine's source code.&lt;br /&gt;
&lt;br /&gt;
= What it does =&lt;br /&gt;
A customer, whose phone number is saved in X2Engine, calls the organization. As soon as the call session is initiated with the VoIP system, the VoIP system makes a web request to X2Engine's [[Web_API_Reference_%28Legacy%29#Inbound_VoIP_call_notifications|web API]] with the caller ID. The call is then recorded, and shows up inside X2Engine, in three places: &lt;br /&gt;
# The activity feed, with a link to the contact;&lt;br /&gt;
# As a notification for the user who has been assigned that contact, or for all users if the contact is unassigned;&lt;br /&gt;
# In the action history on that contact record.&lt;br /&gt;
Additionally, if the contact is not assigned to &amp;quot;anyone&amp;quot;, and the user to whom the contact is assigned does not have pop-up blocking enabled, a pop-up window will appear on the assignee's screen displaying the contact record, thus immediately displaying to that representative the full history of interaction with the customer.&lt;br /&gt;
&lt;br /&gt;
= What it requires in order to work =&lt;br /&gt;
In essence, at the most basic level, the phone system must be connected to the internet, and must be configurable such that it makes an appropriate HTTP/HTTPS request (of type &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;) to X2Engine at the right time. The procedure for implementing this customization can vary widely between phone systems, but how the essential API call must be made is the same in all cases and is described in [[Web_API_Reference_%28Legacy%29#Inbound_VoIP_call_notifications|Remote API: Inbound VoIP call notifications]].&lt;br /&gt;
&lt;br /&gt;
If the VoIP system is Asterisk-based, supports AGI interaction, and has PHP installed on it, a script included in the source code of X2Engine Professional Edition can be used instead of having to write a script from scratch. The rest of this article will cover the usage of this script.&lt;br /&gt;
&lt;br /&gt;
= Using the included script for Asterisk integration =&lt;br /&gt;
The script for Asterisk integration, named &amp;lt;tt&amp;gt;callX2Engine.php&amp;lt;/tt&amp;gt;, is included in the X2Engine source code (in Professional Edition) inside the directory &amp;lt;tt&amp;gt;protected/integration/FreePBX&amp;lt;/tt&amp;gt;. It requires the following two files be present in the same directory where it will run:&lt;br /&gt;
# The [http://phpagi.sourceforge.net/ PHPAGI] class (and with the filename &amp;lt;tt&amp;gt;phpagi.php&amp;lt;/tt&amp;gt;)&lt;br /&gt;
# A copy of the standalone APIModel class from X2Engine, which can be found in &amp;lt;tt&amp;gt;protected/models&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All three of these files (&amp;lt;tt&amp;gt;callX2Engine.php&amp;lt;/tt&amp;gt; and its dependencies) must then be placed in the &amp;lt;tt&amp;gt;agi-bin&amp;lt;/tt&amp;gt; directory on the Asterisk server to allow easily inserting it as an application call into the dialplan. &lt;br /&gt;
&lt;br /&gt;
Finally, the script itself must be configured appropriately. Near the top of the file, one will find the following global string variables that must be properly defined:&lt;br /&gt;
;&amp;lt;tt&amp;gt;$baseUrl&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The URL to the entry script (&amp;lt;tt&amp;gt;index.php&amp;lt;/tt&amp;gt;) of X2Engine. For example, if your CRM is hosted in http://example.com/crm, you will put for the value of this variable &amp;lt;tt&amp;gt;http://example.com/crm/index.php&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;$user&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The user of X2Engine to use for API authentication, when necessary.&lt;br /&gt;
;&amp;lt;tt&amp;gt;$userKey&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The API key of the X2Engine user for API authentication.&lt;br /&gt;
&lt;br /&gt;
= Case example: FreePBX integration =&lt;br /&gt;
FreePBX can be configured to use the AGI script easily, by performing the following:&lt;br /&gt;
# Upload the dialplan file &amp;lt;tt&amp;gt;extensions_custom.conf&amp;lt;/tt&amp;gt; (you'll find it in the same directory as &amp;lt;tt&amp;gt;callX2Engine.php&amp;lt;/tt&amp;gt;) to replace the existing file in the Asterisk configuration directory on the phone system. In FreePBX distro, this directory will be &amp;lt;tt&amp;gt;/etc/asterisk&amp;lt;/tt&amp;gt;.&lt;br /&gt;
# As for the PHP scripts: the &amp;lt;tt&amp;gt;agi-bin&amp;lt;/tt&amp;gt; directory, in FreePBX distro, is located at &amp;lt;tt&amp;gt;/var/lib/asterisk/agi-bin&amp;lt;/tt&amp;gt;. Note, if using FreePBX distro, you should exclude &amp;lt;tt&amp;gt;phpagi.php&amp;lt;/tt&amp;gt;, because it is already present and available in the &amp;lt;tt&amp;gt;agi-bin&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
# Change the permissions on &amp;lt;tt&amp;gt;callX2Engine.php&amp;lt;/tt&amp;gt; to be executable and readable by the system user &amp;lt;tt&amp;gt;asterisk&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== What this all does ==&lt;br /&gt;
The file &amp;lt;tt&amp;gt;extensions_custom.conf&amp;lt;/tt&amp;gt; contains the following lines:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
;; This file can be put in /etc/asterisk to configure a FreePBX-managed Asterisk system for&lt;br /&gt;
;; creating inbound call notifications in X2Engine.&lt;br /&gt;
[from-pstn-custom]&lt;br /&gt;
exten =&amp;gt; _.,1,AGI(callX2Engine.php)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
When the file is included by in FreePBX's default dialplan file (&amp;lt;tt&amp;gt;extensions.conf&amp;lt;/tt&amp;gt;), this &amp;lt;tt&amp;gt;[from-pstn-custom]&amp;lt;/tt&amp;gt; context will be &amp;quot;imported&amp;quot; into the corresponding non-custom context, &amp;lt;tt&amp;gt;[from-pstn]&amp;lt;/tt&amp;gt;. This context is entered by callers coming in from the PSTN (Public Switched Telephone Network). Thus, putting the extension that makes the AGI call in from-pstn-custom will make it so that whenever a call comes in through the SIP trunk from a &amp;quot;traditional&amp;quot; phone network, the AGI call will be made.&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Main_Page&amp;diff=1917</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Main_Page&amp;diff=1917"/>
				<updated>2014-08-07T19:24:56Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Welcome to the X2Engine Wiki, where advanced topics related to configuration, maintenance, customization and development of X2Engine are covered in-depth. For software usage documentation, see [http://www.x2engine.com/reference_guide/ The X2Engine User Reference Guide].&lt;br /&gt;
&lt;br /&gt;
The information presented in this wiki is intended for administrators, power users, IT personnel, developers, etc., but also as supplemental material to the basic user reference guide.&lt;br /&gt;
&lt;br /&gt;
For questions related to any topics not covered by this wiki or the user guide, please visit [http://x2community.com/ the forums].&lt;br /&gt;
&lt;br /&gt;
== Getting Started == &lt;br /&gt;
* [[Preparing a Linux Server Environment]]&lt;br /&gt;
* [[Installation]]&lt;br /&gt;
* [http://x2community.com The X2Engine community forums]&lt;br /&gt;
* [[Introduction To Development]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Support Topics ==&lt;br /&gt;
&amp;lt;DynamicPageList&amp;gt;&lt;br /&gt;
category = Support&lt;br /&gt;
&amp;lt;/DynamicPageList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Development Topics ==&lt;br /&gt;
&amp;lt;DynamicPageList&amp;gt;&lt;br /&gt;
category = Development&lt;br /&gt;
&amp;lt;/DynamicPageList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Using This Wiki ==&lt;br /&gt;
For information on using the wiki software, consult the [//meta.wikimedia.org/wiki/Help:Contents User's Guide] and the [//www.mediawiki.org/wiki/Manual:FAQ MediaWiki FAQ]&lt;br /&gt;
[https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki release mailing list]&lt;br /&gt;
&lt;br /&gt;
=== Special Extensions in use ===&lt;br /&gt;
See their respective documentation for more information on their use;&lt;br /&gt;
* [[Mw:Extension:Cite|Cite]]: For creating footnotes&lt;br /&gt;
* [[Mw:Extension:ConfirmAccount|ConfirmAccount]]: Restricts page edit/create permissions to moderator-approved accounts&lt;br /&gt;
* [[Mw:Extension:Gadgets|Gadgets]]: Includes custom Javascript/CSS in pages&lt;br /&gt;
* [[Mw:Extension:Lingo|Lingo]]: Glossary; automatically provides tooltips for keywords defined on the [[Terminology]] page.&lt;br /&gt;
* [[Mw:Extension:Nuke|Nuke]]: Provides the ability to delete accounts and also bulk-delete all pages by them &lt;br /&gt;
* [[Mw:Extension:ParserFunctions|ParserFunctions]]&lt;br /&gt;
* [[Mw:Extension:Renameuser|Renameuser]]: Provides the ability to rename a user&lt;br /&gt;
* [[Mw:Extension:SyntaxHighlight_GeSHi|SyntaxHighlight GeSHi]]: Code syntax highlighting&lt;br /&gt;
&lt;br /&gt;
=== Brief Introductory Usage Notes ===&lt;br /&gt;
* [[Wiki Usage:For contributors]]&lt;br /&gt;
* [[Wiki Usage:For administrators]]&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Wiki_Usage:For_administrators&amp;diff=1916</id>
		<title>Wiki Usage:For administrators</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Wiki_Usage:For_administrators&amp;diff=1916"/>
				<updated>2014-08-07T19:23:32Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Miscellaneous */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Wiki Usage]]&lt;br /&gt;
&lt;br /&gt;
= Editing the UI =&lt;br /&gt;
;Edit the sidebar&lt;br /&gt;
: [[Mediawiki:Sidebar]]&lt;br /&gt;
;Edit the site notice (the text displayed above the content of every page)&lt;br /&gt;
: [[Mediawiki:Sitenotice]]&lt;br /&gt;
;The above pages, and more, for modifying the layout and UI content&lt;br /&gt;
: [[mw:Category:Interface_messages]]&lt;br /&gt;
iw_local, iw_trans) VALUES (&amp;quot;x2&amp;quot;, &amp;quot;http://www.x2engine.com/$1&amp;quot;, 1, 0);&amp;lt;/syntaxhighlight&amp;gt; Then, a link to the &amp;quot;about&amp;quot; page: &amp;lt;pre&amp;gt;[[x2:partners]]&amp;lt;/pre&amp;gt; For more information, see [[mw:Help:Interwiki]]&lt;br /&gt;
;Add custom HTML to the template&lt;br /&gt;
: Edit &amp;lt;TT&amp;gt;VectorTemplate::execute()&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;public_html/skins/Vector.php&amp;lt;/tt&amp;gt; (a backup of this skin has been made in &amp;lt;tt&amp;gt;Backup/Vector&amp;lt;/tt&amp;gt;, in case anything should go wrong). The body begins after these lines: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
                // Output HTML Page&lt;br /&gt;
                $this-&amp;gt;html( 'headelement' );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= User Management =&lt;br /&gt;
The following two plugins are enabled for user management:&lt;br /&gt;
* [http://www.mediawiki.org/wiki/Extension:ConfirmAccount ConfirmAccount]&lt;br /&gt;
* [http://www.mediawiki.org/wiki/Extension:Nuke Nuke]&lt;br /&gt;
These special pages are of use:&lt;br /&gt;
* [[Special:Block|Block users]]&lt;br /&gt;
* [[Special:Nuke|Mass-delete all pages by any user]]&lt;br /&gt;
* [[Special:ConfirmAccounts|Confirm account requests]]&lt;br /&gt;
* [[Special:UserRights|User Rights]]&lt;br /&gt;
&lt;br /&gt;
= Miscellaneous =&lt;br /&gt;
;Edit global CSS for all pages&lt;br /&gt;
: [[MediaWiki:Common.css]]&lt;br /&gt;
;Change the syntax highlighting CSS&lt;br /&gt;
: [[MediaWiki:Geshi.css]]&lt;br /&gt;
;Purging a cached page (i.e. if the sidebar or site notice is out of date)&lt;br /&gt;
: Add &amp;quot;&amp;amp;action=purge&amp;quot; to the end of the page's url and re-request the page.&lt;br /&gt;
;Globally purging all cached pages and objects (more useful but more involved)&lt;br /&gt;
: Log into the server via SSH and run &amp;lt;tt&amp;gt;clearCache.sh&amp;lt;/tt&amp;gt;; that should change the modification time on LocalSettings.php, which will, due how the dynamic $wgCacheEpoch variable is set, invalidate all cached pages and thus regenerate them. Said variable's declaration is at the end of the file: &lt;br /&gt;
: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$wgCacheEpoch = gmdate( 'YmdHis', @filemtime( __FILE__ ) );&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;Create a URL shorthand for linking to another site&lt;br /&gt;
: For example, one has been made for www.x2engine.com so that the &amp;quot;About X2Engine&amp;quot; in the footer could be made a link to the main X2Engine site: &lt;br /&gt;
: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;INSERT INTO interwiki (iw_prefix, iw_url, iw_local, iw_trans) VALUES (&amp;quot;x2&amp;quot;, &amp;quot;http://www.x2engine.com/$1&amp;quot;, 1, 0);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
: That interwiki entry transforms (for example) the code &amp;lt;pre&amp;gt;[[x2:partners/|About X2Engine]]&amp;lt;/pre&amp;gt; into &amp;lt;syntaxhighlight lang=&amp;quot;html5&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;http://www.x2engine.com/partners/&amp;quot;&amp;gt;About X2Engine&amp;lt;/a&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Preparing_a_Linux_Server_Environment&amp;diff=1915</id>
		<title>Preparing a Linux Server Environment</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Preparing_a_Linux_Server_Environment&amp;diff=1915"/>
				<updated>2014-08-04T18:52:01Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Protecting ~/.ssh */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
&lt;br /&gt;
This aims to be a generic guide for manually preparing a Linux-based web server, which applies both to physical servers and virtual servers, i.e. Amazon EC2 instances. It will cover two main families of Linux distributions: RHEL (Red Hat Enterprise Linux) based and Debian-based. RHEL-based distributions include, most notably: CentOS, Oracle Linux and Amazon Linux. Debian-based distributions include the popular Ubuntu and Ubuntu derivatives, as well as Linux Mint, Knopper Linux and many others&amp;lt;ref&amp;gt;See: [http://wiki.debian.org/Derivatives/Census|the Debian Derivatives Census]&amp;lt;/ref&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
It will be assumed in this guide, unless otherwise noted: &lt;br /&gt;
* Command line operations are being performed as the root user&lt;br /&gt;
* Names enclosed in angle brackets (&amp;amp;lt; &amp;amp;gt;) are intended as placeholders for actual input and should not be used verbatim&lt;br /&gt;
* The server is ''dedicated,'' i.e. will be used mainly for X2CRM on a single domain name, and will not be set up with multiple virtual hosts.&lt;br /&gt;
* The Linux system's ''[http://en.wikipedia.org/wiki/Package_manager software package manager]'' has been configured properly&lt;br /&gt;
* Software will be installed using pre-built packages available through the particular distribution's software package repositories.&lt;br /&gt;
* A distribution of Linux based on Red Hat Enterprise Linux or Debian is in use.&lt;br /&gt;
&lt;br /&gt;
'''When installing packages, note:''' there may be slight differences in the names of software packages between distributions of Linux. Thus, in cases where a package name copied verbatim from this guide does not exist, the package manager's ''search'' utility should be used to determine the proper package name.&lt;br /&gt;
&lt;br /&gt;
The command for searching for packages by name is as follows:&lt;br /&gt;
&amp;lt;pre&amp;gt;apt-cache search &amp;lt;package name&amp;gt; # (Debian-like)&lt;br /&gt;
yum search &amp;lt;package name&amp;gt; # (RHEL-like)&amp;lt;/pre&amp;gt;&lt;br /&gt;
In each case, use parts of the full name to increase the likelihood of getting search results. Note that Debian's &amp;lt;tt&amp;gt;apt-cache search&amp;lt;/tt&amp;gt; utility will accept regular expressions (if quoted properly). To search for packages on RHEL-based systems with regular expressions, search results from &amp;lt;tt&amp;gt;yum&amp;lt;/tt&amp;gt; can be simply piped to grep:&lt;br /&gt;
&amp;lt;pre&amp;gt;yum search &amp;lt;package name&amp;gt; | grep &amp;lt;package regex&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Installing the Web Server =&lt;br /&gt;
== Acquiring Software Packages ==&lt;br /&gt;
To begin, refresh the software package database:&lt;br /&gt;
&amp;lt;pre&amp;gt;apt-get update # (Debian-like)&lt;br /&gt;
yum update # (RHEL-like)&amp;lt;/pre&amp;gt;&lt;br /&gt;
There may also be software updates. If any are available, &amp;lt;tt&amp;gt;yum&amp;lt;/tt&amp;gt; will download prompt you if you want to install them. Debian's apt, on the other hand, only updates the package databases. To install software updates, if any are available, run &amp;lt;tt&amp;gt;apt-get upgrade&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Next install Apache, PHP and MySQL. '''Debian-based systems''' should have a utility called &amp;quot;tasksel&amp;quot; that can begin installation of all the necessary packages for a LAMP (Linux, Apache, MySQL &amp;amp; PHP) server in one command:&lt;br /&gt;
&amp;lt;pre&amp;gt;tasksel install lamp-server&amp;lt;/pre&amp;gt;&lt;br /&gt;
However, this task usually does not include the package for the PHP cURL extension, so it must be installed separately via &amp;lt;pre&amp;gt;apt-get install php5-curl&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On '''RHEL-based systems:''' run&lt;br /&gt;
&amp;lt;pre&amp;gt;yum install mysql mysql-server php-mysql php httpd php-mbstring php-curl&amp;lt;/pre&amp;gt;&lt;br /&gt;
''On many RHEL-based systems,'' PHP will be built without POSIX support (the compile flag '--disable-posix' should appear in the &amp;quot;Configure Command&amp;quot; section of &amp;lt;tt&amp;gt;phpinfo()&amp;lt;/tt&amp;gt; ). Thus, the requirements checker script will not display the proper warning when the apache UID doesn't match the ownership of the web document root. Although it is not necessary (the next steps will ensure that this requirement is met anyway), the POSIX extension can be added if desired by installing the package &amp;lt;tt&amp;gt;php-process&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;php-posix&amp;lt;/tt&amp;gt; (depending on the distribution and release version).&lt;br /&gt;
&lt;br /&gt;
There are two more optional but useful PHP extensions: APC (caching extension) and Suhosin (PHP security). These packages should be available in most Debian-based distributions and can be installed via &amp;lt;tt&amp;gt;apt-get install php5-suhosin php-apc&amp;lt;/tt&amp;gt;. In CentOS, they can be acquired by enabling the EPEL software repository &amp;lt;ref&amp;gt;[http://www.cyberciti.biz/faq/fedora-sl-centos-redhat6-enable-epel-repo/ CentOS / RHEL / Scientific Linux 6 Enable (Install) EPEL Repo]&amp;lt;/ref&amp;gt; and then running &amp;lt;tt&amp;gt;yum install php-suhosin php-pecl-apc&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Starting the Server ==&lt;br /&gt;
To start the server, run:&lt;br /&gt;
&amp;lt;pre&amp;gt;service apache2 start # (Debian-like)&lt;br /&gt;
service httpd start # (RHEL-like)&amp;lt;/pre&amp;gt;&lt;br /&gt;
Navigate to the server with a web browser to verify that the web server is running and responding to requests on its IP address / fully-qualified domain name.&lt;br /&gt;
&lt;br /&gt;
== Assigning a Domain Name ==&lt;br /&gt;
Due to the great diversity of DNS services and tools, the exact procedure for associating a domain name with the web server is beyond the scope of this guide. However, the most important facts are:&lt;br /&gt;
* To direct a domain name to an IP address, use the &amp;lt;tt&amp;gt;A&amp;lt;/tt&amp;gt; type DNS record.&lt;br /&gt;
* To direct a domain name to another domain name, use the &amp;lt;tt&amp;gt;CNAME&amp;lt;/tt&amp;gt; type DNS record. &lt;br /&gt;
For instance, given the hostname of an Amazon Web Services EC2 instance, one would associate a domain name with it via a &amp;lt;TT&amp;gt;CNAME&amp;lt;/tt&amp;gt; record (to ensure proper resolution).&lt;br /&gt;
&lt;br /&gt;
= Server Preparation =&lt;br /&gt;
The default [http://httpd.apache.org/docs/current/mod/core.html#documentroot DocumentRoot] of Apache Webserver on most distributions of Linux will be owned by the &amp;lt;tt&amp;gt;root&amp;lt;/tt&amp;gt; user immediately after installation, whereas its default [http://httpd.apache.org/docs/current/mod/mod_unixd.html#user User] and [http://httpd.apache.org/docs/current/mod/mod_unixd.html#group Group] will be set to &amp;quot;www-data&amp;quot; (Debian) or &amp;quot;apache&amp;quot; (RHEL). This configuration makes it so that no web application whatsoever will have write permissions to the directory it runs in (which is problematic). Thus, the first step of preparation once the server has been installed and started is to ensure the ownership of the files and Apache process are the same.&lt;br /&gt;
&lt;br /&gt;
== Determining the DocumentRoot ==&lt;br /&gt;
This is a rather important aspect of a web server is where the files to be served are stored in the local filesystem, and will be necessary in the next steps of this process. To obtain it, search for the DocumentRoot directive in the configuration as follows:&lt;br /&gt;
&amp;lt;pre&amp;gt;grep -r '^ DocumentRoot' /etc/apache2/ # (Debian-like)&lt;br /&gt;
grep -r '^ DocumentRoot' /etc/httpd/ # (RHEL-like)&amp;lt;/pre&amp;gt;&lt;br /&gt;
In most cases, the directive will be set to &amp;lt;tt&amp;gt;/var/www&amp;lt;/tt&amp;gt; on Debian-based distributions and &amp;lt;tt&amp;gt;/var/www/html&amp;lt;/tt&amp;gt; on RHEL-based ones.&lt;br /&gt;
&lt;br /&gt;
== Determining the Apache User/Group ==&lt;br /&gt;
On Debian-based systems, these directives are typically set to &amp;quot;www-data&amp;quot;, and on RHEL-based systems, to &amp;quot;apache&amp;quot;. However, if in doubt, there are at least three methods of finding out the values to which the User/Group directives set.&lt;br /&gt;
&lt;br /&gt;
=== Direct Method ===&lt;br /&gt;
The most reliable way of finding out what they are is by searching the configuration files for the directives:&lt;br /&gt;
&amp;lt;pre&amp;gt;grep -r '^ *\(User\|Group\)' /etc/apache2/ # (Debian-like)&lt;br /&gt;
grep -r '^ *\(User\|Group\)' /etc/httpd/ # (RHEL-like)&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note that on Debian-based systems you may see that, in place of literal strings, there are references to environment variables, i.e. &amp;lt;tt&amp;gt;${APACHE_RUN_USER}&amp;lt;/tt&amp;gt;. To find their actual values, search for them in the same manner:&lt;br /&gt;
&amp;lt;pre&amp;gt;grep -r '^ *export *APACHE_RUN_' /etc/apache2/&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Using ps ===&lt;br /&gt;
One can search for the process and see its user, if Apache is already running, using &amp;lt;tt&amp;gt;ps&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;ps aux | grep '\(apache|httpd\)'&amp;lt;/pre&amp;gt;&lt;br /&gt;
The first column of output should be the user name or id of the running process, whereas the last should be the executable file of the process. Locate the web server process (the path to the executable should be something like &amp;lt;tt&amp;gt;/usr/sbin/apache2&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;/usr/sbin/httpd&amp;lt;/tt&amp;gt;). &lt;br /&gt;
&lt;br /&gt;
=== Using PHP's POSIX Library ===&lt;br /&gt;
If the POSIX library is available in PHP, you can find the UID by creating a PHP script in the [http://httpd.apache.org/docs/current/mod/core.html#documentroot DocumentRoot] with the following contents and opening it in a web browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php &lt;br /&gt;
echo 'UID (Server): '.posix_geteuid().'&amp;lt;br&amp;gt;';&lt;br /&gt;
echo 'UID (Directory owner): '.fileowner(realpath(dirname(__FILE__)));&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If the web server is properly set up, both of the numbers displayed should be equal.&lt;br /&gt;
&lt;br /&gt;
== Changing DocumentRoot Ownership ==&lt;br /&gt;
Once the UID of the webserver has been determined, change the ownership of the public web directory as follows:&lt;br /&gt;
&amp;lt;pre&amp;gt;chown -R &amp;lt;user&amp;gt;:&amp;lt;group&amp;gt; &amp;lt;DocumentRoot&amp;gt; # (Debian-like)&lt;br /&gt;
chown -R &amp;lt;user&amp;gt;:&amp;lt;group&amp;gt; &amp;lt;DocumentRoot&amp;gt; # (RHEL-like)&amp;lt;/pre&amp;gt;&lt;br /&gt;
Use the user name or ID of the running webserver process in place of &amp;lt;tt&amp;gt;&amp;amp;lt;user&amp;amp;gt;&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;&amp;amp;lt;group&amp;amp;gt;&amp;lt;/tt&amp;gt;, and the server's DocumentRoot  in place of &amp;lt;tt&amp;gt;&amp;amp;lt;DocumentRoot&amp;amp;gt;&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Testing the Webserver ==&lt;br /&gt;
When done, download the requirements checking script: &lt;br /&gt;
&amp;lt;pre&amp;gt;cd &amp;lt;DocumentRoot&amp;gt;&lt;br /&gt;
wget http://x2planet.com/installs/download/requirements.php&lt;br /&gt;
chown &amp;lt;apache user&amp;gt;:&amp;lt;apache user&amp;gt; requirements.php&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next, open the file in a web browser to verify that the system meets all of the requirements. Note that, if you see no &amp;quot;posix&amp;quot; section in the phpinfo() output and the flag &amp;quot;--disable-posix&amp;quot; appears in the configure command, and you skipped [[#Changing DocumentRoot Ownership|changing ownership]], the results of the requirements check will not be accurate.&lt;br /&gt;
&lt;br /&gt;
= Preparing a Database =&lt;br /&gt;
X2CRM needs a MySQL database for storing persistent data. To prepare this final requirement, you will first need to start the MySQL server daemon:&lt;br /&gt;
&amp;lt;pre&amp;gt;service mysql start # (Debian-like)&lt;br /&gt;
service mysqld start # (RHEL-like)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next, log into the database server as the database administrator (typically root).&lt;br /&gt;
&amp;lt;pre&amp;gt;mysql -u root -p&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may have to enter a password. On Debian systems, during the installation of the MySQL server, you should have been prompted for a root password, and thus you will need to use it for logging in. On Amazon Linux and some other RHEL derivatives, the root password won't be set, and so you can log in without the &amp;lt;tt&amp;gt;-p&amp;lt;/tt&amp;gt; flag.&lt;br /&gt;
&lt;br /&gt;
Once you have logged in, create the database, and create a non-admin user with full access to it. The database name &amp;quot;x2crm&amp;quot; and user name &amp;quot;x2crmuser&amp;quot; will be used in this example.&lt;br /&gt;
&amp;lt;pre&amp;gt;mysql&amp;gt; CREATE DATABASE x2crm;&lt;br /&gt;
Query OK, 1 row affected (0.00 sec)&lt;br /&gt;
&lt;br /&gt;
mysql&amp;gt; CREATE USER x2crmuser@localhost IDENTIFIED BY '&amp;lt;password&amp;gt;';&lt;br /&gt;
Query OK, 0 rows affected (0.00 sec)&lt;br /&gt;
&lt;br /&gt;
mysql&amp;gt; GRANT ALL ON x2crm.* TO x2crmuser@localhost;&lt;br /&gt;
Query OK, 0 rows affected (0.00 sec)&lt;br /&gt;
&lt;br /&gt;
mysql&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you install X2CRM on the server, user &amp;quot;localhost&amp;quot; in the database &amp;quot;Host Name&amp;quot; settings, the database name you used in place of &amp;quot;x2crm&amp;quot;, and the username/password you used in place of &amp;quot;x2crmuser&amp;quot; and &amp;quot;&amp;lt;password&amp;gt;.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Finally, if the root user on your MySQL server does not have a password set, it is a very good idea security-wise to set a password to protect the server. Do so using the following command (from the system shell, logged out of &amp;lt;tt&amp;gt;mysql&amp;gt;&amp;lt;/tt&amp;gt;):&lt;br /&gt;
&amp;lt;pre&amp;gt;mysqladmin -u root password &amp;lt;newpassword&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Enabling Login as the Webserver User =&lt;br /&gt;
To greatly ease the process of installing and maintaining X2CRM, the default Apache web user (assumed &amp;lt;tt&amp;gt;www-data&amp;lt;/tt&amp;gt; in the Debian family and &amp;lt;tt&amp;gt;apache&amp;lt;/tt&amp;gt; in the RHEL family, and denoted &amp;lt;tt&amp;gt;&amp;amp;lt;apache user&amp;amp;gt;&amp;lt;/tt&amp;gt; in the general case) can also be used for logging into the server and uploading / altering files, thus avoiding all permissions issues. In many cases, the ability to log in as the web server user will be disabled for security purposes. This is mostly due to how the usernames are easy to guess in brute-force remote login attempts, although this problem can be circumvented. See [[#Securing Login|Securing Login]] for details.&lt;br /&gt;
&lt;br /&gt;
== Setting the Shell ==&lt;br /&gt;
In most cases, login as the user is disabled via the shell environment variable. To check that this is so, use the &amp;lt;tt&amp;gt;getent&amp;lt;/tt&amp;gt; command as follows:&lt;br /&gt;
&amp;lt;pre&amp;gt;getent passwd &amp;lt;apache user&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should see output that looks like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;www-data:x:33:33:www-data:/var/www:/bin/false&amp;lt;/pre&amp;gt;&lt;br /&gt;
Or:&lt;br /&gt;
&amp;lt;pre&amp;gt;apache:x:48:48:Apache:/var/www:/sbin/nologin&amp;lt;/pre&amp;gt;&lt;br /&gt;
In both cases, the last field in the (colon-delinated) line indicates the login shell; &amp;lt;tt&amp;gt;/bin/false&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;/sbin/nologin&amp;lt;/tt&amp;gt; each make it impossible to run a shell as the user. Note also that the second-to-last field is the home directory of the user. If the field is blank (i.e. there are two colons side-by-side before the login shell) it is recommended at this stage to set the home directory of the user as follows:&lt;br /&gt;
&amp;lt;pre&amp;gt;usermod -d &amp;lt;DocumentRoot&amp;gt; &amp;lt;apache user&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
If that command returns the error message that the user is currently logged in, try again after shutting down the web server as follows:&lt;br /&gt;
&amp;lt;pre&amp;gt;service apache2 stop # (Debian-like)&lt;br /&gt;
service httpd stop # (RHEL-like)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally, to change the login shell, use &amp;lt;tt&amp;gt;chsh&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;chsh &amp;lt;apache user&amp;gt; -s /bin/bash&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Securing Login ==&lt;br /&gt;
The risk of brute-force logins can be avoided by ensuring that the main method of logging into the server and transferring files is through the secure shell daemon (SSHD), and that SSHD is configured to disallow password-based logins. SSHD will then require asymmetric key pairs to log in instead of passwords, thus making brute-force password attempts a non-issue. '''If your server is an Amazon Web Services EC2 instance, this will be the case already;''' the key pair is selected/generated before instantiation as the primary means of accessing the virtual server.&lt;br /&gt;
&lt;br /&gt;
=== Configuring SSH For Key-based Authentication ===&lt;br /&gt;
On a generic Linux server:&lt;br /&gt;
# Begin a shell session as the web server user: &amp;lt;pre&amp;gt;sudo -u &amp;lt;apache user&amp;gt; -i&amp;lt;/pre&amp;gt; All of the following commands will be run as this user.&lt;br /&gt;
# Create a &amp;quot;.ssh&amp;quot; directory in the user's home: &amp;lt;pre&amp;gt;mkdir -p ~/.ssh&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Ensure the directory has the proper permissions: &amp;lt;pre&amp;gt;chmod 700 ~/.ssh&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Run: &amp;lt;pre&amp;gt;ssh-keygen -t rsa&amp;lt;/pre&amp;gt; and (optionally) enter a passphrase to protect the key. The keypair, when done, should be stored as the files &amp;lt;tt&amp;gt;~/.ssh/id_rsa&amp;lt;/tt&amp;gt; (private) and &amp;lt;tt&amp;gt;~/.ssh/id_rsa.pub&amp;lt;/tt&amp;gt; (public). &lt;br /&gt;
# Create the authorized_keys file with the public key to enable authenticating with the private key on the remote end: &amp;lt;pre&amp;gt;cat ~/.ssh/id_rsa.pub &amp;gt;&amp;gt; ~/.ssh/authorized_keys&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Ensure the authorized_keys file has the proper permissions: &amp;lt;pre&amp;gt;chmod 600 ~/.ssh/authorized_keys&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Download the private key (&amp;lt;tt&amp;gt;~/.ssh/id_rsa&amp;lt;/tt&amp;gt;) and delete it from the server as soon as possible.&lt;br /&gt;
&lt;br /&gt;
'''If the server is an Amazon EC2 instance,''' it is not necessary to create a new key pair; the existing key pair can be used for authentication, if desired, by using the existing authorized_hosts file in the &amp;lt;tt&amp;gt;.ssh&amp;lt;/tt&amp;gt; directory of &amp;quot;ec2-user&amp;quot; (Amazon Linux) or &amp;quot;ubuntu&amp;quot; (Ubuntu) into the &amp;lt;tt&amp;gt;.ssh/&amp;lt;/tt&amp;gt;, as follows:&lt;br /&gt;
# Make the directory and copy the files: &amp;lt;pre&amp;gt;mkdir -p ~&amp;lt;apache user&amp;gt;/.ssh; cp ~&amp;lt;default user&amp;gt;/.ssh/authorized_keys ~&amp;lt;apache user&amp;gt;/.ssh/&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Ensure proper user/group ownership of the files: &amp;lt;pre&amp;gt;chown -R &amp;lt;apache user&amp;gt;:&amp;lt;apache group&amp;gt; ~&amp;lt;apache user&amp;gt;/.ssh&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Ensure proper permissions of the files: &amp;lt;pre&amp;gt;chmod 700 ~&amp;lt;apache user&amp;gt;/.ssh; chmod 600 ~&amp;lt;apache user&amp;gt;/.ssh/*&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note that you may still have to protect the &amp;lt;tt&amp;gt;.ssh&amp;lt;/tt&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Protecting ~/.ssh ===&lt;br /&gt;
If the user's home directory is the same as (or lies within) the web server's DocumentRoot (this is the case for the default LAMP configuration on Ubuntu 12.04), it is generally a very good idea to prevent all HTTP access to the directory storing the public key, regardless of whether the key pair is password-protected. The most straightforward way of doing this is by editing the main Apache configuration (&amp;lt;tt&amp;gt;/etc/apache2/apache2.conf&amp;lt;/tt&amp;gt; Debian, &amp;lt;tt&amp;gt;/etc/http/conf/httpd.conf&amp;lt;/tt&amp;gt; RHEL ) and adding the following lines to it (for Apache versions 2.0-2.2):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;apache&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;Directory /var/www/.ssh&amp;gt;&lt;br /&gt;
Order allow,deny&lt;br /&gt;
Deny from all&lt;br /&gt;
&amp;lt;/Directory&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If using Apache 2.4 or later, you can use the non-deprecated configuration directive &amp;quot;Require&amp;quot;&amp;lt;ref&amp;gt;See: [http://httpd.apache.org/docs/2.4/howto/access.html Apache 2.4 Documentation: Access Control]&amp;lt;/ref&amp;gt; as follows :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;apache&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;Directory /var/www/.ssh&amp;gt;&lt;br /&gt;
Require All Denied&lt;br /&gt;
&amp;lt;/Directory&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In both of these examples, it is assumed that the home directory is &amp;lt;tt&amp;gt;/var/www&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
For extra privacy and security, it may also be desirable to deny access to all hidden dotfiles and directories used by the user shell and local programs (i.e. &amp;lt;tt&amp;gt;.bashrc&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;.bash_history&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;.viminfo&amp;lt;/tt&amp;gt; etc.), in which case you can use the following directive:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;apache&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;DirectoryMatch /var/www/\..*&amp;gt;&lt;br /&gt;
# Denial directives i.e. &amp;quot;Require All Denied&amp;quot; go here&lt;br /&gt;
&amp;lt;/DirectoryMatch&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Disabling Password-based Authentication ===&lt;br /&gt;
To verify that password authentication is disabled, search for the directive in the SSHD configuration (which should be in the same place on almost every Linux and Unix system&amp;lt;ref&amp;gt;Assuming the ubiquitous [http://www.openssh.com/ OpenSSH] server is the software in use. All RHEL-based and Debian-based Linux systems, as well as FreeBSD, use OpenSSH as their chosen SSH server, and as of this writing none of the mainstream distributions are known to build OpenSSH such that the path to the daemon's configuration file is any different.&amp;lt;/ref&amp;gt;):&lt;br /&gt;
&amp;lt;pre&amp;gt;grep '^PasswordAuthentication' /etc/ssh/sshd_config&amp;lt;/pre&amp;gt;&lt;br /&gt;
In the output of &amp;lt;tt&amp;gt;grep&amp;lt;/tt&amp;gt; you should see this:&lt;br /&gt;
&amp;lt;pre&amp;gt;PasswordAuthentication no&amp;lt;/pre&amp;gt;&lt;br /&gt;
If this is not the case, i.e. if it reads &amp;quot;yes&amp;quot; instead of &amp;quot;no&amp;quot;, or the only instance of &amp;quot;PasswordAuthentication&amp;quot; is commented in the file by having its line begin with a hash symbol (&amp;quot;#&amp;quot;), edit the file &amp;lt;tt&amp;gt;/etc/ssh/sshd_config&amp;lt;/tt&amp;gt; so that it contains a line that reads &amp;lt;tt&amp;gt;PasswordAuthentication no&amp;lt;/tt&amp;gt; and then restart the SSH daemon to enact the changes via &amp;lt;tt&amp;gt;service ssh stop&amp;lt;/tt&amp;gt; (Debian) or &amp;lt;tt&amp;gt;service sshd stop&amp;lt;/tt&amp;gt; (RHEL). However, '''before doing this,''' ensure that the primary administrative user already has a key pair set up for remote access; you will otherwise be locked out of your server. To do this, the same method that was used for creating a key pair for the web server user can be used to do so for the administrative user.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Notes &amp;amp;amp; References =&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1914</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1914"/>
				<updated>2014-08-02T00:51:45Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* HTTP Response Codes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module, by going to the edit page for that user. To summarize:&lt;br /&gt;
# As the administrator, go to the user management module. You'll find it under &amp;quot;Users&amp;quot;&lt;br /&gt;
# Click on the desired user&lt;br /&gt;
# Click &amp;quot;Update User&amp;quot;&lt;br /&gt;
# See the &amp;quot;API Key&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Caveats ===&lt;br /&gt;
'''Note, firstly:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
Furthermore, if you have password-protected any web directories that contain X2Engine, you will need to make a &amp;quot;Satisfy any&amp;quot; exception for URIs within the API, or clients may not be able to authenticate.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow; effectively, the comparison in the query is being performed on a &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt; column in a joined table&lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2Leads&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type can be fully accessed and manipulated, but there is not yet any built-in action available for directly converting a lead to an opportunity.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Direct Manipulation by Attributes ==&lt;br /&gt;
(Coming in 4.2)&lt;br /&gt;
&lt;br /&gt;
Given a set of uniquely-identifying attribute names and values, it is possible to directly access and manipulate an existing X2Model-based record by without first querying it. The direct URI for this use case is:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/{_class}/by:{_findBy}.json&lt;br /&gt;
&lt;br /&gt;
where the query parameter &amp;lt;tt&amp;gt;{_findBy}&amp;lt;/tt&amp;gt; is a list of key and value pairs formatted as &amp;lt;tt&amp;gt;key1=value1;key2=value2&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
For example: provided the email address &amp;quot;james@example.com&amp;quot; and first name &amp;quot;James&amp;quot; uniquely identify the contact with ID of 457, the following two direct access URIs are equivalent:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/Contacts/by:email=james@example.com;firstName=James.json&lt;br /&gt;
  index.php/api2/Contacts/457.json&lt;br /&gt;
&lt;br /&gt;
Furthermore, note what happens in the following scenarios:&lt;br /&gt;
&lt;br /&gt;
'''If the set of attributes (i.e. email address) is not unique to a single record:''' the find-by-attributes direct URI will return with a 300 status and an [[#Error Objects|error object]] containing the two special properties: ''queryUri'', the URI to query for records by the attributes given, and: ''directUris'', a list containing the direct URI of each matching record.&lt;br /&gt;
&lt;br /&gt;
''' If no contact matches the attributes given:''' the URI will respond with a 404 status.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;300&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When using the &amp;quot;[[#Direct Manipulation by Attributes|find by attributes]]&amp;quot; direct access URI&lt;br /&gt;
|''Multiple choices.'' There criteria specified for direct access (as opposed to querying) match more than one record.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1913</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1913"/>
				<updated>2014-08-02T00:50:55Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* HTTP Response Codes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module, by going to the edit page for that user. To summarize:&lt;br /&gt;
# As the administrator, go to the user management module. You'll find it under &amp;quot;Users&amp;quot;&lt;br /&gt;
# Click on the desired user&lt;br /&gt;
# Click &amp;quot;Update User&amp;quot;&lt;br /&gt;
# See the &amp;quot;API Key&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Caveats ===&lt;br /&gt;
'''Note, firstly:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
Furthermore, if you have password-protected any web directories that contain X2Engine, you will need to make a &amp;quot;Satisfy any&amp;quot; exception for URIs within the API, or clients may not be able to authenticate.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow; effectively, the comparison in the query is being performed on a &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt; column in a joined table&lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2Leads&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type can be fully accessed and manipulated, but there is not yet any built-in action available for directly converting a lead to an opportunity.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Direct Manipulation by Attributes ==&lt;br /&gt;
(Coming in 4.2)&lt;br /&gt;
&lt;br /&gt;
Given a set of uniquely-identifying attribute names and values, it is possible to directly access and manipulate an existing X2Model-based record by without first querying it. The direct URI for this use case is:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/{_class}/by:{_findBy}.json&lt;br /&gt;
&lt;br /&gt;
where the query parameter &amp;lt;tt&amp;gt;{_findBy}&amp;lt;/tt&amp;gt; is a list of key and value pairs formatted as &amp;lt;tt&amp;gt;key1=value1;key2=value2&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
For example: provided the email address &amp;quot;james@example.com&amp;quot; and first name &amp;quot;James&amp;quot; uniquely identify the contact with ID of 457, the following two direct access URIs are equivalent:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/Contacts/by:email=james@example.com;firstName=James.json&lt;br /&gt;
  index.php/api2/Contacts/457.json&lt;br /&gt;
&lt;br /&gt;
Furthermore, note what happens in the following scenarios:&lt;br /&gt;
&lt;br /&gt;
'''If the set of attributes (i.e. email address) is not unique to a single record:''' the find-by-attributes direct URI will return with a 300 status and an [[#Error Objects|error object]] containing the two special properties: ''queryUri'', the URI to query for records by the attributes given, and: ''directUris'', a list containing the direct URI of each matching record.&lt;br /&gt;
&lt;br /&gt;
''' If no contact matches the attributes given:''' the URI will respond with a 404 status.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;300&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When using the &amp;quot;[[#Direct Manipulation by Attributes|find by attributes]]&amp;quot; direct access URI&lt;br /&gt;
|''Multiple choices.'' There criteria specified do not uniquely identify any given record.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1912</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1912"/>
				<updated>2014-08-02T00:47:01Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Direct Manipulation by Attributes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module, by going to the edit page for that user. To summarize:&lt;br /&gt;
# As the administrator, go to the user management module. You'll find it under &amp;quot;Users&amp;quot;&lt;br /&gt;
# Click on the desired user&lt;br /&gt;
# Click &amp;quot;Update User&amp;quot;&lt;br /&gt;
# See the &amp;quot;API Key&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Caveats ===&lt;br /&gt;
'''Note, firstly:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
Furthermore, if you have password-protected any web directories that contain X2Engine, you will need to make a &amp;quot;Satisfy any&amp;quot; exception for URIs within the API, or clients may not be able to authenticate.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow; effectively, the comparison in the query is being performed on a &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt; column in a joined table&lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2Leads&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type can be fully accessed and manipulated, but there is not yet any built-in action available for directly converting a lead to an opportunity.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Direct Manipulation by Attributes ==&lt;br /&gt;
(Coming in 4.2)&lt;br /&gt;
&lt;br /&gt;
Given a set of uniquely-identifying attribute names and values, it is possible to directly access and manipulate an existing X2Model-based record by without first querying it. The direct URI for this use case is:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/{_class}/by:{_findBy}.json&lt;br /&gt;
&lt;br /&gt;
where the query parameter &amp;lt;tt&amp;gt;{_findBy}&amp;lt;/tt&amp;gt; is a list of key and value pairs formatted as &amp;lt;tt&amp;gt;key1=value1;key2=value2&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
For example: provided the email address &amp;quot;james@example.com&amp;quot; and first name &amp;quot;James&amp;quot; uniquely identify the contact with ID of 457, the following two direct access URIs are equivalent:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/Contacts/by:email=james@example.com;firstName=James.json&lt;br /&gt;
  index.php/api2/Contacts/457.json&lt;br /&gt;
&lt;br /&gt;
Furthermore, note what happens in the following scenarios:&lt;br /&gt;
&lt;br /&gt;
'''If the set of attributes (i.e. email address) is not unique to a single record:''' the find-by-attributes direct URI will return with a 300 status and an [[#Error Objects|error object]] containing the two special properties: ''queryUri'', the URI to query for records by the attributes given, and: ''directUris'', a list containing the direct URI of each matching record.&lt;br /&gt;
&lt;br /&gt;
''' If no contact matches the attributes given:''' the URI will respond with a 404 status.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1911</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1911"/>
				<updated>2014-08-02T00:46:00Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Direct Manipulation by Attributes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module, by going to the edit page for that user. To summarize:&lt;br /&gt;
# As the administrator, go to the user management module. You'll find it under &amp;quot;Users&amp;quot;&lt;br /&gt;
# Click on the desired user&lt;br /&gt;
# Click &amp;quot;Update User&amp;quot;&lt;br /&gt;
# See the &amp;quot;API Key&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Caveats ===&lt;br /&gt;
'''Note, firstly:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
Furthermore, if you have password-protected any web directories that contain X2Engine, you will need to make a &amp;quot;Satisfy any&amp;quot; exception for URIs within the API, or clients may not be able to authenticate.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow; effectively, the comparison in the query is being performed on a &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt; column in a joined table&lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2Leads&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type can be fully accessed and manipulated, but there is not yet any built-in action available for directly converting a lead to an opportunity.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Direct Manipulation by Attributes ==&lt;br /&gt;
(Coming in 4.2)&lt;br /&gt;
&lt;br /&gt;
Given a set of uniquely-identifying attribute names and values, it is possible to directly access and manipulate an existing record by without first querying it. The direct URI for this use case is:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/{_class}/by:{_findBy}.json&lt;br /&gt;
&lt;br /&gt;
where the query parameter &amp;lt;tt&amp;gt;{_findBy}&amp;lt;/tt&amp;gt; is a list of key and value pairs formatted as &amp;lt;tt&amp;gt;key1=value1;key2=value2&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
For example: provided the email address &amp;quot;james@example.com&amp;quot; and first name &amp;quot;James&amp;quot; uniquely identify the contact with ID of 457, the following two direct access URIs are equivalent:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/Contacts/by:email=james@example.com;firstName=James.json&lt;br /&gt;
  index.php/api2/Contacts/457.json&lt;br /&gt;
&lt;br /&gt;
Furthermore, note what happens in the following scenarios:&lt;br /&gt;
&lt;br /&gt;
'''If the set of attributes (i.e. email address) is not unique to a single record:''' the find-by-attributes direct URI will return with a 300 status and an [[#Error Objects|error object]] containing the two special properties: ''queryUri'', the URI to query for records by the attributes given, and: ''directUris'', a list containing the direct URI of each matching record.&lt;br /&gt;
&lt;br /&gt;
''' If no contact matches the attributes given:''' the URI will respond with a 404 status.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1910</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1910"/>
				<updated>2014-08-02T00:45:41Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Direct Manipulation by Attributes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module, by going to the edit page for that user. To summarize:&lt;br /&gt;
# As the administrator, go to the user management module. You'll find it under &amp;quot;Users&amp;quot;&lt;br /&gt;
# Click on the desired user&lt;br /&gt;
# Click &amp;quot;Update User&amp;quot;&lt;br /&gt;
# See the &amp;quot;API Key&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Caveats ===&lt;br /&gt;
'''Note, firstly:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
Furthermore, if you have password-protected any web directories that contain X2Engine, you will need to make a &amp;quot;Satisfy any&amp;quot; exception for URIs within the API, or clients may not be able to authenticate.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow; effectively, the comparison in the query is being performed on a &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt; column in a joined table&lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2Leads&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type can be fully accessed and manipulated, but there is not yet any built-in action available for directly converting a lead to an opportunity.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Direct Manipulation by Attributes ==&lt;br /&gt;
(Coming in 4.2)&lt;br /&gt;
&lt;br /&gt;
Given a set of uniquely-identifying attribute names and values, it is possible to directly access and manipulate an existing record by without first querying it. The direct URI for this use case is:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/{_class}/by:{_findBy}.json&lt;br /&gt;
&lt;br /&gt;
where the query parameter &amp;lt;tt&amp;gt;{_findBy}&amp;lt;/tt&amp;gt; is a list of key and value pairs formatted as &amp;lt;tt&amp;gt;key1=value1;key2=value2&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
For example: provided the email address &amp;quot;james@example.com&amp;quot; and first name &amp;quot;James&amp;quot; uniquely identify the contact with ID of 457, the following two direct access URLs are equivalent:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/Contacts/by:email=james@example.com;firstName=James.json&lt;br /&gt;
  index.php/api2/Contacts/457.json&lt;br /&gt;
&lt;br /&gt;
Furthermore, note what happens in the following scenarios:&lt;br /&gt;
&lt;br /&gt;
'''If the set of attributes (i.e. email address) is not unique to a single record:''' the find-by-attributes direct URI will return with a 300 status and an [[#Error Objects|error object]] containing the two special properties: ''queryUri'', the URI to query for records by the attributes given, and: ''directUris'', a list containing the direct URI of each matching record.&lt;br /&gt;
&lt;br /&gt;
''' If no contact matches the attributes given:''' the URI will respond with a 404 status.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1909</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1909"/>
				<updated>2014-08-02T00:43:49Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Model-based Input and Output */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module, by going to the edit page for that user. To summarize:&lt;br /&gt;
# As the administrator, go to the user management module. You'll find it under &amp;quot;Users&amp;quot;&lt;br /&gt;
# Click on the desired user&lt;br /&gt;
# Click &amp;quot;Update User&amp;quot;&lt;br /&gt;
# See the &amp;quot;API Key&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Caveats ===&lt;br /&gt;
'''Note, firstly:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
Furthermore, if you have password-protected any web directories that contain X2Engine, you will need to make a &amp;quot;Satisfy any&amp;quot; exception for URIs within the API, or clients may not be able to authenticate.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow; effectively, the comparison in the query is being performed on a &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt; column in a joined table&lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2Leads&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type can be fully accessed and manipulated, but there is not yet any built-in action available for directly converting a lead to an opportunity.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Direct Manipulation by Attributes ==&lt;br /&gt;
(Coming in 4.2)&lt;br /&gt;
&lt;br /&gt;
Given a set of uniquely-identifying attribute names and values, it is possible to directly access and manipulate an existing record by without first querying it. The direct URI for this use case is:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/{_class}/by:{_findBy}.json&lt;br /&gt;
&lt;br /&gt;
where the query parameter &amp;lt;tt&amp;gt;_findBy&amp;lt;/tt&amp;gt; is a list of key and value pairs formatted as &amp;lt;tt&amp;gt;key1=value1;key2=value2&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
For example: provided the email address &amp;lt;tt&amp;gt;james@example.com&amp;lt;/tt&amp;gt; uniquely identifies the contact with ID of 457, the following two direct access URLs are equivalent:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/Contacts/by:email=james@example.com.json&lt;br /&gt;
  index.php/api2/Contacts/457.json&lt;br /&gt;
&lt;br /&gt;
Furthermore, note what happens in the following scenarios:&lt;br /&gt;
&lt;br /&gt;
'''If the set of attributes (i.e. email address) is not unique to a single record:''' the find-by-attributes direct URI will return with a 300 status and an [[#Error Objects|error object]] containing the two special properties: ''queryUri'', the URI to query for records by the attributes given, and: ''directUris'', a list containing the direct URI of each matching record.&lt;br /&gt;
&lt;br /&gt;
''' If no contact matches the attributes given:''' the URI will respond with a 404 status.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1908</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1908"/>
				<updated>2014-08-02T00:40:42Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Model-based Input and Output */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module, by going to the edit page for that user. To summarize:&lt;br /&gt;
# As the administrator, go to the user management module. You'll find it under &amp;quot;Users&amp;quot;&lt;br /&gt;
# Click on the desired user&lt;br /&gt;
# Click &amp;quot;Update User&amp;quot;&lt;br /&gt;
# See the &amp;quot;API Key&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Caveats ===&lt;br /&gt;
'''Note, firstly:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
Furthermore, if you have password-protected any web directories that contain X2Engine, you will need to make a &amp;quot;Satisfy any&amp;quot; exception for URIs within the API, or clients may not be able to authenticate.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow; effectively, the comparison in the query is being performed on a &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt; column in a joined table&lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2Leads&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type can be fully accessed and manipulated, but there is not yet any built-in action available for directly converting a lead to an opportunity.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Direct Manipulation by Attributes ==&lt;br /&gt;
(Coming in 4.2)&lt;br /&gt;
&lt;br /&gt;
It is possible, given a set of uniquely-identifying attribute names and values, to access and manipulate an existing record without first querying it. The direct URI for this use case is:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/{_class}/by:{_findBy}.json&lt;br /&gt;
&lt;br /&gt;
where the query parameter &amp;lt;tt&amp;gt;_findBy&amp;lt;/tt&amp;gt; is a list of key and value pairs formatted as &amp;lt;tt&amp;gt;key1=value1;key2=value2&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
For example: provided the email address &amp;lt;tt&amp;gt;james@example.com&amp;lt;/tt&amp;gt; uniquely identifies the contact with ID of 457, the following two direct access URLs are equivalent:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/Contacts/by:email=james@example.com.json&lt;br /&gt;
  index.php/api2/Contacts/457.json&lt;br /&gt;
&lt;br /&gt;
Furthermore, note what happens in the following scenarios:&lt;br /&gt;
&lt;br /&gt;
'''If the set of attributes (i.e. email address) is not unique to a single record:''' the find-by-attributes direct URI will return with a 300 status and an [[#Error Objects|error object]] containing the two special properties: ''queryUri'', the URI to query for records by the attributes given, and: ''directUris'', a list containing the direct URI of each matching record.&lt;br /&gt;
&lt;br /&gt;
''' If no contact matches the attributes given:''' the URI will respond with a 404 status.&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1855</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1855"/>
				<updated>2014-07-15T04:21:41Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Introduction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
{{TOC right}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module, by going to the edit page for that user. To summarize:&lt;br /&gt;
# As the administrator, go to the user management module. You'll find it under &amp;quot;Users&amp;quot;&lt;br /&gt;
# Click on the desired user&lt;br /&gt;
# Click &amp;quot;Update User&amp;quot;&lt;br /&gt;
# See the &amp;quot;API Key&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Caveats ===&lt;br /&gt;
'''Note, firstly:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
Furthermore, if you have password-protected any web directories that contain X2Engine, you will need to make a &amp;quot;Satisfy any&amp;quot; exception for URIs within the API, or clients may not be able to authenticate.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow; effectively, the comparison in the query is being performed on a &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt; column in a joined table&lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2Leads&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type can be fully accessed and manipulated, but there is not yet any built-in action available for directly converting a lead to an opportunity.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1853</id>
		<title>Software Updates and Upgrades</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1853"/>
				<updated>2014-06-27T20:25:13Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Enacting Database Changes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
[[File:Update_Process.svg|200px|thumb|right|Top-level overview of the update process]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
The following things happen in typical a software update (or upgrade):&lt;br /&gt;
* Files that are new in the new version are added&lt;br /&gt;
* Files that have been changed in the new version are replaced with the new versions of those files &lt;br /&gt;
* Files that are no longer used in the new version are deleted&lt;br /&gt;
* Changes to the database are made to make the database compatible with the new version of X2Engine&lt;br /&gt;
* The main configuration file, &amp;lt;tt&amp;gt;X2Config.php&amp;lt;/tt&amp;gt;, is edited to reflect the new state of the application.&lt;br /&gt;
* X2Engine data caches are cleared&lt;br /&gt;
&lt;br /&gt;
These operations are typically handled by the updater utility component [[x2doc:UpdaterBehavior|UpdaterBehavior]] and controlled via the in-app web-based updater or the command line updater. This article will cover both how to use said interfaces to the updater, and how to perform updates manually or with the &amp;quot;offline&amp;quot; method if the need arises, in addition to troubleshooting and recovering from an update that did not complete cleanly.&lt;br /&gt;
&lt;br /&gt;
= Disclaimer =&lt;br /&gt;
This article covers the standard and recommended update/upgrade methods, in addition to alternate methods and supplemental procedures. As general rules and precautions for using this article:&lt;br /&gt;
# ''If you do not fully and completely understand any step of any instructions given in this article, '''do not''' follow them''; ask for help instead on [http://x2community.com the forums] or through contacting X2Engine Technical Support if you are an existing customer.&lt;br /&gt;
# You should always make a backup of all of your files ''and'' a full backup copy of your database, preferably in a format (i.e. SQL script) that can be used to create a verbatim/carbon copy of the original database, with all of the features of the original schema, '''before''' attempting any steps of the update process manually.&lt;br /&gt;
# Some of the procedures outlined in this article involve manually creating or modifying files. Note that the same filesystem prerequisite for installation, namely that the file ownership must match the user under which PHP executes on the web server, applies to these steps.&lt;br /&gt;
&lt;br /&gt;
Furthermore, '''in regard to uploading files:''' note that any instructions to upload folders into X2Engine, and replace/merge contents, refer to replacing files in X2Engine with files in the archive at analogous paths. For example, if you have a zip file that contains a folder &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, and inside of that is a folder &amp;lt;tt&amp;gt;components&amp;lt;/tt&amp;gt;, and inside of that is a file &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt;, and the instructions are to upload/replace in X2Engine, you should upload &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt; to replace the existing file of the same name in &amp;lt;tt&amp;gt;protected/components&amp;lt;/tt&amp;gt; inside of X2Engine.&lt;br /&gt;
&lt;br /&gt;
X2Engine Inc. cannot be held accountable for damage/errors caused by any missteps or mistakes in manually-performed update operations. If in doubt, make a sandbox/clone of your X2Engine installation by copying the files and database to a different server, and test performing the operations on that before proceeding with your production install of X2Engine.&lt;br /&gt;
&lt;br /&gt;
= Update Notifications =&lt;br /&gt;
[[File:Update Notification.png|200px|frame|right|System flash message displaying software update notification.]]&lt;br /&gt;
To get notified of software updates, unless already using X2Engine Professional Edition, one must first &amp;quot;subscribe&amp;quot; to them. This can be done via checking the box &amp;quot;Notify me of software updates&amp;quot; in the installation form. If X2Engine is already installed, this can be done by going to &amp;quot;Updater Settings&amp;quot; from the Admin page and enabling them. If using Professional Edition, these steps are not not necessary.&lt;br /&gt;
&lt;br /&gt;
Once that has been completed, set the update notification interval as desired in &amp;quot;Updater Settings&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Update Methods =&lt;br /&gt;
There are, in essence, just three basic methods of updating, and variations on each. The three main methods are: &lt;br /&gt;
# web updater,&lt;br /&gt;
# command line updater, and&lt;br /&gt;
# manually.&lt;br /&gt;
In each case, an update package from the update data server is used as the authoritative list of changes to apply, but in the non-manual methods, X2Engine's own updater class does all the work applying changes from said package.&lt;br /&gt;
&lt;br /&gt;
In web and command line updates, there is also the option to first download and unpack the package manually (see [[#Performing_.22Offline.22_Updates|performing &amp;quot;offline&amp;quot; updates]]). This circumvents the need for the local server (on which X2Engine is hosted) to be connected to the internet. Similarly, in the case of manual updates, it is not necessary for the web server to actually have an internet connection.&lt;br /&gt;
&lt;br /&gt;
== Using the Web Updater ==&lt;br /&gt;
[[File:Updater-1.png|300px|thumb|right|The updater utility page.]]&lt;br /&gt;
The web updater can be accessed by any of the following methods:&lt;br /&gt;
* Using the link in the notification message at the top of the window in versions later than 3.5.5&lt;br /&gt;
* By going to the Admin page and clicking the &amp;quot;Update&amp;quot; button at the top, in earlier versions, with update notifications enabled&lt;br /&gt;
* Through &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
First, navigate to the web updater page, and '''do not''' cancel the request (stop or hit your browser's &amp;quot;back&amp;quot; button) until the page has loaded. If the updater utility has undergone changes and needs to self-update to be compatible with the software updates server, this is when it will do so. Thus, it is critical that you do not interrupt this process. If the page does not load, or displays the error message that dependencies could not be retrieved, you may have to [[#Performing_.22Offline.22_Updates|perform an &amp;quot;offline&amp;quot; update]].&lt;br /&gt;
&lt;br /&gt;
There are three ways of getting to the updater page. One is to simply click the link given in the update notification flash message, if available. The other is to click on the &amp;quot;Update&amp;quot; button at the top of the admin page. If software update notifications are not enabled, one can manually check for and install an update by going to &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
Next, make a backup of the database and files (recommended), follow the instructions on the page, and click the &amp;quot;Update&amp;quot; button when ready.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
It is generally quick and is the easiest method to use, and it does not require shell/SSH access to the web server.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
It is not recommended for use on large installations of X2Engine, i.e. containing over a million records, or for updating from very old versions (i.e. a version more than five months old). This is because the final update process may be cut short by server timeout or an interruption in the network connection. This can result in an incomplete/broken update that requires recovering manually (a potentially long process) at best, or actual loss of data at worst. &lt;br /&gt;
&lt;br /&gt;
==== Explanation ====&lt;br /&gt;
The inherent weakness of the web updater (or any web-based updater to any web application) is that it must always rely upon web requests to the server, and it thus must perform extremely critical operations for the update all within the scope of a web request. Web requests, it goes without saying, are not always reliable. For example, one's internet connection might get cut off due being on a wireless network and encountering interference. Furthermore, if any operations take an exceedingly long amount of time, e.g. because of the size of the data set (and thus indexes) on which the update must operate, or the number of database changes that need to be applied, the web request for those operations may take longer than the PHP execution time limit as configured on the webserver. This would potentially cut short the execution of a critical update stage, and thus the update would require manual recovery at best, or completely restoring  X2Engine to a backup at worst.&lt;br /&gt;
&lt;br /&gt;
==== How to Avoid Timeouts ====&lt;br /&gt;
You can usually get around this by setting the value of &amp;lt;tt&amp;gt;'''max_execution_time'''&amp;lt;/tt&amp;gt; to a higher value in the web server's PHP configuration. It is highly recommended that you either disable the timeout or set it to a very generous value (such as 300 or more seconds) while attempting to update via the web updater, especially if you are many versions behind or have a high volume of data in your CRM.&lt;br /&gt;
Also, it is generally always a good idea to create a backup copy ''before'' updating, and to download it.&lt;br /&gt;
&lt;br /&gt;
==== Recovering From Crashes ====&lt;br /&gt;
If you downloaded the backup copy of your database, then after you encounter a failure/timeout, you can use the .sql backup script to restore the database to its original form. However, be aware, that if there are any '''&amp;lt;tt&amp;gt;CREATE TABLE&amp;lt;/tt&amp;gt;''' commands in the update data (expand the ''Database Commands to Run'' list to see), you will need to manually drop the created tables in order to once again make your installation compatible with the update.&lt;br /&gt;
&lt;br /&gt;
== Using the Command Line Updater ==&lt;br /&gt;
This update method uses all of the same thoroughly-tested underpinnings and processes as the web updater, but does so all within a system command to the Yii console.&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
While in via SSH, or in a terminal emulator, or in a script, run the following command in the &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt; subdirectory of X2Engine:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./yiic update app&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the following optional arguments to the command line updater:&lt;br /&gt;
;&amp;lt;tt&amp;gt;--force&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; for this option (i.e. &amp;lt;tt&amp;gt;--force=1&amp;lt;/tt&amp;gt;) to proceed with the update even if compatibility issues were detected. Default: 0 (halt if compatibility issues were detected).&lt;br /&gt;
;&amp;lt;tt&amp;gt;--backup&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that a database backup should be performed first before updating, &amp;quot;0&amp;quot; to proceed without creating a backup first. Default: 1.&lt;br /&gt;
;&amp;lt;tt&amp;gt;--lock&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that the application should be locked during the final installation of the update package to prevent data entry during the sensitive moments when the database is being altered and the files updated, &amp;quot;0&amp;quot; to permit use even when changes to the app are being applied. Default: 0.&lt;br /&gt;
&lt;br /&gt;
If running the command gives you the following message:&lt;br /&gt;
&amp;lt;pre&amp;gt;The updater is now up-to-date and compliant with the updates server. Re-run the command to proceed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
That indicates that the updater first needed to self-update, and succeeded in doing so. You should then run the same command again to complete the update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Safer to use on large, mission-critical systems. The success of a software update (or upgrade) does not hinge upon a single web request completing successfully.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
More difficult to use; requires shell access to the web server. The console command must be run as the same system user that owns all the files and directories of X2Engine, in order to circumvent permission issues when manipulating file system objects.&lt;br /&gt;
&lt;br /&gt;
== Using the Automatic Updater ==&lt;br /&gt;
[[File:Auto-Update.png|400px|thumb|right|Auto-update scheduler on the Updater Settings page]]&lt;br /&gt;
This method uses the command line updater, but runs the command on your behalf using the local cron daemon. To configure, go to the Updater Settings Page, check the box titled &amp;quot;Update Automatically&amp;quot;, and save. If you have Professional Edition, you can manage the update schedule in addition to other automated tasks from the Cron Table settings page, which can also be found under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Automates the entire process of updating.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
Only available on UNIX-like systems that have a cron daemon, and which permit user access to the cron table.&lt;br /&gt;
&lt;br /&gt;
== Performing &amp;quot;Offline&amp;quot; Updates ==&lt;br /&gt;
In some server environments, the updater may not be able to access the updates server by itself, due to (for example) the PHP or network configuration, or because the &amp;quot;zip&amp;quot; PHP extension (requried) is unavailable or has been disabled. It is in these situations necessary to download the update package manually, unpack on the server (or unpack and upload the files to the server), and then run the updater. When performing updates in this way, the updater will not need to access the updates server, but will use the manually-downloaded and extracted package files present in the local filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Refreshing and Updating the Updater Utility ===&lt;br /&gt;
Before starting, note that the updater itself may have undergone changes; the X2Engine update process is subject to change. To address this technical challenge, the updater utility within X2Engine would normally first attempt to download the latest version of itself, in order to be compatible with the updates server. However, this sometimes will not work properly. If your X2Engine installation is running in a hosting environment that prevents it from making outbound HTTP requests (the [[requirements:|requirements check script]] should tell you if this is the case), you will need to do this manually. Also, in version 3.5.5, there was a bug in the updater utility's self-update methods.&lt;br /&gt;
&lt;br /&gt;
To update the updater utility:&lt;br /&gt;
# '''Determine the version of the updater utility package''' by visiting [https://x2planet.com/installs/updates/updateCheck this link]. You should see a page that's entirely blank except for a version number.&lt;br /&gt;
# '''Download the updater package.''' In versions 3.6 and later, a download link for this should be available through the ''Updater Settings'' page. Otherwise, use the following URL (if in Open Source Edition): &amp;lt;pre&amp;gt;https://x2planet.com/installs/updater.zip&amp;lt;/pre&amp;gt; Otherwise, if using Professional Edition, paste the following url, after replacing {key} with your license key, into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/updater-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# '''Extract the downloaded zip archive and upload''' the &amp;quot;protected&amp;quot; folder to your server. Merge folders and overwrite existing files when prompted.&lt;br /&gt;
# '''Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor''' and find the line that looks like this:&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$updaterVersion = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# '''Change the text inside the pair of single quotes''' on that line so that it is the current updater version obtained in step #1. &lt;br /&gt;
# '''Save the file.'''&lt;br /&gt;
&lt;br /&gt;
=== Obtaining the Update Package ===&lt;br /&gt;
First, use the link in the ''Updater Settings'' page entitled &amp;quot;Latest Update Package for Version...&amp;quot;, to download the update package. If the link in the Updater Settings page does not work, use the following URL to download the update package (if using Open Source Edition), replacing &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; with your X2Engine installation's version:&lt;br /&gt;
&amp;lt;pre&amp;gt;https://x2planet.com/installs/updates/{version}/none&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should receive, from that URL, a zip archive file called &amp;quot;update.zip&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If using Professional Edition, and your download link is not working, contact [mailto:customersupport@x2engine.com customer support].&lt;br /&gt;
&lt;br /&gt;
=== Unpacking the Update ===&lt;br /&gt;
[[File:Update-in-Progress.png|200px|frame|right|System flash message displaying notification of pending update.]]&lt;br /&gt;
The update package will be a zip archive. The ''contents'' of this archive must be extracted ''into'' a folder called &amp;quot;update&amp;quot; in the installation directory. So, in other words, after extracting and uploading to the server, there should be a folder called &amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt; alongside &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;framework&amp;lt;/tt&amp;gt; (etc). Within that folder (&amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt;) should be the following contents:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A file containing important data to be used by the updater&lt;br /&gt;
;&amp;lt;tt&amp;gt;contents.md5&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A message digest file containing MD5 checksums of all files in the archive excluding itself.&lt;br /&gt;
;&amp;lt;tt&amp;gt;source&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A sub-folder/directory, whose internal structure mimics that of X2Engine, containing files that were changed in the update. If there were no files changed (i.e. only database changes were in the update), this directory will not be present.&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A directory containing flat data files. These files are typically not used by the updater, but are useful if performing manual updates or recovery.&lt;br /&gt;
&lt;br /&gt;
=== Applying the Update ===&lt;br /&gt;
[[File:Steps-post-Unpack.png|200px|frame|right|Steps of the update process in the event of manually-extracted package (or resuming a preexisting update)]]&lt;br /&gt;
After unpacking, if you run the command line updater, it should (if no problems arise, i.e. incompatible package version) apply the update straight away. Otherwise, log into X2Engine, and you should see a system flash message notifying you of an update in progress. This message shows up whenever there is an update package folder (&amp;quot;update&amp;quot;) present on the server. If you then go to the updater page, once the page has finished loading, you should see that there only remain the steps &amp;quot;Review and confirm changes&amp;quot;, &amp;quot;Apply changes&amp;quot; and &amp;quot;Reload&amp;quot; remain to be completed. To complete the update, when ready, click &amp;quot;Update&amp;quot;; otherwise, to delete the &amp;quot;update&amp;quot; folder and cancel the update, click &amp;quot;Cancel&amp;quot; either on the updater page or in the system flash message.&lt;br /&gt;
&lt;br /&gt;
== Performing Updates Manually ==&lt;br /&gt;
While it is preferable to avoid manually applying changes to X2Engine, and you may never need to, it may in some circumstance or another actually be necessary to do so. For all such situations, the raw update data files generated by the X2Engine update build process are bundled with every update package to help you perform the operations described in [[#Introduction|&amp;quot;Introduction&amp;quot;]]. This section will describe how to perform each of those operations. &lt;br /&gt;
&lt;br /&gt;
=== Prerequisites ===&lt;br /&gt;
First and foremost, all shell commands listed here, and in general, any operations that create or modify files, ''must be run as the same system user under which the server runs PHP scripts.'' Otherwise, the ownership and/or permissions of files and directories will have to be changed after completing all changes.&lt;br /&gt;
&lt;br /&gt;
To obtain the update package (which will be necessary), refer to &amp;quot;[[#Obtaining_the_Update_Package|Obtaining the Update Package]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Furthermore, it is strongly advised that you first make a back-up copy of all files and of X2Engine's database. Your database backup would best be a full, exact copy, such as one that would be made by [http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html mysqldump], or the built-in export tools of [http://www.phpmyadmin.net/home_page/index.php PHPMyAdmin].&lt;br /&gt;
&lt;br /&gt;
The X2Engine global import/export utility (&amp;quot;Import All Data&amp;quot;/&amp;quot;Export All Data&amp;quot;) in Admin '''is not recommended by itself as the sole backup tool'''. This is especially important to note because of how this tool cannot re-create ancillary data such as tags or relationships, or user roles. However, using it to ''supplement'' your standard database backup method will prove useful in the event that you:&lt;br /&gt;
# don't have any custom roles, relationships, tags, etc.&lt;br /&gt;
# cannot manage to update X2Engine with success, and&lt;br /&gt;
# wish to start over with a fresh installation.&lt;br /&gt;
In such a case, you will then be able to import your old data into the new installation.&lt;br /&gt;
&lt;br /&gt;
=== Introduction: Data Files ===&lt;br /&gt;
In [[#Unpacking_the_Update|Unpacking the update]], a rough overview of an update package's contents were given. In this section, the anatomy and purpose of each of the data files will be introduced.&lt;br /&gt;
&lt;br /&gt;
Included in every update package is two copies of the following data:&lt;br /&gt;
* Files to copy&lt;br /&gt;
* Files to delete&lt;br /&gt;
* Database changes to enact&lt;br /&gt;
* Migration scripts to run, if any&lt;br /&gt;
&lt;br /&gt;
The first, immediately visible place where all of this data is stored is in the file &amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;, as a JSON-encoded array. See the next section for how to use this file.&lt;br /&gt;
&lt;br /&gt;
The second place, which can be useful to users who do not wish to automate or script any step of a manual update, is in the files contained in the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; directory. Each file is named after the version to which it corresponds, and contains the data required to update ''to'' that version ''from the version immediately preceding it.''' Thus, the versions you should see in each file name are the versions above your X2Engine installation's current version, up to and including the version being updated to.&lt;br /&gt;
&lt;br /&gt;
In an update, the directory will contain the following subdirectories:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of files to copy. The format is each line containing a file path in the list.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of obsolete files for that version which will need to be deleted. The format is the same as in &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts corresponding to each version. Each file is formatted such that it can be directly run on the database as an SQL script, although this is not generally recommended. The list of SQL commands to run is a of the following format: &lt;br /&gt;
  [query 1];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [query 2];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [etc]&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts with similar naming and formatting conventions to the &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; scripts. See [[#The_structure_of_the_.22data.22_array|The structure of the &amp;quot;data&amp;quot; array]] for explanation of the purpose of &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== How to use the manifest for updating manually ===&lt;br /&gt;
If you so choose, you may write a script to perform all of the operations for an update in one fell swoop. If so, and if the preferred scripting language of choice has a JSON-parsing library available, you will be able to save yourself a lot of effort by using the main data file (&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;) to load all of the necessary data for the update.&lt;br /&gt;
&lt;br /&gt;
==== The structure the manifest ====&lt;br /&gt;
In updates, the contents of the plain-text file manifest.json are a JSON-encoded object containing the following properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the data corresponding to each intermediate version. It is, in essence, all the data stored in the files in &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt;. If the update is for an installation of X2Engine that is only one version behind, it will contain only one element. Each element of this array is itself an array containing respective data for the intermediate version.&lt;br /&gt;
;&amp;lt;tt&amp;gt;versions&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array listing the version numbers of each intermediate version in the update. Similar to &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, this array will contain only one element if updating to the next version, and that version ''will be the next version''.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of deletions of obsolete files across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of files added or changed across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''from'', or in other words, the version of X2Engine before the package is applied. '''This must be the same as the version of installation to which the package is being applied.''' If this is not so, the updater utility would exit immediately, as applying the changes in the update would have potentially harmful results to data and/or compatibility with future updates.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''to'', or in other words, the version of X2Engine before the package is applied.&lt;br /&gt;
;&amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the version of the updater utility for which the package was compiled. If the structure of the update package or of the manifest changes, the updater must also change to accommodate and recognize the changes; else it might try to access properties that don't exist, or ignore important ones that do. This is the single property of manifest.json that should never change.&lt;br /&gt;
;&amp;lt;tt&amp;gt;scenario&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, which should be &amp;quot;update&amp;quot; if the package is for an update. In the case of upgrades, which use most of the same functionality of the updater, it should be &amp;quot;upgrade&amp;quot;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation after the package is applied. It should be &amp;quot;opensource&amp;quot; for open source edition, &amp;quot;pro&amp;quot; for professional edition. In the case of an upgrade package, it differs from the &amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation before the package is applied. &lt;br /&gt;
;&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An integer denoting the timestamp of the release date of the version of X2Engine denoted by &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The structure of the &amp;quot;data&amp;quot; array ====&lt;br /&gt;
In updates, each version's data, contained in each element of the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, contains the following nested array properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files added for the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files deleted in the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of SQL commands to bring the database structure and contents into compliance with the version from the previous version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A prerequisite list of SQL commands that should be run before those in &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, and if any fail, the failures should be ignored. These are added to updates whenever it is necessary to normalize the database schema, i.e. in the event that some installations' databases may not be consistent with others, but the commands might fail on every other system. These SQL commands were intended to be &amp;quot;attempted&amp;quot;; they are not guaranteed to succeed on all installations. An example of when this is necessary would be adding a foreign key constraint when in previous versions the storage engine was left to the database's default settings in old versions. MyISAM does not natively support foreign key constraints. Thus, to standardize this across all installations, including installations on servers where the storage engine defaulted to MyISAM, the constraint must first be dropped (in a &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; command) before the relevant tables are changed to the InnoDB storage engine, and the constraint is added back again, in the usual &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;version&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The version number&lt;br /&gt;
;&amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The edition&lt;br /&gt;
;&amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of PHP scripts that would be run in an update for this version. These are a subset of fileList and are typically used for advanced database operations, i.e. restructuring values of JSON-encoded fields, which are extremely impractical (or impossible) to perform using raw SQL commands. The files contained in the list all have paths that look like &amp;quot;&amp;lt;tt&amp;gt;protected/migrations/{version}/{timestamp}-name-of-operations.php&amp;lt;/tt&amp;gt;&amp;quot;. Note, if this array is not empty, and the &amp;lt;tt&amp;gt;'''runmigrationscript'''&amp;lt;/tt&amp;gt; Yii command does not work (or isn't available yet), you may need to reverse-engineer or otherwise copy and re-purpose/re-write any migration scripts listed in order to perform their necessary operations. This is because they often depend on running within a Yii Framework environment, i.e. will contain references to components of the Yii application singleton such as &amp;quot;db&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Also note that in upgrades, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; are replaced by &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; (respectively), and &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; are missing (because they aren't necessary).&lt;br /&gt;
&lt;br /&gt;
==== What you will need for the update, if using the manifest ====&lt;br /&gt;
''Among this data, what you will need:'' &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, which will give you the full list of files that need to be deleted, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, which will give you the full list of files to copy from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory, &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;, which give you values with which to update the main X2Engine configuration file, and, within each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, the sub-array properties &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;, with which you will construct the list of database commands to run. &lt;br /&gt;
&lt;br /&gt;
A procedure for manually updating X2Engine, with data from manifest.json, thus might look like this:&lt;br /&gt;
# For each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; (or in other words, each version's changes):&lt;br /&gt;
## Run all the SQL commands in its corresponding &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; sub-array, ignoring errors&lt;br /&gt;
## Run all SQL commands in its &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; array&lt;br /&gt;
## Perform database operations that would be equivalent to those performed in migration scripts&lt;br /&gt;
# For each file in the cumulative list &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, copy it from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory into its analogous location in the installation directory of X2Engine, creating parent directories as necessary if they don't already exist.&lt;br /&gt;
# Delete all files in the cumulative list &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; from the installation directory&lt;br /&gt;
# Clear the data cache files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt; and the temporary asset folders in &amp;lt;tt&amp;gt;assets/&amp;lt;/tt&amp;gt;&lt;br /&gt;
# Clear all session data (&amp;lt;tt&amp;gt;x2_sessions&amp;lt;/tt&amp;gt;) and auth cache data (&amp;lt;tt&amp;gt;x2_auth_cache&amp;lt;/tt&amp;gt;).&lt;br /&gt;
# Update the main X2Engine configuration (&amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt;) file, setting:&lt;br /&gt;
## &amp;lt;tt&amp;gt;$buildDate&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt; property,&lt;br /&gt;
## &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt; property, and &lt;br /&gt;
## &amp;lt;tt&amp;gt;$updaterVersion&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
&lt;br /&gt;
=== Enacting Database Changes ===&lt;br /&gt;
Inside the &amp;lt;tt&amp;gt;update/sqlList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/sqlForce&amp;lt;/tt&amp;gt; folder is a list of &amp;lt;tt&amp;gt;.sql&amp;lt;/tt&amp;gt; files, named according to their corresponding version's update, that can essentially be run as SQL scripts to perform the update. Note, however, that '''if the update traverses more than one version, the scripts must be run in the order of their corresponding version.''' This is because, in the database changes for any version, it is assumed that the database schema and contents conform to the version that immediately preceded it. The PHP function [[phpfunc:version-compare|version_compare]], and the method &amp;quot;[http://pythonhosted.org/setuptools/pkg_resources.html#parsing-utilities parse_version]&amp;quot;, in the pkg_resources module of setuptools, should correctly identify which version should come first. Note, the [[#The_structure_the_manifest|&amp;quot;versions&amp;quot; property of the manifest]] will contain the list of versions in the correct order.&lt;br /&gt;
&lt;br /&gt;
Furthermore, note, &amp;quot;sqlList&amp;quot; and &amp;quot;sqlForce&amp;quot; may not encompass all necessary database changes; if there are any migration scripts in the update, the operations they perform will similarly need to be performed in the current update (see the description of &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; in [[#The_structure_of_the_.22data.22_array|&amp;quot;the structure of the 'data' array&amp;quot;]]).&lt;br /&gt;
&lt;br /&gt;
In summary, for each version traversed in the update, the following operations should be run in order, with &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version number of the version in question:&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlForce/{version}.sql&amp;lt;/tt&amp;gt; file,''' in the order they appear in the file. Ignore if any commands fail. Note, if passed to the MySQL client as a script, any failures might trigger a halt in execution and thus skip the remainder of the commands.&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlList/{version}.sql&amp;lt;/tt&amp;gt; file.'''&lt;br /&gt;
# '''Run migration scripts using &amp;lt;tt&amp;gt;./yiic runmigrationscript {path}&amp;lt;/tt&amp;gt;, if any.''' The first part of a script file name is the timestamp it was added to the codebase, and this also dictates the order in which they should be run.&lt;br /&gt;
&lt;br /&gt;
=== Copying new files ===&lt;br /&gt;
Files added/changed in the new version are stored in the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory. In an update, they should be copied into the installation directory, replacing any existing files of the same name/path. If the &amp;lt;tt&amp;gt;rsync&amp;lt;/tt&amp;gt; utility is available on the system, you can use one command to safely copy these files into the installation directory as follows (executed in that same directory):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rsync -avc update/source/ ./&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, you will need to use &amp;lt;tt&amp;gt;cat&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;cp&amp;lt;/tt&amp;gt;, two utilities that should be available on every UNIX-like system, and the lists of files added in each intermediate version. These lists are stored in the folder &amp;lt;tt&amp;gt;update/data/fileList&amp;lt;/tt&amp;gt; and named like &amp;quot;{version}.txt&amp;quot; (&amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version to which it applies), each file on its own line in that file. Run the following command, ignoring errors:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/fileList/*; do for l in $(cat $f); do cp update/source/&amp;quot;$l&amp;quot; ./&amp;quot;$l&amp;quot; ; done ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting old files ===&lt;br /&gt;
Files to be deleted are listed in a similar manner as the files to be added: one file per line. The list of files to delete are located at files with paths that look like this: &amp;lt;tt&amp;gt;update/data/deletionList/{version}.txt&amp;lt;/tt&amp;gt;&lt;br /&gt;
So, for an update from 5.5 to 5.7 with intermediate version 5.6 (for instance), the files would be &amp;lt;tt&amp;gt;update/data/deletionList/5.6&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/data/deletionList/5.7&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is only one version through which to update, the operation is very simple, and can be performed in one UNIX/Linux (bash) shell command as follows (if the version is 3.6.3, for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
xargs -a update/data/deletionList/3.6.3.txt -d&amp;quot;\n&amp;quot; rm -fv&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, if there are multiple versions, this may be a more tricky proposition. The necessity arises to create a cumulative list, because there might be the possibility that a file is deleted in one version and re-established in a new version. Nevertheless, while this is possible, it is highly unlikely, and in most cases it is safe to simply iterate over all the &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; data files and delete all the files listed therein. As a bash command:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/deletionList/*; do xargs -a $f -d&amp;quot;\n&amp;quot; rm -fv ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Editing the Configuration File ===&lt;br /&gt;
This final step in the update process, before refreshing, should not be done manually unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a fresh installation at the same version as the one to which you have updated.&lt;br /&gt;
&lt;br /&gt;
To update the configuration manually:&lt;br /&gt;
# Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor&lt;br /&gt;
# Find the line with the &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; variable in it. It should look like this (but with the version from which you are updating): &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$version = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change the value in quotes to the current version being updated to, and save the file.&lt;br /&gt;
&lt;br /&gt;
=== Clearing caches and refreshing the application ===&lt;br /&gt;
It is generally always a good idea to clear the application's server-side cache and other ephemeral files/data after updating. This is taken care of automatically by the built-in updater, but barring proper use and functioning of that, the operations can be performed manually as follows: &lt;br /&gt;
;clear the data cache&lt;br /&gt;
: delete all &amp;quot;&amp;lt;tt&amp;gt;.bin&amp;lt;/tt&amp;gt;&amp;quot; files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -fv protected/runtime/cache/*.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear web assets&lt;br /&gt;
: delete all sub-folders of &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt; by running this command (in the installation directory): &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -r assets/*/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear the &amp;quot;auth cache&amp;quot;&lt;br /&gt;
: Flush the auth cache table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;&lt;br /&gt;
DELETE FROM `x2_auth_cache` WHERE 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;log all users out&lt;br /&gt;
: Flush the sessions table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;DELETE FROM `x2_sessions` WHERE 1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Upgrading =&lt;br /&gt;
&lt;br /&gt;
== Using the Web Interface ==&lt;br /&gt;
[[File:Software-Upgrade-Registration.png|400px|thumb|right|Software upgrade page, in the first step (registration)]]&lt;br /&gt;
To upgrade to Professional Edition&lt;br /&gt;
# Purchase X2Engine Professional Edition Download. Note the registration info in the activation email.&lt;br /&gt;
# Go to the Admin page in X2Engine and find &amp;quot;Upgrade X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot;.&lt;br /&gt;
# In the ''Upgrade'' page, enter your registration info into the form and click &amp;quot;Register&amp;quot; to continue. Once complete, you will see a list of changes similar to those displayed when performing an update.&lt;br /&gt;
# Review the changes to be applied and then proceed by clicking &amp;quot;Upgrade&amp;quot;, similar to performing an update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Offline&amp;quot; Upgrading ==&lt;br /&gt;
This works in exactly the same way as offline updating. However, to obtain your upgrade package, which you will extract into a folder called &amp;quot;update&amp;quot; on the X2Engine web server, contact [mailto:customersupport@x2engine.com customer support]. Furthermore, you will need to run the following database command on your X2Engine database when done with the upgrade, replacing &amp;lt;tt&amp;gt;{product key}&amp;lt;/tt&amp;gt; with your product key, to allow your installation to receive Professional Edition software updates:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{product key}' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Manually Upgrading ==&lt;br /&gt;
The process of upgrading is very similar to that of updating, and uses all of the same underpinnings as the updater. Thus, almost all of the above information and procedures will apply to upgrades. However, note the following crucial differences:&lt;br /&gt;
&lt;br /&gt;
* '''X2Engine must be at the latest available version in order to upgrade.'''&lt;br /&gt;
* '''To obtain the upgrade package, you must be an owner of an X2Engine Professional Edition Download-type license.''' Contact [mailto:customersupport@x2engine.com customer support] for further details.&lt;br /&gt;
* '''There will be only one &amp;quot;intermediate&amp;quot; version.''' The changes to apply will move X2Engine from a version to that same version, applying instead the necessary changes to switch its ''edition'' from Open Source to Professional.&lt;br /&gt;
* '''There are no file deletions.'''&lt;br /&gt;
* '''The properties of the package manifest differ slightly.''' In upgrades, the properties of the data object are &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, etc. Note also, the update data is in aptly-named sub-folders of &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt; as well, and there are no &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; lists to handle.&lt;br /&gt;
* '''The configuration is not changed.''' Instead, the value stored in the field &amp;lt;tt&amp;gt;unique_id&amp;lt;/tt&amp;gt; of the admin record in the database table &amp;lt;tt&amp;gt;x2_admin&amp;lt;/tt&amp;gt; must be changed to the license key, and the &amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt; field must be changed to &amp;quot;pro&amp;quot;. To perform these both, run the following command on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''It is not necessary to clear caches or user sessions when done.''' These operations are performed in updates to eliminate ephemeral (session-ingrained) data that may no longer be applicable and thus cause problems. Upgrades will rarely, if ever, invalidate this data.&lt;br /&gt;
&lt;br /&gt;
= Updating to Beta Versions = &lt;br /&gt;
Beginning at version 4.1.2, after much advocacy to adopt this practice, X2Engine began a two-phase release process in which each version would be released first as a beta, then to the broader public after initial bug finds. The purpose behind this is to avoid updating production installations of X2Engine to new versions that have had zero public exposure, and thus avoid introducing bugs that were not found in initial QA testing (due to a incompatibility with a particular server environment or configuration of X2Engine) into sensitive production environments.&lt;br /&gt;
&lt;br /&gt;
By default, the updater will not update to beta versions. The updater must be configured to use the proper URL on the update server for beta release data, by the following procedure:&lt;br /&gt;
# Create a file &amp;quot;constants-custom.php&amp;quot; in the root level of X2Engine. You can easily do this by renaming &amp;quot;constants-custom.example.php&amp;quot; to that name (remove &amp;quot;.custom&amp;quot;).&lt;br /&gt;
# Look for the following line, and in it, change &amp;quot;false&amp;quot; to &amp;quot;true&amp;quot;: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('X2_UPDATE_BETA') or define('X2_UPDATE_BETA',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The advantage of enabling beta releases is that new features will be available much sooner. Additionally, fixes to bugs will also be available sooner. The main disadvantage of enabling beta releases is that they are never guaranteed to be as stable as non-beta releases. We encourage users to clone their installations of X2Engine, if able, and use beta versions on them, and report any issues they encounter for their particular server environment/special configuration/custom implementation of X2Engine. This way, any issues that arise can be fixed in the stable release. The more users participate in beta testing, the more stable the non-beta releases will become.&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
== User interface (UI) &amp;quot;bugs&amp;quot; after the update ==&lt;br /&gt;
It is extremely common (and easily remedied) that after an important update, certain features of the user interface of X2Engine may not work properly. This is caused, in nearly all cases, by the web browser's cache, which contains out-of-date copies of X2Engine's essential user interface files. To solve these problems, the first step to try is clearing the cache. A step-by-step guide to doing so is given in [[wikihow:Clear-Your-Browser%27s-Cache]].&lt;br /&gt;
&lt;br /&gt;
== Errors in the updater itself == &lt;br /&gt;
When all else fails, the updater may have to be patched due to an irreconcilable backwards compatibility issue. Fortunately, if one has read/write access to the source files on the webserver, this is straightforward to fix. See [[#Refreshing_and_Updating_the_Updater_Utility|Refreshing and Updating the Updater Utility]].&lt;br /&gt;
&lt;br /&gt;
== Professional Edition not in effect after upgrading ==&lt;br /&gt;
This may be caused by Professional Edition not getting &amp;quot;switched on&amp;quot; properly, despite all the necessary filesystem and database changes already being in effect. First, to see if this scenario applies to you, look for a folder &amp;quot;&amp;lt;tt&amp;gt;reports&amp;lt;/tt&amp;gt;&amp;quot; inside &amp;lt;tt&amp;gt;protected/modules&amp;lt;/tt&amp;gt;, and a table &amp;lt;tt&amp;gt;x2_reports&amp;lt;/tt&amp;gt; in the X2Engine database. If you find these things, you can try the following to enable Professional Edition:&lt;br /&gt;
# Find the file &amp;lt;tt&amp;gt;constants.php&amp;lt;/tt&amp;gt; in the installation directory. Open it in a text editor, and look for the following line: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('PRO_VERSION') or define('PRO_VERSION',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; on that line, and save the file.&lt;br /&gt;
# Run the following SQL command (replacing &amp;lt;tt&amp;gt;{license key}&amp;lt;/tt&amp;gt; with your X2Engine license key) on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General Procedures to Try ==&lt;br /&gt;
It may sometimes be possible that the update did not finish cleanly. In such cases, it will be necessary to manually re-perform some of the final operations in the update, described in [[#Introduction|Introduction]]. &lt;br /&gt;
&lt;br /&gt;
=== Refresh the Source Code ===&lt;br /&gt;
This step is not really the best step to try first, as it is usually more time-consuming. It entails re-uploading the entire application, in order to reset all non-ephemeral (source code) files to what they should be in a fresh installation of X2Engine. To do this:&lt;br /&gt;
# Download the refresh package. There should be a download link for this in the ''Updater Settings'' page. Otherwise, if that doesn't work and you're using Open Source edition, use [https://x2planet.com/installs/refresh.zip this link], or if using Professional Edition, paste the following url (after replacing {key} with your license key) into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/refresh-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Upload the ''contents'' of the downloaded zip archive to the installation directory, overwriting existing files.&lt;br /&gt;
&lt;br /&gt;
=== Clear Caches ===&lt;br /&gt;
See &amp;quot;[[#Clearing_caches_and_refreshing_the_application|Clearing Caches...]]&amp;quot; for details.&lt;br /&gt;
&lt;br /&gt;
=== Regenerate the Configuration ===&lt;br /&gt;
Again, this should not be done unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a new installation at the same version as the one you updated to. Refer to [[#Editing_the_Configuration_File|Editing the Configuration File]].&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1852</id>
		<title>Software Updates and Upgrades</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1852"/>
				<updated>2014-06-27T20:24:04Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* The structure of the &amp;quot;data&amp;quot; array */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
[[File:Update_Process.svg|200px|thumb|right|Top-level overview of the update process]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
The following things happen in typical a software update (or upgrade):&lt;br /&gt;
* Files that are new in the new version are added&lt;br /&gt;
* Files that have been changed in the new version are replaced with the new versions of those files &lt;br /&gt;
* Files that are no longer used in the new version are deleted&lt;br /&gt;
* Changes to the database are made to make the database compatible with the new version of X2Engine&lt;br /&gt;
* The main configuration file, &amp;lt;tt&amp;gt;X2Config.php&amp;lt;/tt&amp;gt;, is edited to reflect the new state of the application.&lt;br /&gt;
* X2Engine data caches are cleared&lt;br /&gt;
&lt;br /&gt;
These operations are typically handled by the updater utility component [[x2doc:UpdaterBehavior|UpdaterBehavior]] and controlled via the in-app web-based updater or the command line updater. This article will cover both how to use said interfaces to the updater, and how to perform updates manually or with the &amp;quot;offline&amp;quot; method if the need arises, in addition to troubleshooting and recovering from an update that did not complete cleanly.&lt;br /&gt;
&lt;br /&gt;
= Disclaimer =&lt;br /&gt;
This article covers the standard and recommended update/upgrade methods, in addition to alternate methods and supplemental procedures. As general rules and precautions for using this article:&lt;br /&gt;
# ''If you do not fully and completely understand any step of any instructions given in this article, '''do not''' follow them''; ask for help instead on [http://x2community.com the forums] or through contacting X2Engine Technical Support if you are an existing customer.&lt;br /&gt;
# You should always make a backup of all of your files ''and'' a full backup copy of your database, preferably in a format (i.e. SQL script) that can be used to create a verbatim/carbon copy of the original database, with all of the features of the original schema, '''before''' attempting any steps of the update process manually.&lt;br /&gt;
# Some of the procedures outlined in this article involve manually creating or modifying files. Note that the same filesystem prerequisite for installation, namely that the file ownership must match the user under which PHP executes on the web server, applies to these steps.&lt;br /&gt;
&lt;br /&gt;
Furthermore, '''in regard to uploading files:''' note that any instructions to upload folders into X2Engine, and replace/merge contents, refer to replacing files in X2Engine with files in the archive at analogous paths. For example, if you have a zip file that contains a folder &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, and inside of that is a folder &amp;lt;tt&amp;gt;components&amp;lt;/tt&amp;gt;, and inside of that is a file &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt;, and the instructions are to upload/replace in X2Engine, you should upload &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt; to replace the existing file of the same name in &amp;lt;tt&amp;gt;protected/components&amp;lt;/tt&amp;gt; inside of X2Engine.&lt;br /&gt;
&lt;br /&gt;
X2Engine Inc. cannot be held accountable for damage/errors caused by any missteps or mistakes in manually-performed update operations. If in doubt, make a sandbox/clone of your X2Engine installation by copying the files and database to a different server, and test performing the operations on that before proceeding with your production install of X2Engine.&lt;br /&gt;
&lt;br /&gt;
= Update Notifications =&lt;br /&gt;
[[File:Update Notification.png|200px|frame|right|System flash message displaying software update notification.]]&lt;br /&gt;
To get notified of software updates, unless already using X2Engine Professional Edition, one must first &amp;quot;subscribe&amp;quot; to them. This can be done via checking the box &amp;quot;Notify me of software updates&amp;quot; in the installation form. If X2Engine is already installed, this can be done by going to &amp;quot;Updater Settings&amp;quot; from the Admin page and enabling them. If using Professional Edition, these steps are not not necessary.&lt;br /&gt;
&lt;br /&gt;
Once that has been completed, set the update notification interval as desired in &amp;quot;Updater Settings&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Update Methods =&lt;br /&gt;
There are, in essence, just three basic methods of updating, and variations on each. The three main methods are: &lt;br /&gt;
# web updater,&lt;br /&gt;
# command line updater, and&lt;br /&gt;
# manually.&lt;br /&gt;
In each case, an update package from the update data server is used as the authoritative list of changes to apply, but in the non-manual methods, X2Engine's own updater class does all the work applying changes from said package.&lt;br /&gt;
&lt;br /&gt;
In web and command line updates, there is also the option to first download and unpack the package manually (see [[#Performing_.22Offline.22_Updates|performing &amp;quot;offline&amp;quot; updates]]). This circumvents the need for the local server (on which X2Engine is hosted) to be connected to the internet. Similarly, in the case of manual updates, it is not necessary for the web server to actually have an internet connection.&lt;br /&gt;
&lt;br /&gt;
== Using the Web Updater ==&lt;br /&gt;
[[File:Updater-1.png|300px|thumb|right|The updater utility page.]]&lt;br /&gt;
The web updater can be accessed by any of the following methods:&lt;br /&gt;
* Using the link in the notification message at the top of the window in versions later than 3.5.5&lt;br /&gt;
* By going to the Admin page and clicking the &amp;quot;Update&amp;quot; button at the top, in earlier versions, with update notifications enabled&lt;br /&gt;
* Through &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
First, navigate to the web updater page, and '''do not''' cancel the request (stop or hit your browser's &amp;quot;back&amp;quot; button) until the page has loaded. If the updater utility has undergone changes and needs to self-update to be compatible with the software updates server, this is when it will do so. Thus, it is critical that you do not interrupt this process. If the page does not load, or displays the error message that dependencies could not be retrieved, you may have to [[#Performing_.22Offline.22_Updates|perform an &amp;quot;offline&amp;quot; update]].&lt;br /&gt;
&lt;br /&gt;
There are three ways of getting to the updater page. One is to simply click the link given in the update notification flash message, if available. The other is to click on the &amp;quot;Update&amp;quot; button at the top of the admin page. If software update notifications are not enabled, one can manually check for and install an update by going to &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
Next, make a backup of the database and files (recommended), follow the instructions on the page, and click the &amp;quot;Update&amp;quot; button when ready.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
It is generally quick and is the easiest method to use, and it does not require shell/SSH access to the web server.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
It is not recommended for use on large installations of X2Engine, i.e. containing over a million records, or for updating from very old versions (i.e. a version more than five months old). This is because the final update process may be cut short by server timeout or an interruption in the network connection. This can result in an incomplete/broken update that requires recovering manually (a potentially long process) at best, or actual loss of data at worst. &lt;br /&gt;
&lt;br /&gt;
==== Explanation ====&lt;br /&gt;
The inherent weakness of the web updater (or any web-based updater to any web application) is that it must always rely upon web requests to the server, and it thus must perform extremely critical operations for the update all within the scope of a web request. Web requests, it goes without saying, are not always reliable. For example, one's internet connection might get cut off due being on a wireless network and encountering interference. Furthermore, if any operations take an exceedingly long amount of time, e.g. because of the size of the data set (and thus indexes) on which the update must operate, or the number of database changes that need to be applied, the web request for those operations may take longer than the PHP execution time limit as configured on the webserver. This would potentially cut short the execution of a critical update stage, and thus the update would require manual recovery at best, or completely restoring  X2Engine to a backup at worst.&lt;br /&gt;
&lt;br /&gt;
==== How to Avoid Timeouts ====&lt;br /&gt;
You can usually get around this by setting the value of &amp;lt;tt&amp;gt;'''max_execution_time'''&amp;lt;/tt&amp;gt; to a higher value in the web server's PHP configuration. It is highly recommended that you either disable the timeout or set it to a very generous value (such as 300 or more seconds) while attempting to update via the web updater, especially if you are many versions behind or have a high volume of data in your CRM.&lt;br /&gt;
Also, it is generally always a good idea to create a backup copy ''before'' updating, and to download it.&lt;br /&gt;
&lt;br /&gt;
==== Recovering From Crashes ====&lt;br /&gt;
If you downloaded the backup copy of your database, then after you encounter a failure/timeout, you can use the .sql backup script to restore the database to its original form. However, be aware, that if there are any '''&amp;lt;tt&amp;gt;CREATE TABLE&amp;lt;/tt&amp;gt;''' commands in the update data (expand the ''Database Commands to Run'' list to see), you will need to manually drop the created tables in order to once again make your installation compatible with the update.&lt;br /&gt;
&lt;br /&gt;
== Using the Command Line Updater ==&lt;br /&gt;
This update method uses all of the same thoroughly-tested underpinnings and processes as the web updater, but does so all within a system command to the Yii console.&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
While in via SSH, or in a terminal emulator, or in a script, run the following command in the &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt; subdirectory of X2Engine:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./yiic update app&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the following optional arguments to the command line updater:&lt;br /&gt;
;&amp;lt;tt&amp;gt;--force&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; for this option (i.e. &amp;lt;tt&amp;gt;--force=1&amp;lt;/tt&amp;gt;) to proceed with the update even if compatibility issues were detected. Default: 0 (halt if compatibility issues were detected).&lt;br /&gt;
;&amp;lt;tt&amp;gt;--backup&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that a database backup should be performed first before updating, &amp;quot;0&amp;quot; to proceed without creating a backup first. Default: 1.&lt;br /&gt;
;&amp;lt;tt&amp;gt;--lock&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that the application should be locked during the final installation of the update package to prevent data entry during the sensitive moments when the database is being altered and the files updated, &amp;quot;0&amp;quot; to permit use even when changes to the app are being applied. Default: 0.&lt;br /&gt;
&lt;br /&gt;
If running the command gives you the following message:&lt;br /&gt;
&amp;lt;pre&amp;gt;The updater is now up-to-date and compliant with the updates server. Re-run the command to proceed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
That indicates that the updater first needed to self-update, and succeeded in doing so. You should then run the same command again to complete the update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Safer to use on large, mission-critical systems. The success of a software update (or upgrade) does not hinge upon a single web request completing successfully.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
More difficult to use; requires shell access to the web server. The console command must be run as the same system user that owns all the files and directories of X2Engine, in order to circumvent permission issues when manipulating file system objects.&lt;br /&gt;
&lt;br /&gt;
== Using the Automatic Updater ==&lt;br /&gt;
[[File:Auto-Update.png|400px|thumb|right|Auto-update scheduler on the Updater Settings page]]&lt;br /&gt;
This method uses the command line updater, but runs the command on your behalf using the local cron daemon. To configure, go to the Updater Settings Page, check the box titled &amp;quot;Update Automatically&amp;quot;, and save. If you have Professional Edition, you can manage the update schedule in addition to other automated tasks from the Cron Table settings page, which can also be found under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Automates the entire process of updating.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
Only available on UNIX-like systems that have a cron daemon, and which permit user access to the cron table.&lt;br /&gt;
&lt;br /&gt;
== Performing &amp;quot;Offline&amp;quot; Updates ==&lt;br /&gt;
In some server environments, the updater may not be able to access the updates server by itself, due to (for example) the PHP or network configuration, or because the &amp;quot;zip&amp;quot; PHP extension (requried) is unavailable or has been disabled. It is in these situations necessary to download the update package manually, unpack on the server (or unpack and upload the files to the server), and then run the updater. When performing updates in this way, the updater will not need to access the updates server, but will use the manually-downloaded and extracted package files present in the local filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Refreshing and Updating the Updater Utility ===&lt;br /&gt;
Before starting, note that the updater itself may have undergone changes; the X2Engine update process is subject to change. To address this technical challenge, the updater utility within X2Engine would normally first attempt to download the latest version of itself, in order to be compatible with the updates server. However, this sometimes will not work properly. If your X2Engine installation is running in a hosting environment that prevents it from making outbound HTTP requests (the [[requirements:|requirements check script]] should tell you if this is the case), you will need to do this manually. Also, in version 3.5.5, there was a bug in the updater utility's self-update methods.&lt;br /&gt;
&lt;br /&gt;
To update the updater utility:&lt;br /&gt;
# '''Determine the version of the updater utility package''' by visiting [https://x2planet.com/installs/updates/updateCheck this link]. You should see a page that's entirely blank except for a version number.&lt;br /&gt;
# '''Download the updater package.''' In versions 3.6 and later, a download link for this should be available through the ''Updater Settings'' page. Otherwise, use the following URL (if in Open Source Edition): &amp;lt;pre&amp;gt;https://x2planet.com/installs/updater.zip&amp;lt;/pre&amp;gt; Otherwise, if using Professional Edition, paste the following url, after replacing {key} with your license key, into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/updater-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# '''Extract the downloaded zip archive and upload''' the &amp;quot;protected&amp;quot; folder to your server. Merge folders and overwrite existing files when prompted.&lt;br /&gt;
# '''Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor''' and find the line that looks like this:&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$updaterVersion = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# '''Change the text inside the pair of single quotes''' on that line so that it is the current updater version obtained in step #1. &lt;br /&gt;
# '''Save the file.'''&lt;br /&gt;
&lt;br /&gt;
=== Obtaining the Update Package ===&lt;br /&gt;
First, use the link in the ''Updater Settings'' page entitled &amp;quot;Latest Update Package for Version...&amp;quot;, to download the update package. If the link in the Updater Settings page does not work, use the following URL to download the update package (if using Open Source Edition), replacing &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; with your X2Engine installation's version:&lt;br /&gt;
&amp;lt;pre&amp;gt;https://x2planet.com/installs/updates/{version}/none&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should receive, from that URL, a zip archive file called &amp;quot;update.zip&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If using Professional Edition, and your download link is not working, contact [mailto:customersupport@x2engine.com customer support].&lt;br /&gt;
&lt;br /&gt;
=== Unpacking the Update ===&lt;br /&gt;
[[File:Update-in-Progress.png|200px|frame|right|System flash message displaying notification of pending update.]]&lt;br /&gt;
The update package will be a zip archive. The ''contents'' of this archive must be extracted ''into'' a folder called &amp;quot;update&amp;quot; in the installation directory. So, in other words, after extracting and uploading to the server, there should be a folder called &amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt; alongside &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;framework&amp;lt;/tt&amp;gt; (etc). Within that folder (&amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt;) should be the following contents:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A file containing important data to be used by the updater&lt;br /&gt;
;&amp;lt;tt&amp;gt;contents.md5&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A message digest file containing MD5 checksums of all files in the archive excluding itself.&lt;br /&gt;
;&amp;lt;tt&amp;gt;source&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A sub-folder/directory, whose internal structure mimics that of X2Engine, containing files that were changed in the update. If there were no files changed (i.e. only database changes were in the update), this directory will not be present.&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A directory containing flat data files. These files are typically not used by the updater, but are useful if performing manual updates or recovery.&lt;br /&gt;
&lt;br /&gt;
=== Applying the Update ===&lt;br /&gt;
[[File:Steps-post-Unpack.png|200px|frame|right|Steps of the update process in the event of manually-extracted package (or resuming a preexisting update)]]&lt;br /&gt;
After unpacking, if you run the command line updater, it should (if no problems arise, i.e. incompatible package version) apply the update straight away. Otherwise, log into X2Engine, and you should see a system flash message notifying you of an update in progress. This message shows up whenever there is an update package folder (&amp;quot;update&amp;quot;) present on the server. If you then go to the updater page, once the page has finished loading, you should see that there only remain the steps &amp;quot;Review and confirm changes&amp;quot;, &amp;quot;Apply changes&amp;quot; and &amp;quot;Reload&amp;quot; remain to be completed. To complete the update, when ready, click &amp;quot;Update&amp;quot;; otherwise, to delete the &amp;quot;update&amp;quot; folder and cancel the update, click &amp;quot;Cancel&amp;quot; either on the updater page or in the system flash message.&lt;br /&gt;
&lt;br /&gt;
== Performing Updates Manually ==&lt;br /&gt;
While it is preferable to avoid manually applying changes to X2Engine, and you may never need to, it may in some circumstance or another actually be necessary to do so. For all such situations, the raw update data files generated by the X2Engine update build process are bundled with every update package to help you perform the operations described in [[#Introduction|&amp;quot;Introduction&amp;quot;]]. This section will describe how to perform each of those operations. &lt;br /&gt;
&lt;br /&gt;
=== Prerequisites ===&lt;br /&gt;
First and foremost, all shell commands listed here, and in general, any operations that create or modify files, ''must be run as the same system user under which the server runs PHP scripts.'' Otherwise, the ownership and/or permissions of files and directories will have to be changed after completing all changes.&lt;br /&gt;
&lt;br /&gt;
To obtain the update package (which will be necessary), refer to &amp;quot;[[#Obtaining_the_Update_Package|Obtaining the Update Package]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Furthermore, it is strongly advised that you first make a back-up copy of all files and of X2Engine's database. Your database backup would best be a full, exact copy, such as one that would be made by [http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html mysqldump], or the built-in export tools of [http://www.phpmyadmin.net/home_page/index.php PHPMyAdmin].&lt;br /&gt;
&lt;br /&gt;
The X2Engine global import/export utility (&amp;quot;Import All Data&amp;quot;/&amp;quot;Export All Data&amp;quot;) in Admin '''is not recommended by itself as the sole backup tool'''. This is especially important to note because of how this tool cannot re-create ancillary data such as tags or relationships, or user roles. However, using it to ''supplement'' your standard database backup method will prove useful in the event that you:&lt;br /&gt;
# don't have any custom roles, relationships, tags, etc.&lt;br /&gt;
# cannot manage to update X2Engine with success, and&lt;br /&gt;
# wish to start over with a fresh installation.&lt;br /&gt;
In such a case, you will then be able to import your old data into the new installation.&lt;br /&gt;
&lt;br /&gt;
=== Introduction: Data Files ===&lt;br /&gt;
In [[#Unpacking_the_Update|Unpacking the update]], a rough overview of an update package's contents were given. In this section, the anatomy and purpose of each of the data files will be introduced.&lt;br /&gt;
&lt;br /&gt;
Included in every update package is two copies of the following data:&lt;br /&gt;
* Files to copy&lt;br /&gt;
* Files to delete&lt;br /&gt;
* Database changes to enact&lt;br /&gt;
* Migration scripts to run, if any&lt;br /&gt;
&lt;br /&gt;
The first, immediately visible place where all of this data is stored is in the file &amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;, as a JSON-encoded array. See the next section for how to use this file.&lt;br /&gt;
&lt;br /&gt;
The second place, which can be useful to users who do not wish to automate or script any step of a manual update, is in the files contained in the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; directory. Each file is named after the version to which it corresponds, and contains the data required to update ''to'' that version ''from the version immediately preceding it.''' Thus, the versions you should see in each file name are the versions above your X2Engine installation's current version, up to and including the version being updated to.&lt;br /&gt;
&lt;br /&gt;
In an update, the directory will contain the following subdirectories:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of files to copy. The format is each line containing a file path in the list.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of obsolete files for that version which will need to be deleted. The format is the same as in &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts corresponding to each version. Each file is formatted such that it can be directly run on the database as an SQL script, although this is not generally recommended. The list of SQL commands to run is a of the following format: &lt;br /&gt;
  [query 1];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [query 2];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [etc]&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts with similar naming and formatting conventions to the &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; scripts. See [[#The_structure_of_the_.22data.22_array|The structure of the &amp;quot;data&amp;quot; array]] for explanation of the purpose of &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== How to use the manifest for updating manually ===&lt;br /&gt;
If you so choose, you may write a script to perform all of the operations for an update in one fell swoop. If so, and if the preferred scripting language of choice has a JSON-parsing library available, you will be able to save yourself a lot of effort by using the main data file (&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;) to load all of the necessary data for the update.&lt;br /&gt;
&lt;br /&gt;
==== The structure the manifest ====&lt;br /&gt;
In updates, the contents of the plain-text file manifest.json are a JSON-encoded object containing the following properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the data corresponding to each intermediate version. It is, in essence, all the data stored in the files in &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt;. If the update is for an installation of X2Engine that is only one version behind, it will contain only one element. Each element of this array is itself an array containing respective data for the intermediate version.&lt;br /&gt;
;&amp;lt;tt&amp;gt;versions&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array listing the version numbers of each intermediate version in the update. Similar to &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, this array will contain only one element if updating to the next version, and that version ''will be the next version''.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of deletions of obsolete files across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of files added or changed across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''from'', or in other words, the version of X2Engine before the package is applied. '''This must be the same as the version of installation to which the package is being applied.''' If this is not so, the updater utility would exit immediately, as applying the changes in the update would have potentially harmful results to data and/or compatibility with future updates.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''to'', or in other words, the version of X2Engine before the package is applied.&lt;br /&gt;
;&amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the version of the updater utility for which the package was compiled. If the structure of the update package or of the manifest changes, the updater must also change to accommodate and recognize the changes; else it might try to access properties that don't exist, or ignore important ones that do. This is the single property of manifest.json that should never change.&lt;br /&gt;
;&amp;lt;tt&amp;gt;scenario&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, which should be &amp;quot;update&amp;quot; if the package is for an update. In the case of upgrades, which use most of the same functionality of the updater, it should be &amp;quot;upgrade&amp;quot;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation after the package is applied. It should be &amp;quot;opensource&amp;quot; for open source edition, &amp;quot;pro&amp;quot; for professional edition. In the case of an upgrade package, it differs from the &amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation before the package is applied. &lt;br /&gt;
;&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An integer denoting the timestamp of the release date of the version of X2Engine denoted by &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The structure of the &amp;quot;data&amp;quot; array ====&lt;br /&gt;
In updates, each version's data, contained in each element of the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, contains the following nested array properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files added for the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files deleted in the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of SQL commands to bring the database structure and contents into compliance with the version from the previous version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A prerequisite list of SQL commands that should be run before those in &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, and if any fail, the failures should be ignored. These are added to updates whenever it is necessary to normalize the database schema, i.e. in the event that some installations' databases may not be consistent with others, but the commands might fail on every other system. These SQL commands were intended to be &amp;quot;attempted&amp;quot;; they are not guaranteed to succeed on all installations. An example of when this is necessary would be adding a foreign key constraint when in previous versions the storage engine was left to the database's default settings in old versions. MyISAM does not natively support foreign key constraints. Thus, to standardize this across all installations, including installations on servers where the storage engine defaulted to MyISAM, the constraint must first be dropped (in a &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; command) before the relevant tables are changed to the InnoDB storage engine, and the constraint is added back again, in the usual &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;version&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The version number&lt;br /&gt;
;&amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The edition&lt;br /&gt;
;&amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of PHP scripts that would be run in an update for this version. These are a subset of fileList and are typically used for advanced database operations, i.e. restructuring values of JSON-encoded fields, which are extremely impractical (or impossible) to perform using raw SQL commands. The files contained in the list all have paths that look like &amp;quot;&amp;lt;tt&amp;gt;protected/migrations/{version}/{timestamp}-name-of-operations.php&amp;lt;/tt&amp;gt;&amp;quot;. Note, if this array is not empty, and the &amp;lt;tt&amp;gt;'''runmigrationscript'''&amp;lt;/tt&amp;gt; Yii command does not work (or isn't available yet), you may need to reverse-engineer or otherwise copy and re-purpose/re-write any migration scripts listed in order to perform their necessary operations. This is because they often depend on running within a Yii Framework environment, i.e. will contain references to components of the Yii application singleton such as &amp;quot;db&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Also note that in upgrades, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; are replaced by &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; (respectively), and &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; are missing (because they aren't necessary).&lt;br /&gt;
&lt;br /&gt;
==== What you will need for the update, if using the manifest ====&lt;br /&gt;
''Among this data, what you will need:'' &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, which will give you the full list of files that need to be deleted, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, which will give you the full list of files to copy from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory, &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;, which give you values with which to update the main X2Engine configuration file, and, within each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, the sub-array properties &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;, with which you will construct the list of database commands to run. &lt;br /&gt;
&lt;br /&gt;
A procedure for manually updating X2Engine, with data from manifest.json, thus might look like this:&lt;br /&gt;
# For each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; (or in other words, each version's changes):&lt;br /&gt;
## Run all the SQL commands in its corresponding &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; sub-array, ignoring errors&lt;br /&gt;
## Run all SQL commands in its &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; array&lt;br /&gt;
## Perform database operations that would be equivalent to those performed in migration scripts&lt;br /&gt;
# For each file in the cumulative list &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, copy it from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory into its analogous location in the installation directory of X2Engine, creating parent directories as necessary if they don't already exist.&lt;br /&gt;
# Delete all files in the cumulative list &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; from the installation directory&lt;br /&gt;
# Clear the data cache files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt; and the temporary asset folders in &amp;lt;tt&amp;gt;assets/&amp;lt;/tt&amp;gt;&lt;br /&gt;
# Clear all session data (&amp;lt;tt&amp;gt;x2_sessions&amp;lt;/tt&amp;gt;) and auth cache data (&amp;lt;tt&amp;gt;x2_auth_cache&amp;lt;/tt&amp;gt;).&lt;br /&gt;
# Update the main X2Engine configuration (&amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt;) file, setting:&lt;br /&gt;
## &amp;lt;tt&amp;gt;$buildDate&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt; property,&lt;br /&gt;
## &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt; property, and &lt;br /&gt;
## &amp;lt;tt&amp;gt;$updaterVersion&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
&lt;br /&gt;
=== Enacting Database Changes ===&lt;br /&gt;
Inside the &amp;lt;tt&amp;gt;update/sqlList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/sqlForce&amp;lt;/tt&amp;gt; folder is a list of &amp;lt;tt&amp;gt;.sql&amp;lt;/tt&amp;gt; files, named according to their corresponding version's update, that can essentially be run as SQL scripts to perform the update. Note, however, that '''if the update traverses more than one version, the scripts must be run in the order of their corresponding version.''' This is because, in the database changes for any version, it is assumed that the database schema and contents conform to the version that immediately preceded it. The PHP function [[phpfunc:version-compare|version_compare]], and the method &amp;quot;[http://pythonhosted.org/setuptools/pkg_resources.html#parsing-utilities parse_version]&amp;quot;, in the pkg_resources module of setuptools, should correctly identify which version should come first. Note, the [[#The_structure_the_manifest|&amp;quot;versions&amp;quot; property of the manifest]] will contain the list of versions in the correct order.&lt;br /&gt;
&lt;br /&gt;
Furthermore, note, &amp;quot;sqlList&amp;quot; and &amp;quot;sqlForce&amp;quot; may not encompass all necessary database changes; if there are any migration scripts in the update, the operations they perform will similarly need to be performed in the current update (see the description of &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; in [[#The_structure_of_the_.22data.22_array|&amp;quot;the structure of the 'data' array&amp;quot;]]).&lt;br /&gt;
&lt;br /&gt;
In summary, for each version traversed in the update, the following operations should be run in order, with &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version number of the version in question:&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlForce/{version}.sql&amp;lt;/tt&amp;gt; file,''' in the order they appear in the file. Ignore if any commands fail. Note, if passed to the MySQL client as a script, any failures might trigger a halt in execution and thus skip the remainder of the commands.&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlList/{version}.sql&amp;lt;/tt&amp;gt; file.'''&lt;br /&gt;
# '''Perform operations equivalent to those in each of the migration scripts, if any.''' The first part of a script file name is the timestamp it was added to the codebase, and this also dictates the order in which they should be run.&lt;br /&gt;
&lt;br /&gt;
=== Copying new files ===&lt;br /&gt;
Files added/changed in the new version are stored in the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory. In an update, they should be copied into the installation directory, replacing any existing files of the same name/path. If the &amp;lt;tt&amp;gt;rsync&amp;lt;/tt&amp;gt; utility is available on the system, you can use one command to safely copy these files into the installation directory as follows (executed in that same directory):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rsync -avc update/source/ ./&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, you will need to use &amp;lt;tt&amp;gt;cat&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;cp&amp;lt;/tt&amp;gt;, two utilities that should be available on every UNIX-like system, and the lists of files added in each intermediate version. These lists are stored in the folder &amp;lt;tt&amp;gt;update/data/fileList&amp;lt;/tt&amp;gt; and named like &amp;quot;{version}.txt&amp;quot; (&amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version to which it applies), each file on its own line in that file. Run the following command, ignoring errors:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/fileList/*; do for l in $(cat $f); do cp update/source/&amp;quot;$l&amp;quot; ./&amp;quot;$l&amp;quot; ; done ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting old files ===&lt;br /&gt;
Files to be deleted are listed in a similar manner as the files to be added: one file per line. The list of files to delete are located at files with paths that look like this: &amp;lt;tt&amp;gt;update/data/deletionList/{version}.txt&amp;lt;/tt&amp;gt;&lt;br /&gt;
So, for an update from 5.5 to 5.7 with intermediate version 5.6 (for instance), the files would be &amp;lt;tt&amp;gt;update/data/deletionList/5.6&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/data/deletionList/5.7&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is only one version through which to update, the operation is very simple, and can be performed in one UNIX/Linux (bash) shell command as follows (if the version is 3.6.3, for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
xargs -a update/data/deletionList/3.6.3.txt -d&amp;quot;\n&amp;quot; rm -fv&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, if there are multiple versions, this may be a more tricky proposition. The necessity arises to create a cumulative list, because there might be the possibility that a file is deleted in one version and re-established in a new version. Nevertheless, while this is possible, it is highly unlikely, and in most cases it is safe to simply iterate over all the &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; data files and delete all the files listed therein. As a bash command:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/deletionList/*; do xargs -a $f -d&amp;quot;\n&amp;quot; rm -fv ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Editing the Configuration File ===&lt;br /&gt;
This final step in the update process, before refreshing, should not be done manually unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a fresh installation at the same version as the one to which you have updated.&lt;br /&gt;
&lt;br /&gt;
To update the configuration manually:&lt;br /&gt;
# Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor&lt;br /&gt;
# Find the line with the &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; variable in it. It should look like this (but with the version from which you are updating): &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$version = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change the value in quotes to the current version being updated to, and save the file.&lt;br /&gt;
&lt;br /&gt;
=== Clearing caches and refreshing the application ===&lt;br /&gt;
It is generally always a good idea to clear the application's server-side cache and other ephemeral files/data after updating. This is taken care of automatically by the built-in updater, but barring proper use and functioning of that, the operations can be performed manually as follows: &lt;br /&gt;
;clear the data cache&lt;br /&gt;
: delete all &amp;quot;&amp;lt;tt&amp;gt;.bin&amp;lt;/tt&amp;gt;&amp;quot; files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -fv protected/runtime/cache/*.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear web assets&lt;br /&gt;
: delete all sub-folders of &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt; by running this command (in the installation directory): &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -r assets/*/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear the &amp;quot;auth cache&amp;quot;&lt;br /&gt;
: Flush the auth cache table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;&lt;br /&gt;
DELETE FROM `x2_auth_cache` WHERE 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;log all users out&lt;br /&gt;
: Flush the sessions table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;DELETE FROM `x2_sessions` WHERE 1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Upgrading =&lt;br /&gt;
&lt;br /&gt;
== Using the Web Interface ==&lt;br /&gt;
[[File:Software-Upgrade-Registration.png|400px|thumb|right|Software upgrade page, in the first step (registration)]]&lt;br /&gt;
To upgrade to Professional Edition&lt;br /&gt;
# Purchase X2Engine Professional Edition Download. Note the registration info in the activation email.&lt;br /&gt;
# Go to the Admin page in X2Engine and find &amp;quot;Upgrade X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot;.&lt;br /&gt;
# In the ''Upgrade'' page, enter your registration info into the form and click &amp;quot;Register&amp;quot; to continue. Once complete, you will see a list of changes similar to those displayed when performing an update.&lt;br /&gt;
# Review the changes to be applied and then proceed by clicking &amp;quot;Upgrade&amp;quot;, similar to performing an update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Offline&amp;quot; Upgrading ==&lt;br /&gt;
This works in exactly the same way as offline updating. However, to obtain your upgrade package, which you will extract into a folder called &amp;quot;update&amp;quot; on the X2Engine web server, contact [mailto:customersupport@x2engine.com customer support]. Furthermore, you will need to run the following database command on your X2Engine database when done with the upgrade, replacing &amp;lt;tt&amp;gt;{product key}&amp;lt;/tt&amp;gt; with your product key, to allow your installation to receive Professional Edition software updates:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{product key}' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Manually Upgrading ==&lt;br /&gt;
The process of upgrading is very similar to that of updating, and uses all of the same underpinnings as the updater. Thus, almost all of the above information and procedures will apply to upgrades. However, note the following crucial differences:&lt;br /&gt;
&lt;br /&gt;
* '''X2Engine must be at the latest available version in order to upgrade.'''&lt;br /&gt;
* '''To obtain the upgrade package, you must be an owner of an X2Engine Professional Edition Download-type license.''' Contact [mailto:customersupport@x2engine.com customer support] for further details.&lt;br /&gt;
* '''There will be only one &amp;quot;intermediate&amp;quot; version.''' The changes to apply will move X2Engine from a version to that same version, applying instead the necessary changes to switch its ''edition'' from Open Source to Professional.&lt;br /&gt;
* '''There are no file deletions.'''&lt;br /&gt;
* '''The properties of the package manifest differ slightly.''' In upgrades, the properties of the data object are &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, etc. Note also, the update data is in aptly-named sub-folders of &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt; as well, and there are no &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; lists to handle.&lt;br /&gt;
* '''The configuration is not changed.''' Instead, the value stored in the field &amp;lt;tt&amp;gt;unique_id&amp;lt;/tt&amp;gt; of the admin record in the database table &amp;lt;tt&amp;gt;x2_admin&amp;lt;/tt&amp;gt; must be changed to the license key, and the &amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt; field must be changed to &amp;quot;pro&amp;quot;. To perform these both, run the following command on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''It is not necessary to clear caches or user sessions when done.''' These operations are performed in updates to eliminate ephemeral (session-ingrained) data that may no longer be applicable and thus cause problems. Upgrades will rarely, if ever, invalidate this data.&lt;br /&gt;
&lt;br /&gt;
= Updating to Beta Versions = &lt;br /&gt;
Beginning at version 4.1.2, after much advocacy to adopt this practice, X2Engine began a two-phase release process in which each version would be released first as a beta, then to the broader public after initial bug finds. The purpose behind this is to avoid updating production installations of X2Engine to new versions that have had zero public exposure, and thus avoid introducing bugs that were not found in initial QA testing (due to a incompatibility with a particular server environment or configuration of X2Engine) into sensitive production environments.&lt;br /&gt;
&lt;br /&gt;
By default, the updater will not update to beta versions. The updater must be configured to use the proper URL on the update server for beta release data, by the following procedure:&lt;br /&gt;
# Create a file &amp;quot;constants-custom.php&amp;quot; in the root level of X2Engine. You can easily do this by renaming &amp;quot;constants-custom.example.php&amp;quot; to that name (remove &amp;quot;.custom&amp;quot;).&lt;br /&gt;
# Look for the following line, and in it, change &amp;quot;false&amp;quot; to &amp;quot;true&amp;quot;: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('X2_UPDATE_BETA') or define('X2_UPDATE_BETA',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The advantage of enabling beta releases is that new features will be available much sooner. Additionally, fixes to bugs will also be available sooner. The main disadvantage of enabling beta releases is that they are never guaranteed to be as stable as non-beta releases. We encourage users to clone their installations of X2Engine, if able, and use beta versions on them, and report any issues they encounter for their particular server environment/special configuration/custom implementation of X2Engine. This way, any issues that arise can be fixed in the stable release. The more users participate in beta testing, the more stable the non-beta releases will become.&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
== User interface (UI) &amp;quot;bugs&amp;quot; after the update ==&lt;br /&gt;
It is extremely common (and easily remedied) that after an important update, certain features of the user interface of X2Engine may not work properly. This is caused, in nearly all cases, by the web browser's cache, which contains out-of-date copies of X2Engine's essential user interface files. To solve these problems, the first step to try is clearing the cache. A step-by-step guide to doing so is given in [[wikihow:Clear-Your-Browser%27s-Cache]].&lt;br /&gt;
&lt;br /&gt;
== Errors in the updater itself == &lt;br /&gt;
When all else fails, the updater may have to be patched due to an irreconcilable backwards compatibility issue. Fortunately, if one has read/write access to the source files on the webserver, this is straightforward to fix. See [[#Refreshing_and_Updating_the_Updater_Utility|Refreshing and Updating the Updater Utility]].&lt;br /&gt;
&lt;br /&gt;
== Professional Edition not in effect after upgrading ==&lt;br /&gt;
This may be caused by Professional Edition not getting &amp;quot;switched on&amp;quot; properly, despite all the necessary filesystem and database changes already being in effect. First, to see if this scenario applies to you, look for a folder &amp;quot;&amp;lt;tt&amp;gt;reports&amp;lt;/tt&amp;gt;&amp;quot; inside &amp;lt;tt&amp;gt;protected/modules&amp;lt;/tt&amp;gt;, and a table &amp;lt;tt&amp;gt;x2_reports&amp;lt;/tt&amp;gt; in the X2Engine database. If you find these things, you can try the following to enable Professional Edition:&lt;br /&gt;
# Find the file &amp;lt;tt&amp;gt;constants.php&amp;lt;/tt&amp;gt; in the installation directory. Open it in a text editor, and look for the following line: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('PRO_VERSION') or define('PRO_VERSION',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; on that line, and save the file.&lt;br /&gt;
# Run the following SQL command (replacing &amp;lt;tt&amp;gt;{license key}&amp;lt;/tt&amp;gt; with your X2Engine license key) on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General Procedures to Try ==&lt;br /&gt;
It may sometimes be possible that the update did not finish cleanly. In such cases, it will be necessary to manually re-perform some of the final operations in the update, described in [[#Introduction|Introduction]]. &lt;br /&gt;
&lt;br /&gt;
=== Refresh the Source Code ===&lt;br /&gt;
This step is not really the best step to try first, as it is usually more time-consuming. It entails re-uploading the entire application, in order to reset all non-ephemeral (source code) files to what they should be in a fresh installation of X2Engine. To do this:&lt;br /&gt;
# Download the refresh package. There should be a download link for this in the ''Updater Settings'' page. Otherwise, if that doesn't work and you're using Open Source edition, use [https://x2planet.com/installs/refresh.zip this link], or if using Professional Edition, paste the following url (after replacing {key} with your license key) into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/refresh-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Upload the ''contents'' of the downloaded zip archive to the installation directory, overwriting existing files.&lt;br /&gt;
&lt;br /&gt;
=== Clear Caches ===&lt;br /&gt;
See &amp;quot;[[#Clearing_caches_and_refreshing_the_application|Clearing Caches...]]&amp;quot; for details.&lt;br /&gt;
&lt;br /&gt;
=== Regenerate the Configuration ===&lt;br /&gt;
Again, this should not be done unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a new installation at the same version as the one you updated to. Refer to [[#Editing_the_Configuration_File|Editing the Configuration File]].&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1851</id>
		<title>Software Updates and Upgrades</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1851"/>
				<updated>2014-06-27T20:22:10Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Prerequisites */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
[[File:Update_Process.svg|200px|thumb|right|Top-level overview of the update process]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
The following things happen in typical a software update (or upgrade):&lt;br /&gt;
* Files that are new in the new version are added&lt;br /&gt;
* Files that have been changed in the new version are replaced with the new versions of those files &lt;br /&gt;
* Files that are no longer used in the new version are deleted&lt;br /&gt;
* Changes to the database are made to make the database compatible with the new version of X2Engine&lt;br /&gt;
* The main configuration file, &amp;lt;tt&amp;gt;X2Config.php&amp;lt;/tt&amp;gt;, is edited to reflect the new state of the application.&lt;br /&gt;
* X2Engine data caches are cleared&lt;br /&gt;
&lt;br /&gt;
These operations are typically handled by the updater utility component [[x2doc:UpdaterBehavior|UpdaterBehavior]] and controlled via the in-app web-based updater or the command line updater. This article will cover both how to use said interfaces to the updater, and how to perform updates manually or with the &amp;quot;offline&amp;quot; method if the need arises, in addition to troubleshooting and recovering from an update that did not complete cleanly.&lt;br /&gt;
&lt;br /&gt;
= Disclaimer =&lt;br /&gt;
This article covers the standard and recommended update/upgrade methods, in addition to alternate methods and supplemental procedures. As general rules and precautions for using this article:&lt;br /&gt;
# ''If you do not fully and completely understand any step of any instructions given in this article, '''do not''' follow them''; ask for help instead on [http://x2community.com the forums] or through contacting X2Engine Technical Support if you are an existing customer.&lt;br /&gt;
# You should always make a backup of all of your files ''and'' a full backup copy of your database, preferably in a format (i.e. SQL script) that can be used to create a verbatim/carbon copy of the original database, with all of the features of the original schema, '''before''' attempting any steps of the update process manually.&lt;br /&gt;
# Some of the procedures outlined in this article involve manually creating or modifying files. Note that the same filesystem prerequisite for installation, namely that the file ownership must match the user under which PHP executes on the web server, applies to these steps.&lt;br /&gt;
&lt;br /&gt;
Furthermore, '''in regard to uploading files:''' note that any instructions to upload folders into X2Engine, and replace/merge contents, refer to replacing files in X2Engine with files in the archive at analogous paths. For example, if you have a zip file that contains a folder &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, and inside of that is a folder &amp;lt;tt&amp;gt;components&amp;lt;/tt&amp;gt;, and inside of that is a file &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt;, and the instructions are to upload/replace in X2Engine, you should upload &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt; to replace the existing file of the same name in &amp;lt;tt&amp;gt;protected/components&amp;lt;/tt&amp;gt; inside of X2Engine.&lt;br /&gt;
&lt;br /&gt;
X2Engine Inc. cannot be held accountable for damage/errors caused by any missteps or mistakes in manually-performed update operations. If in doubt, make a sandbox/clone of your X2Engine installation by copying the files and database to a different server, and test performing the operations on that before proceeding with your production install of X2Engine.&lt;br /&gt;
&lt;br /&gt;
= Update Notifications =&lt;br /&gt;
[[File:Update Notification.png|200px|frame|right|System flash message displaying software update notification.]]&lt;br /&gt;
To get notified of software updates, unless already using X2Engine Professional Edition, one must first &amp;quot;subscribe&amp;quot; to them. This can be done via checking the box &amp;quot;Notify me of software updates&amp;quot; in the installation form. If X2Engine is already installed, this can be done by going to &amp;quot;Updater Settings&amp;quot; from the Admin page and enabling them. If using Professional Edition, these steps are not not necessary.&lt;br /&gt;
&lt;br /&gt;
Once that has been completed, set the update notification interval as desired in &amp;quot;Updater Settings&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Update Methods =&lt;br /&gt;
There are, in essence, just three basic methods of updating, and variations on each. The three main methods are: &lt;br /&gt;
# web updater,&lt;br /&gt;
# command line updater, and&lt;br /&gt;
# manually.&lt;br /&gt;
In each case, an update package from the update data server is used as the authoritative list of changes to apply, but in the non-manual methods, X2Engine's own updater class does all the work applying changes from said package.&lt;br /&gt;
&lt;br /&gt;
In web and command line updates, there is also the option to first download and unpack the package manually (see [[#Performing_.22Offline.22_Updates|performing &amp;quot;offline&amp;quot; updates]]). This circumvents the need for the local server (on which X2Engine is hosted) to be connected to the internet. Similarly, in the case of manual updates, it is not necessary for the web server to actually have an internet connection.&lt;br /&gt;
&lt;br /&gt;
== Using the Web Updater ==&lt;br /&gt;
[[File:Updater-1.png|300px|thumb|right|The updater utility page.]]&lt;br /&gt;
The web updater can be accessed by any of the following methods:&lt;br /&gt;
* Using the link in the notification message at the top of the window in versions later than 3.5.5&lt;br /&gt;
* By going to the Admin page and clicking the &amp;quot;Update&amp;quot; button at the top, in earlier versions, with update notifications enabled&lt;br /&gt;
* Through &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
First, navigate to the web updater page, and '''do not''' cancel the request (stop or hit your browser's &amp;quot;back&amp;quot; button) until the page has loaded. If the updater utility has undergone changes and needs to self-update to be compatible with the software updates server, this is when it will do so. Thus, it is critical that you do not interrupt this process. If the page does not load, or displays the error message that dependencies could not be retrieved, you may have to [[#Performing_.22Offline.22_Updates|perform an &amp;quot;offline&amp;quot; update]].&lt;br /&gt;
&lt;br /&gt;
There are three ways of getting to the updater page. One is to simply click the link given in the update notification flash message, if available. The other is to click on the &amp;quot;Update&amp;quot; button at the top of the admin page. If software update notifications are not enabled, one can manually check for and install an update by going to &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
Next, make a backup of the database and files (recommended), follow the instructions on the page, and click the &amp;quot;Update&amp;quot; button when ready.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
It is generally quick and is the easiest method to use, and it does not require shell/SSH access to the web server.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
It is not recommended for use on large installations of X2Engine, i.e. containing over a million records, or for updating from very old versions (i.e. a version more than five months old). This is because the final update process may be cut short by server timeout or an interruption in the network connection. This can result in an incomplete/broken update that requires recovering manually (a potentially long process) at best, or actual loss of data at worst. &lt;br /&gt;
&lt;br /&gt;
==== Explanation ====&lt;br /&gt;
The inherent weakness of the web updater (or any web-based updater to any web application) is that it must always rely upon web requests to the server, and it thus must perform extremely critical operations for the update all within the scope of a web request. Web requests, it goes without saying, are not always reliable. For example, one's internet connection might get cut off due being on a wireless network and encountering interference. Furthermore, if any operations take an exceedingly long amount of time, e.g. because of the size of the data set (and thus indexes) on which the update must operate, or the number of database changes that need to be applied, the web request for those operations may take longer than the PHP execution time limit as configured on the webserver. This would potentially cut short the execution of a critical update stage, and thus the update would require manual recovery at best, or completely restoring  X2Engine to a backup at worst.&lt;br /&gt;
&lt;br /&gt;
==== How to Avoid Timeouts ====&lt;br /&gt;
You can usually get around this by setting the value of &amp;lt;tt&amp;gt;'''max_execution_time'''&amp;lt;/tt&amp;gt; to a higher value in the web server's PHP configuration. It is highly recommended that you either disable the timeout or set it to a very generous value (such as 300 or more seconds) while attempting to update via the web updater, especially if you are many versions behind or have a high volume of data in your CRM.&lt;br /&gt;
Also, it is generally always a good idea to create a backup copy ''before'' updating, and to download it.&lt;br /&gt;
&lt;br /&gt;
==== Recovering From Crashes ====&lt;br /&gt;
If you downloaded the backup copy of your database, then after you encounter a failure/timeout, you can use the .sql backup script to restore the database to its original form. However, be aware, that if there are any '''&amp;lt;tt&amp;gt;CREATE TABLE&amp;lt;/tt&amp;gt;''' commands in the update data (expand the ''Database Commands to Run'' list to see), you will need to manually drop the created tables in order to once again make your installation compatible with the update.&lt;br /&gt;
&lt;br /&gt;
== Using the Command Line Updater ==&lt;br /&gt;
This update method uses all of the same thoroughly-tested underpinnings and processes as the web updater, but does so all within a system command to the Yii console.&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
While in via SSH, or in a terminal emulator, or in a script, run the following command in the &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt; subdirectory of X2Engine:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./yiic update app&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the following optional arguments to the command line updater:&lt;br /&gt;
;&amp;lt;tt&amp;gt;--force&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; for this option (i.e. &amp;lt;tt&amp;gt;--force=1&amp;lt;/tt&amp;gt;) to proceed with the update even if compatibility issues were detected. Default: 0 (halt if compatibility issues were detected).&lt;br /&gt;
;&amp;lt;tt&amp;gt;--backup&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that a database backup should be performed first before updating, &amp;quot;0&amp;quot; to proceed without creating a backup first. Default: 1.&lt;br /&gt;
;&amp;lt;tt&amp;gt;--lock&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that the application should be locked during the final installation of the update package to prevent data entry during the sensitive moments when the database is being altered and the files updated, &amp;quot;0&amp;quot; to permit use even when changes to the app are being applied. Default: 0.&lt;br /&gt;
&lt;br /&gt;
If running the command gives you the following message:&lt;br /&gt;
&amp;lt;pre&amp;gt;The updater is now up-to-date and compliant with the updates server. Re-run the command to proceed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
That indicates that the updater first needed to self-update, and succeeded in doing so. You should then run the same command again to complete the update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Safer to use on large, mission-critical systems. The success of a software update (or upgrade) does not hinge upon a single web request completing successfully.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
More difficult to use; requires shell access to the web server. The console command must be run as the same system user that owns all the files and directories of X2Engine, in order to circumvent permission issues when manipulating file system objects.&lt;br /&gt;
&lt;br /&gt;
== Using the Automatic Updater ==&lt;br /&gt;
[[File:Auto-Update.png|400px|thumb|right|Auto-update scheduler on the Updater Settings page]]&lt;br /&gt;
This method uses the command line updater, but runs the command on your behalf using the local cron daemon. To configure, go to the Updater Settings Page, check the box titled &amp;quot;Update Automatically&amp;quot;, and save. If you have Professional Edition, you can manage the update schedule in addition to other automated tasks from the Cron Table settings page, which can also be found under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Automates the entire process of updating.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
Only available on UNIX-like systems that have a cron daemon, and which permit user access to the cron table.&lt;br /&gt;
&lt;br /&gt;
== Performing &amp;quot;Offline&amp;quot; Updates ==&lt;br /&gt;
In some server environments, the updater may not be able to access the updates server by itself, due to (for example) the PHP or network configuration, or because the &amp;quot;zip&amp;quot; PHP extension (requried) is unavailable or has been disabled. It is in these situations necessary to download the update package manually, unpack on the server (or unpack and upload the files to the server), and then run the updater. When performing updates in this way, the updater will not need to access the updates server, but will use the manually-downloaded and extracted package files present in the local filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Refreshing and Updating the Updater Utility ===&lt;br /&gt;
Before starting, note that the updater itself may have undergone changes; the X2Engine update process is subject to change. To address this technical challenge, the updater utility within X2Engine would normally first attempt to download the latest version of itself, in order to be compatible with the updates server. However, this sometimes will not work properly. If your X2Engine installation is running in a hosting environment that prevents it from making outbound HTTP requests (the [[requirements:|requirements check script]] should tell you if this is the case), you will need to do this manually. Also, in version 3.5.5, there was a bug in the updater utility's self-update methods.&lt;br /&gt;
&lt;br /&gt;
To update the updater utility:&lt;br /&gt;
# '''Determine the version of the updater utility package''' by visiting [https://x2planet.com/installs/updates/updateCheck this link]. You should see a page that's entirely blank except for a version number.&lt;br /&gt;
# '''Download the updater package.''' In versions 3.6 and later, a download link for this should be available through the ''Updater Settings'' page. Otherwise, use the following URL (if in Open Source Edition): &amp;lt;pre&amp;gt;https://x2planet.com/installs/updater.zip&amp;lt;/pre&amp;gt; Otherwise, if using Professional Edition, paste the following url, after replacing {key} with your license key, into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/updater-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# '''Extract the downloaded zip archive and upload''' the &amp;quot;protected&amp;quot; folder to your server. Merge folders and overwrite existing files when prompted.&lt;br /&gt;
# '''Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor''' and find the line that looks like this:&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$updaterVersion = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# '''Change the text inside the pair of single quotes''' on that line so that it is the current updater version obtained in step #1. &lt;br /&gt;
# '''Save the file.'''&lt;br /&gt;
&lt;br /&gt;
=== Obtaining the Update Package ===&lt;br /&gt;
First, use the link in the ''Updater Settings'' page entitled &amp;quot;Latest Update Package for Version...&amp;quot;, to download the update package. If the link in the Updater Settings page does not work, use the following URL to download the update package (if using Open Source Edition), replacing &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; with your X2Engine installation's version:&lt;br /&gt;
&amp;lt;pre&amp;gt;https://x2planet.com/installs/updates/{version}/none&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should receive, from that URL, a zip archive file called &amp;quot;update.zip&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If using Professional Edition, and your download link is not working, contact [mailto:customersupport@x2engine.com customer support].&lt;br /&gt;
&lt;br /&gt;
=== Unpacking the Update ===&lt;br /&gt;
[[File:Update-in-Progress.png|200px|frame|right|System flash message displaying notification of pending update.]]&lt;br /&gt;
The update package will be a zip archive. The ''contents'' of this archive must be extracted ''into'' a folder called &amp;quot;update&amp;quot; in the installation directory. So, in other words, after extracting and uploading to the server, there should be a folder called &amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt; alongside &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;framework&amp;lt;/tt&amp;gt; (etc). Within that folder (&amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt;) should be the following contents:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A file containing important data to be used by the updater&lt;br /&gt;
;&amp;lt;tt&amp;gt;contents.md5&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A message digest file containing MD5 checksums of all files in the archive excluding itself.&lt;br /&gt;
;&amp;lt;tt&amp;gt;source&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A sub-folder/directory, whose internal structure mimics that of X2Engine, containing files that were changed in the update. If there were no files changed (i.e. only database changes were in the update), this directory will not be present.&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A directory containing flat data files. These files are typically not used by the updater, but are useful if performing manual updates or recovery.&lt;br /&gt;
&lt;br /&gt;
=== Applying the Update ===&lt;br /&gt;
[[File:Steps-post-Unpack.png|200px|frame|right|Steps of the update process in the event of manually-extracted package (or resuming a preexisting update)]]&lt;br /&gt;
After unpacking, if you run the command line updater, it should (if no problems arise, i.e. incompatible package version) apply the update straight away. Otherwise, log into X2Engine, and you should see a system flash message notifying you of an update in progress. This message shows up whenever there is an update package folder (&amp;quot;update&amp;quot;) present on the server. If you then go to the updater page, once the page has finished loading, you should see that there only remain the steps &amp;quot;Review and confirm changes&amp;quot;, &amp;quot;Apply changes&amp;quot; and &amp;quot;Reload&amp;quot; remain to be completed. To complete the update, when ready, click &amp;quot;Update&amp;quot;; otherwise, to delete the &amp;quot;update&amp;quot; folder and cancel the update, click &amp;quot;Cancel&amp;quot; either on the updater page or in the system flash message.&lt;br /&gt;
&lt;br /&gt;
== Performing Updates Manually ==&lt;br /&gt;
While it is preferable to avoid manually applying changes to X2Engine, and you may never need to, it may in some circumstance or another actually be necessary to do so. For all such situations, the raw update data files generated by the X2Engine update build process are bundled with every update package to help you perform the operations described in [[#Introduction|&amp;quot;Introduction&amp;quot;]]. This section will describe how to perform each of those operations. &lt;br /&gt;
&lt;br /&gt;
=== Prerequisites ===&lt;br /&gt;
First and foremost, all shell commands listed here, and in general, any operations that create or modify files, ''must be run as the same system user under which the server runs PHP scripts.'' Otherwise, the ownership and/or permissions of files and directories will have to be changed after completing all changes.&lt;br /&gt;
&lt;br /&gt;
To obtain the update package (which will be necessary), refer to &amp;quot;[[#Obtaining_the_Update_Package|Obtaining the Update Package]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Furthermore, it is strongly advised that you first make a back-up copy of all files and of X2Engine's database. Your database backup would best be a full, exact copy, such as one that would be made by [http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html mysqldump], or the built-in export tools of [http://www.phpmyadmin.net/home_page/index.php PHPMyAdmin].&lt;br /&gt;
&lt;br /&gt;
The X2Engine global import/export utility (&amp;quot;Import All Data&amp;quot;/&amp;quot;Export All Data&amp;quot;) in Admin '''is not recommended by itself as the sole backup tool'''. This is especially important to note because of how this tool cannot re-create ancillary data such as tags or relationships, or user roles. However, using it to ''supplement'' your standard database backup method will prove useful in the event that you:&lt;br /&gt;
# don't have any custom roles, relationships, tags, etc.&lt;br /&gt;
# cannot manage to update X2Engine with success, and&lt;br /&gt;
# wish to start over with a fresh installation.&lt;br /&gt;
In such a case, you will then be able to import your old data into the new installation.&lt;br /&gt;
&lt;br /&gt;
=== Introduction: Data Files ===&lt;br /&gt;
In [[#Unpacking_the_Update|Unpacking the update]], a rough overview of an update package's contents were given. In this section, the anatomy and purpose of each of the data files will be introduced.&lt;br /&gt;
&lt;br /&gt;
Included in every update package is two copies of the following data:&lt;br /&gt;
* Files to copy&lt;br /&gt;
* Files to delete&lt;br /&gt;
* Database changes to enact&lt;br /&gt;
* Migration scripts to run, if any&lt;br /&gt;
&lt;br /&gt;
The first, immediately visible place where all of this data is stored is in the file &amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;, as a JSON-encoded array. See the next section for how to use this file.&lt;br /&gt;
&lt;br /&gt;
The second place, which can be useful to users who do not wish to automate or script any step of a manual update, is in the files contained in the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; directory. Each file is named after the version to which it corresponds, and contains the data required to update ''to'' that version ''from the version immediately preceding it.''' Thus, the versions you should see in each file name are the versions above your X2Engine installation's current version, up to and including the version being updated to.&lt;br /&gt;
&lt;br /&gt;
In an update, the directory will contain the following subdirectories:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of files to copy. The format is each line containing a file path in the list.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of obsolete files for that version which will need to be deleted. The format is the same as in &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts corresponding to each version. Each file is formatted such that it can be directly run on the database as an SQL script, although this is not generally recommended. The list of SQL commands to run is a of the following format: &lt;br /&gt;
  [query 1];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [query 2];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [etc]&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts with similar naming and formatting conventions to the &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; scripts. See [[#The_structure_of_the_.22data.22_array|The structure of the &amp;quot;data&amp;quot; array]] for explanation of the purpose of &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== How to use the manifest for updating manually ===&lt;br /&gt;
If you so choose, you may write a script to perform all of the operations for an update in one fell swoop. If so, and if the preferred scripting language of choice has a JSON-parsing library available, you will be able to save yourself a lot of effort by using the main data file (&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;) to load all of the necessary data for the update.&lt;br /&gt;
&lt;br /&gt;
==== The structure the manifest ====&lt;br /&gt;
In updates, the contents of the plain-text file manifest.json are a JSON-encoded object containing the following properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the data corresponding to each intermediate version. It is, in essence, all the data stored in the files in &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt;. If the update is for an installation of X2Engine that is only one version behind, it will contain only one element. Each element of this array is itself an array containing respective data for the intermediate version.&lt;br /&gt;
;&amp;lt;tt&amp;gt;versions&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array listing the version numbers of each intermediate version in the update. Similar to &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, this array will contain only one element if updating to the next version, and that version ''will be the next version''.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of deletions of obsolete files across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of files added or changed across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''from'', or in other words, the version of X2Engine before the package is applied. '''This must be the same as the version of installation to which the package is being applied.''' If this is not so, the updater utility would exit immediately, as applying the changes in the update would have potentially harmful results to data and/or compatibility with future updates.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''to'', or in other words, the version of X2Engine before the package is applied.&lt;br /&gt;
;&amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the version of the updater utility for which the package was compiled. If the structure of the update package or of the manifest changes, the updater must also change to accommodate and recognize the changes; else it might try to access properties that don't exist, or ignore important ones that do. This is the single property of manifest.json that should never change.&lt;br /&gt;
;&amp;lt;tt&amp;gt;scenario&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, which should be &amp;quot;update&amp;quot; if the package is for an update. In the case of upgrades, which use most of the same functionality of the updater, it should be &amp;quot;upgrade&amp;quot;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation after the package is applied. It should be &amp;quot;opensource&amp;quot; for open source edition, &amp;quot;pro&amp;quot; for professional edition. In the case of an upgrade package, it differs from the &amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation before the package is applied. &lt;br /&gt;
;&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An integer denoting the timestamp of the release date of the version of X2Engine denoted by &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The structure of the &amp;quot;data&amp;quot; array ====&lt;br /&gt;
In updates, each version's data, contained in each element of the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, contains the following nested array properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files added for the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files deleted in the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of SQL commands to bring the database structure and contents into compliance with the version from the previous version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A prerequisite list of SQL commands that should be run before those in &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, and if any fail, the failures should be ignored. These are added to updates whenever it is necessary to normalize the database schema, i.e. in the event that some installations' databases may not be consistent with others, but the commands might fail on every other system. These SQL commands were intended to be &amp;quot;attempted&amp;quot;; they are not guaranteed to succeed on all installations. An example of when this is necessary would be adding a foreign key constraint when in previous versions the storage engine was left to the database's default settings in old versions. MyISAM does not natively support foreign key constraints. Thus, to standardize this across all installations, including installations on servers where the storage engine defaulted to MyISAM, the constraint must first be dropped (in a &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; command) before the relevant tables are changed to the InnoDB storage engine, and the constraint is added back again, in the usual &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;version&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The version number&lt;br /&gt;
;&amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The edition&lt;br /&gt;
;&amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of PHP scripts that would be run in an update for this version. These are a subset of fileList and are typically used for advanced database operations, i.e. restructuring values of JSON-encoded fields, which are extremely impractical (or impossible) to perform using raw SQL commands. The files contained in the list all have paths that look like &amp;quot;&amp;lt;tt&amp;gt;protected/migrations/{version}/{timestamp}-name-of-operations.php&amp;lt;/tt&amp;gt;&amp;quot;. Note, if this array is not empty, you may need to reverse-engineer or otherwise copy and re-purpose/re-write any migration scripts listed in order to perform their necessary operations. This is because they may depend on running within a Yii Framework environment, i.e. will contain references to components of the Yii application singleton such as &amp;quot;db&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Note that in upgrades, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; are replaced by &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; (respectively), and &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; are missing (because they aren't necessary).&lt;br /&gt;
&lt;br /&gt;
==== What you will need for the update, if using the manifest ====&lt;br /&gt;
''Among this data, what you will need:'' &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, which will give you the full list of files that need to be deleted, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, which will give you the full list of files to copy from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory, &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;, which give you values with which to update the main X2Engine configuration file, and, within each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, the sub-array properties &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;, with which you will construct the list of database commands to run. &lt;br /&gt;
&lt;br /&gt;
A procedure for manually updating X2Engine, with data from manifest.json, thus might look like this:&lt;br /&gt;
# For each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; (or in other words, each version's changes):&lt;br /&gt;
## Run all the SQL commands in its corresponding &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; sub-array, ignoring errors&lt;br /&gt;
## Run all SQL commands in its &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; array&lt;br /&gt;
## Perform database operations that would be equivalent to those performed in migration scripts&lt;br /&gt;
# For each file in the cumulative list &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, copy it from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory into its analogous location in the installation directory of X2Engine, creating parent directories as necessary if they don't already exist.&lt;br /&gt;
# Delete all files in the cumulative list &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; from the installation directory&lt;br /&gt;
# Clear the data cache files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt; and the temporary asset folders in &amp;lt;tt&amp;gt;assets/&amp;lt;/tt&amp;gt;&lt;br /&gt;
# Clear all session data (&amp;lt;tt&amp;gt;x2_sessions&amp;lt;/tt&amp;gt;) and auth cache data (&amp;lt;tt&amp;gt;x2_auth_cache&amp;lt;/tt&amp;gt;).&lt;br /&gt;
# Update the main X2Engine configuration (&amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt;) file, setting:&lt;br /&gt;
## &amp;lt;tt&amp;gt;$buildDate&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt; property,&lt;br /&gt;
## &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt; property, and &lt;br /&gt;
## &amp;lt;tt&amp;gt;$updaterVersion&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
&lt;br /&gt;
=== Enacting Database Changes ===&lt;br /&gt;
Inside the &amp;lt;tt&amp;gt;update/sqlList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/sqlForce&amp;lt;/tt&amp;gt; folder is a list of &amp;lt;tt&amp;gt;.sql&amp;lt;/tt&amp;gt; files, named according to their corresponding version's update, that can essentially be run as SQL scripts to perform the update. Note, however, that '''if the update traverses more than one version, the scripts must be run in the order of their corresponding version.''' This is because, in the database changes for any version, it is assumed that the database schema and contents conform to the version that immediately preceded it. The PHP function [[phpfunc:version-compare|version_compare]], and the method &amp;quot;[http://pythonhosted.org/setuptools/pkg_resources.html#parsing-utilities parse_version]&amp;quot;, in the pkg_resources module of setuptools, should correctly identify which version should come first. Note, the [[#The_structure_the_manifest|&amp;quot;versions&amp;quot; property of the manifest]] will contain the list of versions in the correct order.&lt;br /&gt;
&lt;br /&gt;
Furthermore, note, &amp;quot;sqlList&amp;quot; and &amp;quot;sqlForce&amp;quot; may not encompass all necessary database changes; if there are any migration scripts in the update, the operations they perform will similarly need to be performed in the current update (see the description of &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; in [[#The_structure_of_the_.22data.22_array|&amp;quot;the structure of the 'data' array&amp;quot;]]).&lt;br /&gt;
&lt;br /&gt;
In summary, for each version traversed in the update, the following operations should be run in order, with &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version number of the version in question:&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlForce/{version}.sql&amp;lt;/tt&amp;gt; file,''' in the order they appear in the file. Ignore if any commands fail. Note, if passed to the MySQL client as a script, any failures might trigger a halt in execution and thus skip the remainder of the commands.&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlList/{version}.sql&amp;lt;/tt&amp;gt; file.'''&lt;br /&gt;
# '''Perform operations equivalent to those in each of the migration scripts, if any.''' The first part of a script file name is the timestamp it was added to the codebase, and this also dictates the order in which they should be run.&lt;br /&gt;
&lt;br /&gt;
=== Copying new files ===&lt;br /&gt;
Files added/changed in the new version are stored in the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory. In an update, they should be copied into the installation directory, replacing any existing files of the same name/path. If the &amp;lt;tt&amp;gt;rsync&amp;lt;/tt&amp;gt; utility is available on the system, you can use one command to safely copy these files into the installation directory as follows (executed in that same directory):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rsync -avc update/source/ ./&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, you will need to use &amp;lt;tt&amp;gt;cat&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;cp&amp;lt;/tt&amp;gt;, two utilities that should be available on every UNIX-like system, and the lists of files added in each intermediate version. These lists are stored in the folder &amp;lt;tt&amp;gt;update/data/fileList&amp;lt;/tt&amp;gt; and named like &amp;quot;{version}.txt&amp;quot; (&amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version to which it applies), each file on its own line in that file. Run the following command, ignoring errors:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/fileList/*; do for l in $(cat $f); do cp update/source/&amp;quot;$l&amp;quot; ./&amp;quot;$l&amp;quot; ; done ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting old files ===&lt;br /&gt;
Files to be deleted are listed in a similar manner as the files to be added: one file per line. The list of files to delete are located at files with paths that look like this: &amp;lt;tt&amp;gt;update/data/deletionList/{version}.txt&amp;lt;/tt&amp;gt;&lt;br /&gt;
So, for an update from 5.5 to 5.7 with intermediate version 5.6 (for instance), the files would be &amp;lt;tt&amp;gt;update/data/deletionList/5.6&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/data/deletionList/5.7&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is only one version through which to update, the operation is very simple, and can be performed in one UNIX/Linux (bash) shell command as follows (if the version is 3.6.3, for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
xargs -a update/data/deletionList/3.6.3.txt -d&amp;quot;\n&amp;quot; rm -fv&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, if there are multiple versions, this may be a more tricky proposition. The necessity arises to create a cumulative list, because there might be the possibility that a file is deleted in one version and re-established in a new version. Nevertheless, while this is possible, it is highly unlikely, and in most cases it is safe to simply iterate over all the &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; data files and delete all the files listed therein. As a bash command:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/deletionList/*; do xargs -a $f -d&amp;quot;\n&amp;quot; rm -fv ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Editing the Configuration File ===&lt;br /&gt;
This final step in the update process, before refreshing, should not be done manually unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a fresh installation at the same version as the one to which you have updated.&lt;br /&gt;
&lt;br /&gt;
To update the configuration manually:&lt;br /&gt;
# Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor&lt;br /&gt;
# Find the line with the &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; variable in it. It should look like this (but with the version from which you are updating): &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$version = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change the value in quotes to the current version being updated to, and save the file.&lt;br /&gt;
&lt;br /&gt;
=== Clearing caches and refreshing the application ===&lt;br /&gt;
It is generally always a good idea to clear the application's server-side cache and other ephemeral files/data after updating. This is taken care of automatically by the built-in updater, but barring proper use and functioning of that, the operations can be performed manually as follows: &lt;br /&gt;
;clear the data cache&lt;br /&gt;
: delete all &amp;quot;&amp;lt;tt&amp;gt;.bin&amp;lt;/tt&amp;gt;&amp;quot; files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -fv protected/runtime/cache/*.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear web assets&lt;br /&gt;
: delete all sub-folders of &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt; by running this command (in the installation directory): &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -r assets/*/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear the &amp;quot;auth cache&amp;quot;&lt;br /&gt;
: Flush the auth cache table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;&lt;br /&gt;
DELETE FROM `x2_auth_cache` WHERE 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;log all users out&lt;br /&gt;
: Flush the sessions table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;DELETE FROM `x2_sessions` WHERE 1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Upgrading =&lt;br /&gt;
&lt;br /&gt;
== Using the Web Interface ==&lt;br /&gt;
[[File:Software-Upgrade-Registration.png|400px|thumb|right|Software upgrade page, in the first step (registration)]]&lt;br /&gt;
To upgrade to Professional Edition&lt;br /&gt;
# Purchase X2Engine Professional Edition Download. Note the registration info in the activation email.&lt;br /&gt;
# Go to the Admin page in X2Engine and find &amp;quot;Upgrade X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot;.&lt;br /&gt;
# In the ''Upgrade'' page, enter your registration info into the form and click &amp;quot;Register&amp;quot; to continue. Once complete, you will see a list of changes similar to those displayed when performing an update.&lt;br /&gt;
# Review the changes to be applied and then proceed by clicking &amp;quot;Upgrade&amp;quot;, similar to performing an update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Offline&amp;quot; Upgrading ==&lt;br /&gt;
This works in exactly the same way as offline updating. However, to obtain your upgrade package, which you will extract into a folder called &amp;quot;update&amp;quot; on the X2Engine web server, contact [mailto:customersupport@x2engine.com customer support]. Furthermore, you will need to run the following database command on your X2Engine database when done with the upgrade, replacing &amp;lt;tt&amp;gt;{product key}&amp;lt;/tt&amp;gt; with your product key, to allow your installation to receive Professional Edition software updates:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{product key}' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Manually Upgrading ==&lt;br /&gt;
The process of upgrading is very similar to that of updating, and uses all of the same underpinnings as the updater. Thus, almost all of the above information and procedures will apply to upgrades. However, note the following crucial differences:&lt;br /&gt;
&lt;br /&gt;
* '''X2Engine must be at the latest available version in order to upgrade.'''&lt;br /&gt;
* '''To obtain the upgrade package, you must be an owner of an X2Engine Professional Edition Download-type license.''' Contact [mailto:customersupport@x2engine.com customer support] for further details.&lt;br /&gt;
* '''There will be only one &amp;quot;intermediate&amp;quot; version.''' The changes to apply will move X2Engine from a version to that same version, applying instead the necessary changes to switch its ''edition'' from Open Source to Professional.&lt;br /&gt;
* '''There are no file deletions.'''&lt;br /&gt;
* '''The properties of the package manifest differ slightly.''' In upgrades, the properties of the data object are &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, etc. Note also, the update data is in aptly-named sub-folders of &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt; as well, and there are no &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; lists to handle.&lt;br /&gt;
* '''The configuration is not changed.''' Instead, the value stored in the field &amp;lt;tt&amp;gt;unique_id&amp;lt;/tt&amp;gt; of the admin record in the database table &amp;lt;tt&amp;gt;x2_admin&amp;lt;/tt&amp;gt; must be changed to the license key, and the &amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt; field must be changed to &amp;quot;pro&amp;quot;. To perform these both, run the following command on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''It is not necessary to clear caches or user sessions when done.''' These operations are performed in updates to eliminate ephemeral (session-ingrained) data that may no longer be applicable and thus cause problems. Upgrades will rarely, if ever, invalidate this data.&lt;br /&gt;
&lt;br /&gt;
= Updating to Beta Versions = &lt;br /&gt;
Beginning at version 4.1.2, after much advocacy to adopt this practice, X2Engine began a two-phase release process in which each version would be released first as a beta, then to the broader public after initial bug finds. The purpose behind this is to avoid updating production installations of X2Engine to new versions that have had zero public exposure, and thus avoid introducing bugs that were not found in initial QA testing (due to a incompatibility with a particular server environment or configuration of X2Engine) into sensitive production environments.&lt;br /&gt;
&lt;br /&gt;
By default, the updater will not update to beta versions. The updater must be configured to use the proper URL on the update server for beta release data, by the following procedure:&lt;br /&gt;
# Create a file &amp;quot;constants-custom.php&amp;quot; in the root level of X2Engine. You can easily do this by renaming &amp;quot;constants-custom.example.php&amp;quot; to that name (remove &amp;quot;.custom&amp;quot;).&lt;br /&gt;
# Look for the following line, and in it, change &amp;quot;false&amp;quot; to &amp;quot;true&amp;quot;: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('X2_UPDATE_BETA') or define('X2_UPDATE_BETA',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The advantage of enabling beta releases is that new features will be available much sooner. Additionally, fixes to bugs will also be available sooner. The main disadvantage of enabling beta releases is that they are never guaranteed to be as stable as non-beta releases. We encourage users to clone their installations of X2Engine, if able, and use beta versions on them, and report any issues they encounter for their particular server environment/special configuration/custom implementation of X2Engine. This way, any issues that arise can be fixed in the stable release. The more users participate in beta testing, the more stable the non-beta releases will become.&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
== User interface (UI) &amp;quot;bugs&amp;quot; after the update ==&lt;br /&gt;
It is extremely common (and easily remedied) that after an important update, certain features of the user interface of X2Engine may not work properly. This is caused, in nearly all cases, by the web browser's cache, which contains out-of-date copies of X2Engine's essential user interface files. To solve these problems, the first step to try is clearing the cache. A step-by-step guide to doing so is given in [[wikihow:Clear-Your-Browser%27s-Cache]].&lt;br /&gt;
&lt;br /&gt;
== Errors in the updater itself == &lt;br /&gt;
When all else fails, the updater may have to be patched due to an irreconcilable backwards compatibility issue. Fortunately, if one has read/write access to the source files on the webserver, this is straightforward to fix. See [[#Refreshing_and_Updating_the_Updater_Utility|Refreshing and Updating the Updater Utility]].&lt;br /&gt;
&lt;br /&gt;
== Professional Edition not in effect after upgrading ==&lt;br /&gt;
This may be caused by Professional Edition not getting &amp;quot;switched on&amp;quot; properly, despite all the necessary filesystem and database changes already being in effect. First, to see if this scenario applies to you, look for a folder &amp;quot;&amp;lt;tt&amp;gt;reports&amp;lt;/tt&amp;gt;&amp;quot; inside &amp;lt;tt&amp;gt;protected/modules&amp;lt;/tt&amp;gt;, and a table &amp;lt;tt&amp;gt;x2_reports&amp;lt;/tt&amp;gt; in the X2Engine database. If you find these things, you can try the following to enable Professional Edition:&lt;br /&gt;
# Find the file &amp;lt;tt&amp;gt;constants.php&amp;lt;/tt&amp;gt; in the installation directory. Open it in a text editor, and look for the following line: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('PRO_VERSION') or define('PRO_VERSION',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; on that line, and save the file.&lt;br /&gt;
# Run the following SQL command (replacing &amp;lt;tt&amp;gt;{license key}&amp;lt;/tt&amp;gt; with your X2Engine license key) on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General Procedures to Try ==&lt;br /&gt;
It may sometimes be possible that the update did not finish cleanly. In such cases, it will be necessary to manually re-perform some of the final operations in the update, described in [[#Introduction|Introduction]]. &lt;br /&gt;
&lt;br /&gt;
=== Refresh the Source Code ===&lt;br /&gt;
This step is not really the best step to try first, as it is usually more time-consuming. It entails re-uploading the entire application, in order to reset all non-ephemeral (source code) files to what they should be in a fresh installation of X2Engine. To do this:&lt;br /&gt;
# Download the refresh package. There should be a download link for this in the ''Updater Settings'' page. Otherwise, if that doesn't work and you're using Open Source edition, use [https://x2planet.com/installs/refresh.zip this link], or if using Professional Edition, paste the following url (after replacing {key} with your license key) into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/refresh-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Upload the ''contents'' of the downloaded zip archive to the installation directory, overwriting existing files.&lt;br /&gt;
&lt;br /&gt;
=== Clear Caches ===&lt;br /&gt;
See &amp;quot;[[#Clearing_caches_and_refreshing_the_application|Clearing Caches...]]&amp;quot; for details.&lt;br /&gt;
&lt;br /&gt;
=== Regenerate the Configuration ===&lt;br /&gt;
Again, this should not be done unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a new installation at the same version as the one you updated to. Refer to [[#Editing_the_Configuration_File|Editing the Configuration File]].&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1850</id>
		<title>Software Updates and Upgrades</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1850"/>
				<updated>2014-06-26T23:35:57Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Recovering From Crashes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
[[File:Update_Process.svg|200px|thumb|right|Top-level overview of the update process]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
The following things happen in typical a software update (or upgrade):&lt;br /&gt;
* Files that are new in the new version are added&lt;br /&gt;
* Files that have been changed in the new version are replaced with the new versions of those files &lt;br /&gt;
* Files that are no longer used in the new version are deleted&lt;br /&gt;
* Changes to the database are made to make the database compatible with the new version of X2Engine&lt;br /&gt;
* The main configuration file, &amp;lt;tt&amp;gt;X2Config.php&amp;lt;/tt&amp;gt;, is edited to reflect the new state of the application.&lt;br /&gt;
* X2Engine data caches are cleared&lt;br /&gt;
&lt;br /&gt;
These operations are typically handled by the updater utility component [[x2doc:UpdaterBehavior|UpdaterBehavior]] and controlled via the in-app web-based updater or the command line updater. This article will cover both how to use said interfaces to the updater, and how to perform updates manually or with the &amp;quot;offline&amp;quot; method if the need arises, in addition to troubleshooting and recovering from an update that did not complete cleanly.&lt;br /&gt;
&lt;br /&gt;
= Disclaimer =&lt;br /&gt;
This article covers the standard and recommended update/upgrade methods, in addition to alternate methods and supplemental procedures. As general rules and precautions for using this article:&lt;br /&gt;
# ''If you do not fully and completely understand any step of any instructions given in this article, '''do not''' follow them''; ask for help instead on [http://x2community.com the forums] or through contacting X2Engine Technical Support if you are an existing customer.&lt;br /&gt;
# You should always make a backup of all of your files ''and'' a full backup copy of your database, preferably in a format (i.e. SQL script) that can be used to create a verbatim/carbon copy of the original database, with all of the features of the original schema, '''before''' attempting any steps of the update process manually.&lt;br /&gt;
# Some of the procedures outlined in this article involve manually creating or modifying files. Note that the same filesystem prerequisite for installation, namely that the file ownership must match the user under which PHP executes on the web server, applies to these steps.&lt;br /&gt;
&lt;br /&gt;
Furthermore, '''in regard to uploading files:''' note that any instructions to upload folders into X2Engine, and replace/merge contents, refer to replacing files in X2Engine with files in the archive at analogous paths. For example, if you have a zip file that contains a folder &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, and inside of that is a folder &amp;lt;tt&amp;gt;components&amp;lt;/tt&amp;gt;, and inside of that is a file &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt;, and the instructions are to upload/replace in X2Engine, you should upload &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt; to replace the existing file of the same name in &amp;lt;tt&amp;gt;protected/components&amp;lt;/tt&amp;gt; inside of X2Engine.&lt;br /&gt;
&lt;br /&gt;
X2Engine Inc. cannot be held accountable for damage/errors caused by any missteps or mistakes in manually-performed update operations. If in doubt, make a sandbox/clone of your X2Engine installation by copying the files and database to a different server, and test performing the operations on that before proceeding with your production install of X2Engine.&lt;br /&gt;
&lt;br /&gt;
= Update Notifications =&lt;br /&gt;
[[File:Update Notification.png|200px|frame|right|System flash message displaying software update notification.]]&lt;br /&gt;
To get notified of software updates, unless already using X2Engine Professional Edition, one must first &amp;quot;subscribe&amp;quot; to them. This can be done via checking the box &amp;quot;Notify me of software updates&amp;quot; in the installation form. If X2Engine is already installed, this can be done by going to &amp;quot;Updater Settings&amp;quot; from the Admin page and enabling them. If using Professional Edition, these steps are not not necessary.&lt;br /&gt;
&lt;br /&gt;
Once that has been completed, set the update notification interval as desired in &amp;quot;Updater Settings&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Update Methods =&lt;br /&gt;
There are, in essence, just three basic methods of updating, and variations on each. The three main methods are: &lt;br /&gt;
# web updater,&lt;br /&gt;
# command line updater, and&lt;br /&gt;
# manually.&lt;br /&gt;
In each case, an update package from the update data server is used as the authoritative list of changes to apply, but in the non-manual methods, X2Engine's own updater class does all the work applying changes from said package.&lt;br /&gt;
&lt;br /&gt;
In web and command line updates, there is also the option to first download and unpack the package manually (see [[#Performing_.22Offline.22_Updates|performing &amp;quot;offline&amp;quot; updates]]). This circumvents the need for the local server (on which X2Engine is hosted) to be connected to the internet. Similarly, in the case of manual updates, it is not necessary for the web server to actually have an internet connection.&lt;br /&gt;
&lt;br /&gt;
== Using the Web Updater ==&lt;br /&gt;
[[File:Updater-1.png|300px|thumb|right|The updater utility page.]]&lt;br /&gt;
The web updater can be accessed by any of the following methods:&lt;br /&gt;
* Using the link in the notification message at the top of the window in versions later than 3.5.5&lt;br /&gt;
* By going to the Admin page and clicking the &amp;quot;Update&amp;quot; button at the top, in earlier versions, with update notifications enabled&lt;br /&gt;
* Through &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
First, navigate to the web updater page, and '''do not''' cancel the request (stop or hit your browser's &amp;quot;back&amp;quot; button) until the page has loaded. If the updater utility has undergone changes and needs to self-update to be compatible with the software updates server, this is when it will do so. Thus, it is critical that you do not interrupt this process. If the page does not load, or displays the error message that dependencies could not be retrieved, you may have to [[#Performing_.22Offline.22_Updates|perform an &amp;quot;offline&amp;quot; update]].&lt;br /&gt;
&lt;br /&gt;
There are three ways of getting to the updater page. One is to simply click the link given in the update notification flash message, if available. The other is to click on the &amp;quot;Update&amp;quot; button at the top of the admin page. If software update notifications are not enabled, one can manually check for and install an update by going to &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
Next, make a backup of the database and files (recommended), follow the instructions on the page, and click the &amp;quot;Update&amp;quot; button when ready.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
It is generally quick and is the easiest method to use, and it does not require shell/SSH access to the web server.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
It is not recommended for use on large installations of X2Engine, i.e. containing over a million records, or for updating from very old versions (i.e. a version more than five months old). This is because the final update process may be cut short by server timeout or an interruption in the network connection. This can result in an incomplete/broken update that requires recovering manually (a potentially long process) at best, or actual loss of data at worst. &lt;br /&gt;
&lt;br /&gt;
==== Explanation ====&lt;br /&gt;
The inherent weakness of the web updater (or any web-based updater to any web application) is that it must always rely upon web requests to the server, and it thus must perform extremely critical operations for the update all within the scope of a web request. Web requests, it goes without saying, are not always reliable. For example, one's internet connection might get cut off due being on a wireless network and encountering interference. Furthermore, if any operations take an exceedingly long amount of time, e.g. because of the size of the data set (and thus indexes) on which the update must operate, or the number of database changes that need to be applied, the web request for those operations may take longer than the PHP execution time limit as configured on the webserver. This would potentially cut short the execution of a critical update stage, and thus the update would require manual recovery at best, or completely restoring  X2Engine to a backup at worst.&lt;br /&gt;
&lt;br /&gt;
==== How to Avoid Timeouts ====&lt;br /&gt;
You can usually get around this by setting the value of &amp;lt;tt&amp;gt;'''max_execution_time'''&amp;lt;/tt&amp;gt; to a higher value in the web server's PHP configuration. It is highly recommended that you either disable the timeout or set it to a very generous value (such as 300 or more seconds) while attempting to update via the web updater, especially if you are many versions behind or have a high volume of data in your CRM.&lt;br /&gt;
Also, it is generally always a good idea to create a backup copy ''before'' updating, and to download it.&lt;br /&gt;
&lt;br /&gt;
==== Recovering From Crashes ====&lt;br /&gt;
If you downloaded the backup copy of your database, then after you encounter a failure/timeout, you can use the .sql backup script to restore the database to its original form. However, be aware, that if there are any '''&amp;lt;tt&amp;gt;CREATE TABLE&amp;lt;/tt&amp;gt;''' commands in the update data (expand the ''Database Commands to Run'' list to see), you will need to manually drop the created tables in order to once again make your installation compatible with the update.&lt;br /&gt;
&lt;br /&gt;
== Using the Command Line Updater ==&lt;br /&gt;
This update method uses all of the same thoroughly-tested underpinnings and processes as the web updater, but does so all within a system command to the Yii console.&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
While in via SSH, or in a terminal emulator, or in a script, run the following command in the &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt; subdirectory of X2Engine:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./yiic update app&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the following optional arguments to the command line updater:&lt;br /&gt;
;&amp;lt;tt&amp;gt;--force&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; for this option (i.e. &amp;lt;tt&amp;gt;--force=1&amp;lt;/tt&amp;gt;) to proceed with the update even if compatibility issues were detected. Default: 0 (halt if compatibility issues were detected).&lt;br /&gt;
;&amp;lt;tt&amp;gt;--backup&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that a database backup should be performed first before updating, &amp;quot;0&amp;quot; to proceed without creating a backup first. Default: 1.&lt;br /&gt;
;&amp;lt;tt&amp;gt;--lock&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that the application should be locked during the final installation of the update package to prevent data entry during the sensitive moments when the database is being altered and the files updated, &amp;quot;0&amp;quot; to permit use even when changes to the app are being applied. Default: 0.&lt;br /&gt;
&lt;br /&gt;
If running the command gives you the following message:&lt;br /&gt;
&amp;lt;pre&amp;gt;The updater is now up-to-date and compliant with the updates server. Re-run the command to proceed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
That indicates that the updater first needed to self-update, and succeeded in doing so. You should then run the same command again to complete the update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Safer to use on large, mission-critical systems. The success of a software update (or upgrade) does not hinge upon a single web request completing successfully.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
More difficult to use; requires shell access to the web server. The console command must be run as the same system user that owns all the files and directories of X2Engine, in order to circumvent permission issues when manipulating file system objects.&lt;br /&gt;
&lt;br /&gt;
== Using the Automatic Updater ==&lt;br /&gt;
[[File:Auto-Update.png|400px|thumb|right|Auto-update scheduler on the Updater Settings page]]&lt;br /&gt;
This method uses the command line updater, but runs the command on your behalf using the local cron daemon. To configure, go to the Updater Settings Page, check the box titled &amp;quot;Update Automatically&amp;quot;, and save. If you have Professional Edition, you can manage the update schedule in addition to other automated tasks from the Cron Table settings page, which can also be found under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Automates the entire process of updating.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
Only available on UNIX-like systems that have a cron daemon, and which permit user access to the cron table.&lt;br /&gt;
&lt;br /&gt;
== Performing &amp;quot;Offline&amp;quot; Updates ==&lt;br /&gt;
In some server environments, the updater may not be able to access the updates server by itself, due to (for example) the PHP or network configuration, or because the &amp;quot;zip&amp;quot; PHP extension (requried) is unavailable or has been disabled. It is in these situations necessary to download the update package manually, unpack on the server (or unpack and upload the files to the server), and then run the updater. When performing updates in this way, the updater will not need to access the updates server, but will use the manually-downloaded and extracted package files present in the local filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Refreshing and Updating the Updater Utility ===&lt;br /&gt;
Before starting, note that the updater itself may have undergone changes; the X2Engine update process is subject to change. To address this technical challenge, the updater utility within X2Engine would normally first attempt to download the latest version of itself, in order to be compatible with the updates server. However, this sometimes will not work properly. If your X2Engine installation is running in a hosting environment that prevents it from making outbound HTTP requests (the [[requirements:|requirements check script]] should tell you if this is the case), you will need to do this manually. Also, in version 3.5.5, there was a bug in the updater utility's self-update methods.&lt;br /&gt;
&lt;br /&gt;
To update the updater utility:&lt;br /&gt;
# '''Determine the version of the updater utility package''' by visiting [https://x2planet.com/installs/updates/updateCheck this link]. You should see a page that's entirely blank except for a version number.&lt;br /&gt;
# '''Download the updater package.''' In versions 3.6 and later, a download link for this should be available through the ''Updater Settings'' page. Otherwise, use the following URL (if in Open Source Edition): &amp;lt;pre&amp;gt;https://x2planet.com/installs/updater.zip&amp;lt;/pre&amp;gt; Otherwise, if using Professional Edition, paste the following url, after replacing {key} with your license key, into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/updater-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# '''Extract the downloaded zip archive and upload''' the &amp;quot;protected&amp;quot; folder to your server. Merge folders and overwrite existing files when prompted.&lt;br /&gt;
# '''Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor''' and find the line that looks like this:&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$updaterVersion = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# '''Change the text inside the pair of single quotes''' on that line so that it is the current updater version obtained in step #1. &lt;br /&gt;
# '''Save the file.'''&lt;br /&gt;
&lt;br /&gt;
=== Obtaining the Update Package ===&lt;br /&gt;
First, use the link in the ''Updater Settings'' page entitled &amp;quot;Latest Update Package for Version...&amp;quot;, to download the update package. If the link in the Updater Settings page does not work, use the following URL to download the update package (if using Open Source Edition), replacing &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; with your X2Engine installation's version:&lt;br /&gt;
&amp;lt;pre&amp;gt;https://x2planet.com/installs/updates/{version}/none&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should receive, from that URL, a zip archive file called &amp;quot;update.zip&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If using Professional Edition, and your download link is not working, contact [mailto:customersupport@x2engine.com customer support].&lt;br /&gt;
&lt;br /&gt;
=== Unpacking the Update ===&lt;br /&gt;
[[File:Update-in-Progress.png|200px|frame|right|System flash message displaying notification of pending update.]]&lt;br /&gt;
The update package will be a zip archive. The ''contents'' of this archive must be extracted ''into'' a folder called &amp;quot;update&amp;quot; in the installation directory. So, in other words, after extracting and uploading to the server, there should be a folder called &amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt; alongside &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;framework&amp;lt;/tt&amp;gt; (etc). Within that folder (&amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt;) should be the following contents:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A file containing important data to be used by the updater&lt;br /&gt;
;&amp;lt;tt&amp;gt;contents.md5&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A message digest file containing MD5 checksums of all files in the archive excluding itself.&lt;br /&gt;
;&amp;lt;tt&amp;gt;source&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A sub-folder/directory, whose internal structure mimics that of X2Engine, containing files that were changed in the update. If there were no files changed (i.e. only database changes were in the update), this directory will not be present.&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A directory containing flat data files. These files are typically not used by the updater, but are useful if performing manual updates or recovery.&lt;br /&gt;
&lt;br /&gt;
=== Applying the Update ===&lt;br /&gt;
[[File:Steps-post-Unpack.png|200px|frame|right|Steps of the update process in the event of manually-extracted package (or resuming a preexisting update)]]&lt;br /&gt;
After unpacking, if you run the command line updater, it should (if no problems arise, i.e. incompatible package version) apply the update straight away. Otherwise, log into X2Engine, and you should see a system flash message notifying you of an update in progress. This message shows up whenever there is an update package folder (&amp;quot;update&amp;quot;) present on the server. If you then go to the updater page, once the page has finished loading, you should see that there only remain the steps &amp;quot;Review and confirm changes&amp;quot;, &amp;quot;Apply changes&amp;quot; and &amp;quot;Reload&amp;quot; remain to be completed. To complete the update, when ready, click &amp;quot;Update&amp;quot;; otherwise, to delete the &amp;quot;update&amp;quot; folder and cancel the update, click &amp;quot;Cancel&amp;quot; either on the updater page or in the system flash message.&lt;br /&gt;
&lt;br /&gt;
== Performing Updates Manually ==&lt;br /&gt;
While it is preferable to avoid manually applying changes to X2Engine, and you may never need to, it may in some circumstance or another actually be necessary to do so. For all such situations, the raw update data files generated by the X2Engine update build process are bundled with every update package to help you perform the operations described in [[#Introduction|&amp;quot;Introduction&amp;quot;]]. This section will describe how to perform each of those operations. &lt;br /&gt;
&lt;br /&gt;
=== Prerequisites ===&lt;br /&gt;
First and foremost, all shell commands listed here, and in general, any operations that create or modify files, ''must be run as the same system user under which the server runs PHP scripts.'' Otherwise, the ownership and/or permissions of files and directories will have to be changed after completing all changes.&lt;br /&gt;
&lt;br /&gt;
To obtain the update package (which will be necessary), refer to &amp;quot;[[#Obtaining_the_Update_Package|Obtaining the Update Package]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Furthermore, it is strongly advised that you first make a back-up copy of all files and of X2Engine's database. Your database backup would best be a full, exact copy, such as one that would be made by [http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html mysqldump], or the built-in export tools of [http://www.phpmyadmin.net/home_page/index.php PHPMyAdmin].&lt;br /&gt;
&lt;br /&gt;
The X2Engine global import/export utility (&amp;quot;Import All Data&amp;quot;/&amp;quot;Export All Data&amp;quot;) in Admin '''is not recommended by itself as the sole backup tool'''. This is especially important to note because of how this tool cannot re-create custom fields; they must exist in the target installation before the import. However, using it to ''supplement'' your standard database backup method will prove useful in the event that you:&lt;br /&gt;
# don't have any custom fields,&lt;br /&gt;
# cannot manage to update X2Engine with success, and&lt;br /&gt;
# wish to start over with a fresh installation.&lt;br /&gt;
In such a case, you will then be able to import your old data into the new installation.&lt;br /&gt;
&lt;br /&gt;
=== Introduction: Data Files ===&lt;br /&gt;
In [[#Unpacking_the_Update|Unpacking the update]], a rough overview of an update package's contents were given. In this section, the anatomy and purpose of each of the data files will be introduced.&lt;br /&gt;
&lt;br /&gt;
Included in every update package is two copies of the following data:&lt;br /&gt;
* Files to copy&lt;br /&gt;
* Files to delete&lt;br /&gt;
* Database changes to enact&lt;br /&gt;
* Migration scripts to run, if any&lt;br /&gt;
&lt;br /&gt;
The first, immediately visible place where all of this data is stored is in the file &amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;, as a JSON-encoded array. See the next section for how to use this file.&lt;br /&gt;
&lt;br /&gt;
The second place, which can be useful to users who do not wish to automate or script any step of a manual update, is in the files contained in the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; directory. Each file is named after the version to which it corresponds, and contains the data required to update ''to'' that version ''from the version immediately preceding it.''' Thus, the versions you should see in each file name are the versions above your X2Engine installation's current version, up to and including the version being updated to.&lt;br /&gt;
&lt;br /&gt;
In an update, the directory will contain the following subdirectories:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of files to copy. The format is each line containing a file path in the list.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of obsolete files for that version which will need to be deleted. The format is the same as in &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts corresponding to each version. Each file is formatted such that it can be directly run on the database as an SQL script, although this is not generally recommended. The list of SQL commands to run is a of the following format: &lt;br /&gt;
  [query 1];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [query 2];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [etc]&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts with similar naming and formatting conventions to the &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; scripts. See [[#The_structure_of_the_.22data.22_array|The structure of the &amp;quot;data&amp;quot; array]] for explanation of the purpose of &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== How to use the manifest for updating manually ===&lt;br /&gt;
If you so choose, you may write a script to perform all of the operations for an update in one fell swoop. If so, and if the preferred scripting language of choice has a JSON-parsing library available, you will be able to save yourself a lot of effort by using the main data file (&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;) to load all of the necessary data for the update.&lt;br /&gt;
&lt;br /&gt;
==== The structure the manifest ====&lt;br /&gt;
In updates, the contents of the plain-text file manifest.json are a JSON-encoded object containing the following properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the data corresponding to each intermediate version. It is, in essence, all the data stored in the files in &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt;. If the update is for an installation of X2Engine that is only one version behind, it will contain only one element. Each element of this array is itself an array containing respective data for the intermediate version.&lt;br /&gt;
;&amp;lt;tt&amp;gt;versions&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array listing the version numbers of each intermediate version in the update. Similar to &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, this array will contain only one element if updating to the next version, and that version ''will be the next version''.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of deletions of obsolete files across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of files added or changed across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''from'', or in other words, the version of X2Engine before the package is applied. '''This must be the same as the version of installation to which the package is being applied.''' If this is not so, the updater utility would exit immediately, as applying the changes in the update would have potentially harmful results to data and/or compatibility with future updates.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''to'', or in other words, the version of X2Engine before the package is applied.&lt;br /&gt;
;&amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the version of the updater utility for which the package was compiled. If the structure of the update package or of the manifest changes, the updater must also change to accommodate and recognize the changes; else it might try to access properties that don't exist, or ignore important ones that do. This is the single property of manifest.json that should never change.&lt;br /&gt;
;&amp;lt;tt&amp;gt;scenario&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, which should be &amp;quot;update&amp;quot; if the package is for an update. In the case of upgrades, which use most of the same functionality of the updater, it should be &amp;quot;upgrade&amp;quot;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation after the package is applied. It should be &amp;quot;opensource&amp;quot; for open source edition, &amp;quot;pro&amp;quot; for professional edition. In the case of an upgrade package, it differs from the &amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation before the package is applied. &lt;br /&gt;
;&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An integer denoting the timestamp of the release date of the version of X2Engine denoted by &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The structure of the &amp;quot;data&amp;quot; array ====&lt;br /&gt;
In updates, each version's data, contained in each element of the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, contains the following nested array properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files added for the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files deleted in the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of SQL commands to bring the database structure and contents into compliance with the version from the previous version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A prerequisite list of SQL commands that should be run before those in &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, and if any fail, the failures should be ignored. These are added to updates whenever it is necessary to normalize the database schema, i.e. in the event that some installations' databases may not be consistent with others, but the commands might fail on every other system. These SQL commands were intended to be &amp;quot;attempted&amp;quot;; they are not guaranteed to succeed on all installations. An example of when this is necessary would be adding a foreign key constraint when in previous versions the storage engine was left to the database's default settings in old versions. MyISAM does not natively support foreign key constraints. Thus, to standardize this across all installations, including installations on servers where the storage engine defaulted to MyISAM, the constraint must first be dropped (in a &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; command) before the relevant tables are changed to the InnoDB storage engine, and the constraint is added back again, in the usual &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;version&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The version number&lt;br /&gt;
;&amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The edition&lt;br /&gt;
;&amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of PHP scripts that would be run in an update for this version. These are a subset of fileList and are typically used for advanced database operations, i.e. restructuring values of JSON-encoded fields, which are extremely impractical (or impossible) to perform using raw SQL commands. The files contained in the list all have paths that look like &amp;quot;&amp;lt;tt&amp;gt;protected/migrations/{version}/{timestamp}-name-of-operations.php&amp;lt;/tt&amp;gt;&amp;quot;. Note, if this array is not empty, you may need to reverse-engineer or otherwise copy and re-purpose/re-write any migration scripts listed in order to perform their necessary operations. This is because they may depend on running within a Yii Framework environment, i.e. will contain references to components of the Yii application singleton such as &amp;quot;db&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Note that in upgrades, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; are replaced by &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; (respectively), and &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; are missing (because they aren't necessary).&lt;br /&gt;
&lt;br /&gt;
==== What you will need for the update, if using the manifest ====&lt;br /&gt;
''Among this data, what you will need:'' &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, which will give you the full list of files that need to be deleted, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, which will give you the full list of files to copy from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory, &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;, which give you values with which to update the main X2Engine configuration file, and, within each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, the sub-array properties &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;, with which you will construct the list of database commands to run. &lt;br /&gt;
&lt;br /&gt;
A procedure for manually updating X2Engine, with data from manifest.json, thus might look like this:&lt;br /&gt;
# For each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; (or in other words, each version's changes):&lt;br /&gt;
## Run all the SQL commands in its corresponding &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; sub-array, ignoring errors&lt;br /&gt;
## Run all SQL commands in its &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; array&lt;br /&gt;
## Perform database operations that would be equivalent to those performed in migration scripts&lt;br /&gt;
# For each file in the cumulative list &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, copy it from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory into its analogous location in the installation directory of X2Engine, creating parent directories as necessary if they don't already exist.&lt;br /&gt;
# Delete all files in the cumulative list &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; from the installation directory&lt;br /&gt;
# Clear the data cache files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt; and the temporary asset folders in &amp;lt;tt&amp;gt;assets/&amp;lt;/tt&amp;gt;&lt;br /&gt;
# Clear all session data (&amp;lt;tt&amp;gt;x2_sessions&amp;lt;/tt&amp;gt;) and auth cache data (&amp;lt;tt&amp;gt;x2_auth_cache&amp;lt;/tt&amp;gt;).&lt;br /&gt;
# Update the main X2Engine configuration (&amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt;) file, setting:&lt;br /&gt;
## &amp;lt;tt&amp;gt;$buildDate&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt; property,&lt;br /&gt;
## &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt; property, and &lt;br /&gt;
## &amp;lt;tt&amp;gt;$updaterVersion&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
&lt;br /&gt;
=== Enacting Database Changes ===&lt;br /&gt;
Inside the &amp;lt;tt&amp;gt;update/sqlList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/sqlForce&amp;lt;/tt&amp;gt; folder is a list of &amp;lt;tt&amp;gt;.sql&amp;lt;/tt&amp;gt; files, named according to their corresponding version's update, that can essentially be run as SQL scripts to perform the update. Note, however, that '''if the update traverses more than one version, the scripts must be run in the order of their corresponding version.''' This is because, in the database changes for any version, it is assumed that the database schema and contents conform to the version that immediately preceded it. The PHP function [[phpfunc:version-compare|version_compare]], and the method &amp;quot;[http://pythonhosted.org/setuptools/pkg_resources.html#parsing-utilities parse_version]&amp;quot;, in the pkg_resources module of setuptools, should correctly identify which version should come first. Note, the [[#The_structure_the_manifest|&amp;quot;versions&amp;quot; property of the manifest]] will contain the list of versions in the correct order.&lt;br /&gt;
&lt;br /&gt;
Furthermore, note, &amp;quot;sqlList&amp;quot; and &amp;quot;sqlForce&amp;quot; may not encompass all necessary database changes; if there are any migration scripts in the update, the operations they perform will similarly need to be performed in the current update (see the description of &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; in [[#The_structure_of_the_.22data.22_array|&amp;quot;the structure of the 'data' array&amp;quot;]]).&lt;br /&gt;
&lt;br /&gt;
In summary, for each version traversed in the update, the following operations should be run in order, with &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version number of the version in question:&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlForce/{version}.sql&amp;lt;/tt&amp;gt; file,''' in the order they appear in the file. Ignore if any commands fail. Note, if passed to the MySQL client as a script, any failures might trigger a halt in execution and thus skip the remainder of the commands.&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlList/{version}.sql&amp;lt;/tt&amp;gt; file.'''&lt;br /&gt;
# '''Perform operations equivalent to those in each of the migration scripts, if any.''' The first part of a script file name is the timestamp it was added to the codebase, and this also dictates the order in which they should be run.&lt;br /&gt;
&lt;br /&gt;
=== Copying new files ===&lt;br /&gt;
Files added/changed in the new version are stored in the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory. In an update, they should be copied into the installation directory, replacing any existing files of the same name/path. If the &amp;lt;tt&amp;gt;rsync&amp;lt;/tt&amp;gt; utility is available on the system, you can use one command to safely copy these files into the installation directory as follows (executed in that same directory):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rsync -avc update/source/ ./&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, you will need to use &amp;lt;tt&amp;gt;cat&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;cp&amp;lt;/tt&amp;gt;, two utilities that should be available on every UNIX-like system, and the lists of files added in each intermediate version. These lists are stored in the folder &amp;lt;tt&amp;gt;update/data/fileList&amp;lt;/tt&amp;gt; and named like &amp;quot;{version}.txt&amp;quot; (&amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version to which it applies), each file on its own line in that file. Run the following command, ignoring errors:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/fileList/*; do for l in $(cat $f); do cp update/source/&amp;quot;$l&amp;quot; ./&amp;quot;$l&amp;quot; ; done ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting old files ===&lt;br /&gt;
Files to be deleted are listed in a similar manner as the files to be added: one file per line. The list of files to delete are located at files with paths that look like this: &amp;lt;tt&amp;gt;update/data/deletionList/{version}.txt&amp;lt;/tt&amp;gt;&lt;br /&gt;
So, for an update from 5.5 to 5.7 with intermediate version 5.6 (for instance), the files would be &amp;lt;tt&amp;gt;update/data/deletionList/5.6&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/data/deletionList/5.7&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is only one version through which to update, the operation is very simple, and can be performed in one UNIX/Linux (bash) shell command as follows (if the version is 3.6.3, for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
xargs -a update/data/deletionList/3.6.3.txt -d&amp;quot;\n&amp;quot; rm -fv&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, if there are multiple versions, this may be a more tricky proposition. The necessity arises to create a cumulative list, because there might be the possibility that a file is deleted in one version and re-established in a new version. Nevertheless, while this is possible, it is highly unlikely, and in most cases it is safe to simply iterate over all the &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; data files and delete all the files listed therein. As a bash command:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/deletionList/*; do xargs -a $f -d&amp;quot;\n&amp;quot; rm -fv ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Editing the Configuration File ===&lt;br /&gt;
This final step in the update process, before refreshing, should not be done manually unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a fresh installation at the same version as the one to which you have updated.&lt;br /&gt;
&lt;br /&gt;
To update the configuration manually:&lt;br /&gt;
# Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor&lt;br /&gt;
# Find the line with the &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; variable in it. It should look like this (but with the version from which you are updating): &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$version = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change the value in quotes to the current version being updated to, and save the file.&lt;br /&gt;
&lt;br /&gt;
=== Clearing caches and refreshing the application ===&lt;br /&gt;
It is generally always a good idea to clear the application's server-side cache and other ephemeral files/data after updating. This is taken care of automatically by the built-in updater, but barring proper use and functioning of that, the operations can be performed manually as follows: &lt;br /&gt;
;clear the data cache&lt;br /&gt;
: delete all &amp;quot;&amp;lt;tt&amp;gt;.bin&amp;lt;/tt&amp;gt;&amp;quot; files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -fv protected/runtime/cache/*.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear web assets&lt;br /&gt;
: delete all sub-folders of &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt; by running this command (in the installation directory): &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -r assets/*/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear the &amp;quot;auth cache&amp;quot;&lt;br /&gt;
: Flush the auth cache table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;&lt;br /&gt;
DELETE FROM `x2_auth_cache` WHERE 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;log all users out&lt;br /&gt;
: Flush the sessions table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;DELETE FROM `x2_sessions` WHERE 1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Upgrading =&lt;br /&gt;
&lt;br /&gt;
== Using the Web Interface ==&lt;br /&gt;
[[File:Software-Upgrade-Registration.png|400px|thumb|right|Software upgrade page, in the first step (registration)]]&lt;br /&gt;
To upgrade to Professional Edition&lt;br /&gt;
# Purchase X2Engine Professional Edition Download. Note the registration info in the activation email.&lt;br /&gt;
# Go to the Admin page in X2Engine and find &amp;quot;Upgrade X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot;.&lt;br /&gt;
# In the ''Upgrade'' page, enter your registration info into the form and click &amp;quot;Register&amp;quot; to continue. Once complete, you will see a list of changes similar to those displayed when performing an update.&lt;br /&gt;
# Review the changes to be applied and then proceed by clicking &amp;quot;Upgrade&amp;quot;, similar to performing an update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Offline&amp;quot; Upgrading ==&lt;br /&gt;
This works in exactly the same way as offline updating. However, to obtain your upgrade package, which you will extract into a folder called &amp;quot;update&amp;quot; on the X2Engine web server, contact [mailto:customersupport@x2engine.com customer support]. Furthermore, you will need to run the following database command on your X2Engine database when done with the upgrade, replacing &amp;lt;tt&amp;gt;{product key}&amp;lt;/tt&amp;gt; with your product key, to allow your installation to receive Professional Edition software updates:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{product key}' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Manually Upgrading ==&lt;br /&gt;
The process of upgrading is very similar to that of updating, and uses all of the same underpinnings as the updater. Thus, almost all of the above information and procedures will apply to upgrades. However, note the following crucial differences:&lt;br /&gt;
&lt;br /&gt;
* '''X2Engine must be at the latest available version in order to upgrade.'''&lt;br /&gt;
* '''To obtain the upgrade package, you must be an owner of an X2Engine Professional Edition Download-type license.''' Contact [mailto:customersupport@x2engine.com customer support] for further details.&lt;br /&gt;
* '''There will be only one &amp;quot;intermediate&amp;quot; version.''' The changes to apply will move X2Engine from a version to that same version, applying instead the necessary changes to switch its ''edition'' from Open Source to Professional.&lt;br /&gt;
* '''There are no file deletions.'''&lt;br /&gt;
* '''The properties of the package manifest differ slightly.''' In upgrades, the properties of the data object are &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, etc. Note also, the update data is in aptly-named sub-folders of &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt; as well, and there are no &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; lists to handle.&lt;br /&gt;
* '''The configuration is not changed.''' Instead, the value stored in the field &amp;lt;tt&amp;gt;unique_id&amp;lt;/tt&amp;gt; of the admin record in the database table &amp;lt;tt&amp;gt;x2_admin&amp;lt;/tt&amp;gt; must be changed to the license key, and the &amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt; field must be changed to &amp;quot;pro&amp;quot;. To perform these both, run the following command on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''It is not necessary to clear caches or user sessions when done.''' These operations are performed in updates to eliminate ephemeral (session-ingrained) data that may no longer be applicable and thus cause problems. Upgrades will rarely, if ever, invalidate this data.&lt;br /&gt;
&lt;br /&gt;
= Updating to Beta Versions = &lt;br /&gt;
Beginning at version 4.1.2, after much advocacy to adopt this practice, X2Engine began a two-phase release process in which each version would be released first as a beta, then to the broader public after initial bug finds. The purpose behind this is to avoid updating production installations of X2Engine to new versions that have had zero public exposure, and thus avoid introducing bugs that were not found in initial QA testing (due to a incompatibility with a particular server environment or configuration of X2Engine) into sensitive production environments.&lt;br /&gt;
&lt;br /&gt;
By default, the updater will not update to beta versions. The updater must be configured to use the proper URL on the update server for beta release data, by the following procedure:&lt;br /&gt;
# Create a file &amp;quot;constants-custom.php&amp;quot; in the root level of X2Engine. You can easily do this by renaming &amp;quot;constants-custom.example.php&amp;quot; to that name (remove &amp;quot;.custom&amp;quot;).&lt;br /&gt;
# Look for the following line, and in it, change &amp;quot;false&amp;quot; to &amp;quot;true&amp;quot;: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('X2_UPDATE_BETA') or define('X2_UPDATE_BETA',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The advantage of enabling beta releases is that new features will be available much sooner. Additionally, fixes to bugs will also be available sooner. The main disadvantage of enabling beta releases is that they are never guaranteed to be as stable as non-beta releases. We encourage users to clone their installations of X2Engine, if able, and use beta versions on them, and report any issues they encounter for their particular server environment/special configuration/custom implementation of X2Engine. This way, any issues that arise can be fixed in the stable release. The more users participate in beta testing, the more stable the non-beta releases will become.&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
== User interface (UI) &amp;quot;bugs&amp;quot; after the update ==&lt;br /&gt;
It is extremely common (and easily remedied) that after an important update, certain features of the user interface of X2Engine may not work properly. This is caused, in nearly all cases, by the web browser's cache, which contains out-of-date copies of X2Engine's essential user interface files. To solve these problems, the first step to try is clearing the cache. A step-by-step guide to doing so is given in [[wikihow:Clear-Your-Browser%27s-Cache]].&lt;br /&gt;
&lt;br /&gt;
== Errors in the updater itself == &lt;br /&gt;
When all else fails, the updater may have to be patched due to an irreconcilable backwards compatibility issue. Fortunately, if one has read/write access to the source files on the webserver, this is straightforward to fix. See [[#Refreshing_and_Updating_the_Updater_Utility|Refreshing and Updating the Updater Utility]].&lt;br /&gt;
&lt;br /&gt;
== Professional Edition not in effect after upgrading ==&lt;br /&gt;
This may be caused by Professional Edition not getting &amp;quot;switched on&amp;quot; properly, despite all the necessary filesystem and database changes already being in effect. First, to see if this scenario applies to you, look for a folder &amp;quot;&amp;lt;tt&amp;gt;reports&amp;lt;/tt&amp;gt;&amp;quot; inside &amp;lt;tt&amp;gt;protected/modules&amp;lt;/tt&amp;gt;, and a table &amp;lt;tt&amp;gt;x2_reports&amp;lt;/tt&amp;gt; in the X2Engine database. If you find these things, you can try the following to enable Professional Edition:&lt;br /&gt;
# Find the file &amp;lt;tt&amp;gt;constants.php&amp;lt;/tt&amp;gt; in the installation directory. Open it in a text editor, and look for the following line: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('PRO_VERSION') or define('PRO_VERSION',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; on that line, and save the file.&lt;br /&gt;
# Run the following SQL command (replacing &amp;lt;tt&amp;gt;{license key}&amp;lt;/tt&amp;gt; with your X2Engine license key) on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General Procedures to Try ==&lt;br /&gt;
It may sometimes be possible that the update did not finish cleanly. In such cases, it will be necessary to manually re-perform some of the final operations in the update, described in [[#Introduction|Introduction]]. &lt;br /&gt;
&lt;br /&gt;
=== Refresh the Source Code ===&lt;br /&gt;
This step is not really the best step to try first, as it is usually more time-consuming. It entails re-uploading the entire application, in order to reset all non-ephemeral (source code) files to what they should be in a fresh installation of X2Engine. To do this:&lt;br /&gt;
# Download the refresh package. There should be a download link for this in the ''Updater Settings'' page. Otherwise, if that doesn't work and you're using Open Source edition, use [https://x2planet.com/installs/refresh.zip this link], or if using Professional Edition, paste the following url (after replacing {key} with your license key) into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/refresh-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Upload the ''contents'' of the downloaded zip archive to the installation directory, overwriting existing files.&lt;br /&gt;
&lt;br /&gt;
=== Clear Caches ===&lt;br /&gt;
See &amp;quot;[[#Clearing_caches_and_refreshing_the_application|Clearing Caches...]]&amp;quot; for details.&lt;br /&gt;
&lt;br /&gt;
=== Regenerate the Configuration ===&lt;br /&gt;
Again, this should not be done unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a new installation at the same version as the one you updated to. Refer to [[#Editing_the_Configuration_File|Editing the Configuration File]].&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1849</id>
		<title>Software Updates and Upgrades</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1849"/>
				<updated>2014-06-26T23:34:08Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* How to Avoid Timeouts */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
[[File:Update_Process.svg|200px|thumb|right|Top-level overview of the update process]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
The following things happen in typical a software update (or upgrade):&lt;br /&gt;
* Files that are new in the new version are added&lt;br /&gt;
* Files that have been changed in the new version are replaced with the new versions of those files &lt;br /&gt;
* Files that are no longer used in the new version are deleted&lt;br /&gt;
* Changes to the database are made to make the database compatible with the new version of X2Engine&lt;br /&gt;
* The main configuration file, &amp;lt;tt&amp;gt;X2Config.php&amp;lt;/tt&amp;gt;, is edited to reflect the new state of the application.&lt;br /&gt;
* X2Engine data caches are cleared&lt;br /&gt;
&lt;br /&gt;
These operations are typically handled by the updater utility component [[x2doc:UpdaterBehavior|UpdaterBehavior]] and controlled via the in-app web-based updater or the command line updater. This article will cover both how to use said interfaces to the updater, and how to perform updates manually or with the &amp;quot;offline&amp;quot; method if the need arises, in addition to troubleshooting and recovering from an update that did not complete cleanly.&lt;br /&gt;
&lt;br /&gt;
= Disclaimer =&lt;br /&gt;
This article covers the standard and recommended update/upgrade methods, in addition to alternate methods and supplemental procedures. As general rules and precautions for using this article:&lt;br /&gt;
# ''If you do not fully and completely understand any step of any instructions given in this article, '''do not''' follow them''; ask for help instead on [http://x2community.com the forums] or through contacting X2Engine Technical Support if you are an existing customer.&lt;br /&gt;
# You should always make a backup of all of your files ''and'' a full backup copy of your database, preferably in a format (i.e. SQL script) that can be used to create a verbatim/carbon copy of the original database, with all of the features of the original schema, '''before''' attempting any steps of the update process manually.&lt;br /&gt;
# Some of the procedures outlined in this article involve manually creating or modifying files. Note that the same filesystem prerequisite for installation, namely that the file ownership must match the user under which PHP executes on the web server, applies to these steps.&lt;br /&gt;
&lt;br /&gt;
Furthermore, '''in regard to uploading files:''' note that any instructions to upload folders into X2Engine, and replace/merge contents, refer to replacing files in X2Engine with files in the archive at analogous paths. For example, if you have a zip file that contains a folder &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, and inside of that is a folder &amp;lt;tt&amp;gt;components&amp;lt;/tt&amp;gt;, and inside of that is a file &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt;, and the instructions are to upload/replace in X2Engine, you should upload &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt; to replace the existing file of the same name in &amp;lt;tt&amp;gt;protected/components&amp;lt;/tt&amp;gt; inside of X2Engine.&lt;br /&gt;
&lt;br /&gt;
X2Engine Inc. cannot be held accountable for damage/errors caused by any missteps or mistakes in manually-performed update operations. If in doubt, make a sandbox/clone of your X2Engine installation by copying the files and database to a different server, and test performing the operations on that before proceeding with your production install of X2Engine.&lt;br /&gt;
&lt;br /&gt;
= Update Notifications =&lt;br /&gt;
[[File:Update Notification.png|200px|frame|right|System flash message displaying software update notification.]]&lt;br /&gt;
To get notified of software updates, unless already using X2Engine Professional Edition, one must first &amp;quot;subscribe&amp;quot; to them. This can be done via checking the box &amp;quot;Notify me of software updates&amp;quot; in the installation form. If X2Engine is already installed, this can be done by going to &amp;quot;Updater Settings&amp;quot; from the Admin page and enabling them. If using Professional Edition, these steps are not not necessary.&lt;br /&gt;
&lt;br /&gt;
Once that has been completed, set the update notification interval as desired in &amp;quot;Updater Settings&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Update Methods =&lt;br /&gt;
There are, in essence, just three basic methods of updating, and variations on each. The three main methods are: &lt;br /&gt;
# web updater,&lt;br /&gt;
# command line updater, and&lt;br /&gt;
# manually.&lt;br /&gt;
In each case, an update package from the update data server is used as the authoritative list of changes to apply, but in the non-manual methods, X2Engine's own updater class does all the work applying changes from said package.&lt;br /&gt;
&lt;br /&gt;
In web and command line updates, there is also the option to first download and unpack the package manually (see [[#Performing_.22Offline.22_Updates|performing &amp;quot;offline&amp;quot; updates]]). This circumvents the need for the local server (on which X2Engine is hosted) to be connected to the internet. Similarly, in the case of manual updates, it is not necessary for the web server to actually have an internet connection.&lt;br /&gt;
&lt;br /&gt;
== Using the Web Updater ==&lt;br /&gt;
[[File:Updater-1.png|300px|thumb|right|The updater utility page.]]&lt;br /&gt;
The web updater can be accessed by any of the following methods:&lt;br /&gt;
* Using the link in the notification message at the top of the window in versions later than 3.5.5&lt;br /&gt;
* By going to the Admin page and clicking the &amp;quot;Update&amp;quot; button at the top, in earlier versions, with update notifications enabled&lt;br /&gt;
* Through &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
First, navigate to the web updater page, and '''do not''' cancel the request (stop or hit your browser's &amp;quot;back&amp;quot; button) until the page has loaded. If the updater utility has undergone changes and needs to self-update to be compatible with the software updates server, this is when it will do so. Thus, it is critical that you do not interrupt this process. If the page does not load, or displays the error message that dependencies could not be retrieved, you may have to [[#Performing_.22Offline.22_Updates|perform an &amp;quot;offline&amp;quot; update]].&lt;br /&gt;
&lt;br /&gt;
There are three ways of getting to the updater page. One is to simply click the link given in the update notification flash message, if available. The other is to click on the &amp;quot;Update&amp;quot; button at the top of the admin page. If software update notifications are not enabled, one can manually check for and install an update by going to &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
Next, make a backup of the database and files (recommended), follow the instructions on the page, and click the &amp;quot;Update&amp;quot; button when ready.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
It is generally quick and is the easiest method to use, and it does not require shell/SSH access to the web server.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
It is not recommended for use on large installations of X2Engine, i.e. containing over a million records, or for updating from very old versions (i.e. a version more than five months old). This is because the final update process may be cut short by server timeout or an interruption in the network connection. This can result in an incomplete/broken update that requires recovering manually (a potentially long process) at best, or actual loss of data at worst. &lt;br /&gt;
&lt;br /&gt;
==== Explanation ====&lt;br /&gt;
The inherent weakness of the web updater (or any web-based updater to any web application) is that it must always rely upon web requests to the server, and it thus must perform extremely critical operations for the update all within the scope of a web request. Web requests, it goes without saying, are not always reliable. For example, one's internet connection might get cut off due being on a wireless network and encountering interference. Furthermore, if any operations take an exceedingly long amount of time, e.g. because of the size of the data set (and thus indexes) on which the update must operate, or the number of database changes that need to be applied, the web request for those operations may take longer than the PHP execution time limit as configured on the webserver. This would potentially cut short the execution of a critical update stage, and thus the update would require manual recovery at best, or completely restoring  X2Engine to a backup at worst.&lt;br /&gt;
&lt;br /&gt;
==== How to Avoid Timeouts ====&lt;br /&gt;
You can usually get around this by setting the value of &amp;lt;tt&amp;gt;'''max_execution_time'''&amp;lt;/tt&amp;gt; to a higher value in the web server's PHP configuration. It is highly recommended that you either disable the timeout or set it to a very generous value (such as 300 or more seconds) while attempting to update via the web updater, especially if you are many versions behind or have a high volume of data in your CRM.&lt;br /&gt;
Also, it is generally always a good idea to create a backup copy ''before'' updating, and to download it.&lt;br /&gt;
&lt;br /&gt;
==== Recovering From Crashes ====&lt;br /&gt;
If you downloaded the backup copy of your database, then after you encounter a failure/timeout, you can use the .sql backup script to restore the database to its original form. However, be aware, that if there are any '''&amp;lt;tt&amp;gt;CREATE TABLE&amp;lt;/tt&amp;gt;''' commands in the update data (expand the ''Database Commands to Run'' list to see), you will need to manually drop them in order to once again make your installation compatible with the update.&lt;br /&gt;
&lt;br /&gt;
== Using the Command Line Updater ==&lt;br /&gt;
This update method uses all of the same thoroughly-tested underpinnings and processes as the web updater, but does so all within a system command to the Yii console.&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
While in via SSH, or in a terminal emulator, or in a script, run the following command in the &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt; subdirectory of X2Engine:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./yiic update app&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the following optional arguments to the command line updater:&lt;br /&gt;
;&amp;lt;tt&amp;gt;--force&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; for this option (i.e. &amp;lt;tt&amp;gt;--force=1&amp;lt;/tt&amp;gt;) to proceed with the update even if compatibility issues were detected. Default: 0 (halt if compatibility issues were detected).&lt;br /&gt;
;&amp;lt;tt&amp;gt;--backup&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that a database backup should be performed first before updating, &amp;quot;0&amp;quot; to proceed without creating a backup first. Default: 1.&lt;br /&gt;
;&amp;lt;tt&amp;gt;--lock&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that the application should be locked during the final installation of the update package to prevent data entry during the sensitive moments when the database is being altered and the files updated, &amp;quot;0&amp;quot; to permit use even when changes to the app are being applied. Default: 0.&lt;br /&gt;
&lt;br /&gt;
If running the command gives you the following message:&lt;br /&gt;
&amp;lt;pre&amp;gt;The updater is now up-to-date and compliant with the updates server. Re-run the command to proceed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
That indicates that the updater first needed to self-update, and succeeded in doing so. You should then run the same command again to complete the update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Safer to use on large, mission-critical systems. The success of a software update (or upgrade) does not hinge upon a single web request completing successfully.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
More difficult to use; requires shell access to the web server. The console command must be run as the same system user that owns all the files and directories of X2Engine, in order to circumvent permission issues when manipulating file system objects.&lt;br /&gt;
&lt;br /&gt;
== Using the Automatic Updater ==&lt;br /&gt;
[[File:Auto-Update.png|400px|thumb|right|Auto-update scheduler on the Updater Settings page]]&lt;br /&gt;
This method uses the command line updater, but runs the command on your behalf using the local cron daemon. To configure, go to the Updater Settings Page, check the box titled &amp;quot;Update Automatically&amp;quot;, and save. If you have Professional Edition, you can manage the update schedule in addition to other automated tasks from the Cron Table settings page, which can also be found under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Automates the entire process of updating.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
Only available on UNIX-like systems that have a cron daemon, and which permit user access to the cron table.&lt;br /&gt;
&lt;br /&gt;
== Performing &amp;quot;Offline&amp;quot; Updates ==&lt;br /&gt;
In some server environments, the updater may not be able to access the updates server by itself, due to (for example) the PHP or network configuration, or because the &amp;quot;zip&amp;quot; PHP extension (requried) is unavailable or has been disabled. It is in these situations necessary to download the update package manually, unpack on the server (or unpack and upload the files to the server), and then run the updater. When performing updates in this way, the updater will not need to access the updates server, but will use the manually-downloaded and extracted package files present in the local filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Refreshing and Updating the Updater Utility ===&lt;br /&gt;
Before starting, note that the updater itself may have undergone changes; the X2Engine update process is subject to change. To address this technical challenge, the updater utility within X2Engine would normally first attempt to download the latest version of itself, in order to be compatible with the updates server. However, this sometimes will not work properly. If your X2Engine installation is running in a hosting environment that prevents it from making outbound HTTP requests (the [[requirements:|requirements check script]] should tell you if this is the case), you will need to do this manually. Also, in version 3.5.5, there was a bug in the updater utility's self-update methods.&lt;br /&gt;
&lt;br /&gt;
To update the updater utility:&lt;br /&gt;
# '''Determine the version of the updater utility package''' by visiting [https://x2planet.com/installs/updates/updateCheck this link]. You should see a page that's entirely blank except for a version number.&lt;br /&gt;
# '''Download the updater package.''' In versions 3.6 and later, a download link for this should be available through the ''Updater Settings'' page. Otherwise, use the following URL (if in Open Source Edition): &amp;lt;pre&amp;gt;https://x2planet.com/installs/updater.zip&amp;lt;/pre&amp;gt; Otherwise, if using Professional Edition, paste the following url, after replacing {key} with your license key, into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/updater-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# '''Extract the downloaded zip archive and upload''' the &amp;quot;protected&amp;quot; folder to your server. Merge folders and overwrite existing files when prompted.&lt;br /&gt;
# '''Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor''' and find the line that looks like this:&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$updaterVersion = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# '''Change the text inside the pair of single quotes''' on that line so that it is the current updater version obtained in step #1. &lt;br /&gt;
# '''Save the file.'''&lt;br /&gt;
&lt;br /&gt;
=== Obtaining the Update Package ===&lt;br /&gt;
First, use the link in the ''Updater Settings'' page entitled &amp;quot;Latest Update Package for Version...&amp;quot;, to download the update package. If the link in the Updater Settings page does not work, use the following URL to download the update package (if using Open Source Edition), replacing &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; with your X2Engine installation's version:&lt;br /&gt;
&amp;lt;pre&amp;gt;https://x2planet.com/installs/updates/{version}/none&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should receive, from that URL, a zip archive file called &amp;quot;update.zip&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If using Professional Edition, and your download link is not working, contact [mailto:customersupport@x2engine.com customer support].&lt;br /&gt;
&lt;br /&gt;
=== Unpacking the Update ===&lt;br /&gt;
[[File:Update-in-Progress.png|200px|frame|right|System flash message displaying notification of pending update.]]&lt;br /&gt;
The update package will be a zip archive. The ''contents'' of this archive must be extracted ''into'' a folder called &amp;quot;update&amp;quot; in the installation directory. So, in other words, after extracting and uploading to the server, there should be a folder called &amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt; alongside &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;framework&amp;lt;/tt&amp;gt; (etc). Within that folder (&amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt;) should be the following contents:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A file containing important data to be used by the updater&lt;br /&gt;
;&amp;lt;tt&amp;gt;contents.md5&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A message digest file containing MD5 checksums of all files in the archive excluding itself.&lt;br /&gt;
;&amp;lt;tt&amp;gt;source&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A sub-folder/directory, whose internal structure mimics that of X2Engine, containing files that were changed in the update. If there were no files changed (i.e. only database changes were in the update), this directory will not be present.&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A directory containing flat data files. These files are typically not used by the updater, but are useful if performing manual updates or recovery.&lt;br /&gt;
&lt;br /&gt;
=== Applying the Update ===&lt;br /&gt;
[[File:Steps-post-Unpack.png|200px|frame|right|Steps of the update process in the event of manually-extracted package (or resuming a preexisting update)]]&lt;br /&gt;
After unpacking, if you run the command line updater, it should (if no problems arise, i.e. incompatible package version) apply the update straight away. Otherwise, log into X2Engine, and you should see a system flash message notifying you of an update in progress. This message shows up whenever there is an update package folder (&amp;quot;update&amp;quot;) present on the server. If you then go to the updater page, once the page has finished loading, you should see that there only remain the steps &amp;quot;Review and confirm changes&amp;quot;, &amp;quot;Apply changes&amp;quot; and &amp;quot;Reload&amp;quot; remain to be completed. To complete the update, when ready, click &amp;quot;Update&amp;quot;; otherwise, to delete the &amp;quot;update&amp;quot; folder and cancel the update, click &amp;quot;Cancel&amp;quot; either on the updater page or in the system flash message.&lt;br /&gt;
&lt;br /&gt;
== Performing Updates Manually ==&lt;br /&gt;
While it is preferable to avoid manually applying changes to X2Engine, and you may never need to, it may in some circumstance or another actually be necessary to do so. For all such situations, the raw update data files generated by the X2Engine update build process are bundled with every update package to help you perform the operations described in [[#Introduction|&amp;quot;Introduction&amp;quot;]]. This section will describe how to perform each of those operations. &lt;br /&gt;
&lt;br /&gt;
=== Prerequisites ===&lt;br /&gt;
First and foremost, all shell commands listed here, and in general, any operations that create or modify files, ''must be run as the same system user under which the server runs PHP scripts.'' Otherwise, the ownership and/or permissions of files and directories will have to be changed after completing all changes.&lt;br /&gt;
&lt;br /&gt;
To obtain the update package (which will be necessary), refer to &amp;quot;[[#Obtaining_the_Update_Package|Obtaining the Update Package]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Furthermore, it is strongly advised that you first make a back-up copy of all files and of X2Engine's database. Your database backup would best be a full, exact copy, such as one that would be made by [http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html mysqldump], or the built-in export tools of [http://www.phpmyadmin.net/home_page/index.php PHPMyAdmin].&lt;br /&gt;
&lt;br /&gt;
The X2Engine global import/export utility (&amp;quot;Import All Data&amp;quot;/&amp;quot;Export All Data&amp;quot;) in Admin '''is not recommended by itself as the sole backup tool'''. This is especially important to note because of how this tool cannot re-create custom fields; they must exist in the target installation before the import. However, using it to ''supplement'' your standard database backup method will prove useful in the event that you:&lt;br /&gt;
# don't have any custom fields,&lt;br /&gt;
# cannot manage to update X2Engine with success, and&lt;br /&gt;
# wish to start over with a fresh installation.&lt;br /&gt;
In such a case, you will then be able to import your old data into the new installation.&lt;br /&gt;
&lt;br /&gt;
=== Introduction: Data Files ===&lt;br /&gt;
In [[#Unpacking_the_Update|Unpacking the update]], a rough overview of an update package's contents were given. In this section, the anatomy and purpose of each of the data files will be introduced.&lt;br /&gt;
&lt;br /&gt;
Included in every update package is two copies of the following data:&lt;br /&gt;
* Files to copy&lt;br /&gt;
* Files to delete&lt;br /&gt;
* Database changes to enact&lt;br /&gt;
* Migration scripts to run, if any&lt;br /&gt;
&lt;br /&gt;
The first, immediately visible place where all of this data is stored is in the file &amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;, as a JSON-encoded array. See the next section for how to use this file.&lt;br /&gt;
&lt;br /&gt;
The second place, which can be useful to users who do not wish to automate or script any step of a manual update, is in the files contained in the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; directory. Each file is named after the version to which it corresponds, and contains the data required to update ''to'' that version ''from the version immediately preceding it.''' Thus, the versions you should see in each file name are the versions above your X2Engine installation's current version, up to and including the version being updated to.&lt;br /&gt;
&lt;br /&gt;
In an update, the directory will contain the following subdirectories:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of files to copy. The format is each line containing a file path in the list.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of obsolete files for that version which will need to be deleted. The format is the same as in &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts corresponding to each version. Each file is formatted such that it can be directly run on the database as an SQL script, although this is not generally recommended. The list of SQL commands to run is a of the following format: &lt;br /&gt;
  [query 1];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [query 2];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [etc]&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts with similar naming and formatting conventions to the &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; scripts. See [[#The_structure_of_the_.22data.22_array|The structure of the &amp;quot;data&amp;quot; array]] for explanation of the purpose of &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== How to use the manifest for updating manually ===&lt;br /&gt;
If you so choose, you may write a script to perform all of the operations for an update in one fell swoop. If so, and if the preferred scripting language of choice has a JSON-parsing library available, you will be able to save yourself a lot of effort by using the main data file (&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;) to load all of the necessary data for the update.&lt;br /&gt;
&lt;br /&gt;
==== The structure the manifest ====&lt;br /&gt;
In updates, the contents of the plain-text file manifest.json are a JSON-encoded object containing the following properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the data corresponding to each intermediate version. It is, in essence, all the data stored in the files in &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt;. If the update is for an installation of X2Engine that is only one version behind, it will contain only one element. Each element of this array is itself an array containing respective data for the intermediate version.&lt;br /&gt;
;&amp;lt;tt&amp;gt;versions&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array listing the version numbers of each intermediate version in the update. Similar to &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, this array will contain only one element if updating to the next version, and that version ''will be the next version''.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of deletions of obsolete files across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of files added or changed across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''from'', or in other words, the version of X2Engine before the package is applied. '''This must be the same as the version of installation to which the package is being applied.''' If this is not so, the updater utility would exit immediately, as applying the changes in the update would have potentially harmful results to data and/or compatibility with future updates.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''to'', or in other words, the version of X2Engine before the package is applied.&lt;br /&gt;
;&amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the version of the updater utility for which the package was compiled. If the structure of the update package or of the manifest changes, the updater must also change to accommodate and recognize the changes; else it might try to access properties that don't exist, or ignore important ones that do. This is the single property of manifest.json that should never change.&lt;br /&gt;
;&amp;lt;tt&amp;gt;scenario&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, which should be &amp;quot;update&amp;quot; if the package is for an update. In the case of upgrades, which use most of the same functionality of the updater, it should be &amp;quot;upgrade&amp;quot;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation after the package is applied. It should be &amp;quot;opensource&amp;quot; for open source edition, &amp;quot;pro&amp;quot; for professional edition. In the case of an upgrade package, it differs from the &amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation before the package is applied. &lt;br /&gt;
;&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An integer denoting the timestamp of the release date of the version of X2Engine denoted by &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The structure of the &amp;quot;data&amp;quot; array ====&lt;br /&gt;
In updates, each version's data, contained in each element of the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, contains the following nested array properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files added for the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files deleted in the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of SQL commands to bring the database structure and contents into compliance with the version from the previous version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A prerequisite list of SQL commands that should be run before those in &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, and if any fail, the failures should be ignored. These are added to updates whenever it is necessary to normalize the database schema, i.e. in the event that some installations' databases may not be consistent with others, but the commands might fail on every other system. These SQL commands were intended to be &amp;quot;attempted&amp;quot;; they are not guaranteed to succeed on all installations. An example of when this is necessary would be adding a foreign key constraint when in previous versions the storage engine was left to the database's default settings in old versions. MyISAM does not natively support foreign key constraints. Thus, to standardize this across all installations, including installations on servers where the storage engine defaulted to MyISAM, the constraint must first be dropped (in a &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; command) before the relevant tables are changed to the InnoDB storage engine, and the constraint is added back again, in the usual &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;version&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The version number&lt;br /&gt;
;&amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The edition&lt;br /&gt;
;&amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of PHP scripts that would be run in an update for this version. These are a subset of fileList and are typically used for advanced database operations, i.e. restructuring values of JSON-encoded fields, which are extremely impractical (or impossible) to perform using raw SQL commands. The files contained in the list all have paths that look like &amp;quot;&amp;lt;tt&amp;gt;protected/migrations/{version}/{timestamp}-name-of-operations.php&amp;lt;/tt&amp;gt;&amp;quot;. Note, if this array is not empty, you may need to reverse-engineer or otherwise copy and re-purpose/re-write any migration scripts listed in order to perform their necessary operations. This is because they may depend on running within a Yii Framework environment, i.e. will contain references to components of the Yii application singleton such as &amp;quot;db&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Note that in upgrades, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; are replaced by &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; (respectively), and &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; are missing (because they aren't necessary).&lt;br /&gt;
&lt;br /&gt;
==== What you will need for the update, if using the manifest ====&lt;br /&gt;
''Among this data, what you will need:'' &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, which will give you the full list of files that need to be deleted, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, which will give you the full list of files to copy from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory, &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;, which give you values with which to update the main X2Engine configuration file, and, within each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, the sub-array properties &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;, with which you will construct the list of database commands to run. &lt;br /&gt;
&lt;br /&gt;
A procedure for manually updating X2Engine, with data from manifest.json, thus might look like this:&lt;br /&gt;
# For each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; (or in other words, each version's changes):&lt;br /&gt;
## Run all the SQL commands in its corresponding &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; sub-array, ignoring errors&lt;br /&gt;
## Run all SQL commands in its &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; array&lt;br /&gt;
## Perform database operations that would be equivalent to those performed in migration scripts&lt;br /&gt;
# For each file in the cumulative list &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, copy it from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory into its analogous location in the installation directory of X2Engine, creating parent directories as necessary if they don't already exist.&lt;br /&gt;
# Delete all files in the cumulative list &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; from the installation directory&lt;br /&gt;
# Clear the data cache files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt; and the temporary asset folders in &amp;lt;tt&amp;gt;assets/&amp;lt;/tt&amp;gt;&lt;br /&gt;
# Clear all session data (&amp;lt;tt&amp;gt;x2_sessions&amp;lt;/tt&amp;gt;) and auth cache data (&amp;lt;tt&amp;gt;x2_auth_cache&amp;lt;/tt&amp;gt;).&lt;br /&gt;
# Update the main X2Engine configuration (&amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt;) file, setting:&lt;br /&gt;
## &amp;lt;tt&amp;gt;$buildDate&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt; property,&lt;br /&gt;
## &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt; property, and &lt;br /&gt;
## &amp;lt;tt&amp;gt;$updaterVersion&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
&lt;br /&gt;
=== Enacting Database Changes ===&lt;br /&gt;
Inside the &amp;lt;tt&amp;gt;update/sqlList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/sqlForce&amp;lt;/tt&amp;gt; folder is a list of &amp;lt;tt&amp;gt;.sql&amp;lt;/tt&amp;gt; files, named according to their corresponding version's update, that can essentially be run as SQL scripts to perform the update. Note, however, that '''if the update traverses more than one version, the scripts must be run in the order of their corresponding version.''' This is because, in the database changes for any version, it is assumed that the database schema and contents conform to the version that immediately preceded it. The PHP function [[phpfunc:version-compare|version_compare]], and the method &amp;quot;[http://pythonhosted.org/setuptools/pkg_resources.html#parsing-utilities parse_version]&amp;quot;, in the pkg_resources module of setuptools, should correctly identify which version should come first. Note, the [[#The_structure_the_manifest|&amp;quot;versions&amp;quot; property of the manifest]] will contain the list of versions in the correct order.&lt;br /&gt;
&lt;br /&gt;
Furthermore, note, &amp;quot;sqlList&amp;quot; and &amp;quot;sqlForce&amp;quot; may not encompass all necessary database changes; if there are any migration scripts in the update, the operations they perform will similarly need to be performed in the current update (see the description of &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; in [[#The_structure_of_the_.22data.22_array|&amp;quot;the structure of the 'data' array&amp;quot;]]).&lt;br /&gt;
&lt;br /&gt;
In summary, for each version traversed in the update, the following operations should be run in order, with &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version number of the version in question:&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlForce/{version}.sql&amp;lt;/tt&amp;gt; file,''' in the order they appear in the file. Ignore if any commands fail. Note, if passed to the MySQL client as a script, any failures might trigger a halt in execution and thus skip the remainder of the commands.&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlList/{version}.sql&amp;lt;/tt&amp;gt; file.'''&lt;br /&gt;
# '''Perform operations equivalent to those in each of the migration scripts, if any.''' The first part of a script file name is the timestamp it was added to the codebase, and this also dictates the order in which they should be run.&lt;br /&gt;
&lt;br /&gt;
=== Copying new files ===&lt;br /&gt;
Files added/changed in the new version are stored in the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory. In an update, they should be copied into the installation directory, replacing any existing files of the same name/path. If the &amp;lt;tt&amp;gt;rsync&amp;lt;/tt&amp;gt; utility is available on the system, you can use one command to safely copy these files into the installation directory as follows (executed in that same directory):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rsync -avc update/source/ ./&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, you will need to use &amp;lt;tt&amp;gt;cat&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;cp&amp;lt;/tt&amp;gt;, two utilities that should be available on every UNIX-like system, and the lists of files added in each intermediate version. These lists are stored in the folder &amp;lt;tt&amp;gt;update/data/fileList&amp;lt;/tt&amp;gt; and named like &amp;quot;{version}.txt&amp;quot; (&amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version to which it applies), each file on its own line in that file. Run the following command, ignoring errors:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/fileList/*; do for l in $(cat $f); do cp update/source/&amp;quot;$l&amp;quot; ./&amp;quot;$l&amp;quot; ; done ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting old files ===&lt;br /&gt;
Files to be deleted are listed in a similar manner as the files to be added: one file per line. The list of files to delete are located at files with paths that look like this: &amp;lt;tt&amp;gt;update/data/deletionList/{version}.txt&amp;lt;/tt&amp;gt;&lt;br /&gt;
So, for an update from 5.5 to 5.7 with intermediate version 5.6 (for instance), the files would be &amp;lt;tt&amp;gt;update/data/deletionList/5.6&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/data/deletionList/5.7&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is only one version through which to update, the operation is very simple, and can be performed in one UNIX/Linux (bash) shell command as follows (if the version is 3.6.3, for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
xargs -a update/data/deletionList/3.6.3.txt -d&amp;quot;\n&amp;quot; rm -fv&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, if there are multiple versions, this may be a more tricky proposition. The necessity arises to create a cumulative list, because there might be the possibility that a file is deleted in one version and re-established in a new version. Nevertheless, while this is possible, it is highly unlikely, and in most cases it is safe to simply iterate over all the &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; data files and delete all the files listed therein. As a bash command:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/deletionList/*; do xargs -a $f -d&amp;quot;\n&amp;quot; rm -fv ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Editing the Configuration File ===&lt;br /&gt;
This final step in the update process, before refreshing, should not be done manually unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a fresh installation at the same version as the one to which you have updated.&lt;br /&gt;
&lt;br /&gt;
To update the configuration manually:&lt;br /&gt;
# Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor&lt;br /&gt;
# Find the line with the &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; variable in it. It should look like this (but with the version from which you are updating): &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$version = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change the value in quotes to the current version being updated to, and save the file.&lt;br /&gt;
&lt;br /&gt;
=== Clearing caches and refreshing the application ===&lt;br /&gt;
It is generally always a good idea to clear the application's server-side cache and other ephemeral files/data after updating. This is taken care of automatically by the built-in updater, but barring proper use and functioning of that, the operations can be performed manually as follows: &lt;br /&gt;
;clear the data cache&lt;br /&gt;
: delete all &amp;quot;&amp;lt;tt&amp;gt;.bin&amp;lt;/tt&amp;gt;&amp;quot; files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -fv protected/runtime/cache/*.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear web assets&lt;br /&gt;
: delete all sub-folders of &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt; by running this command (in the installation directory): &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -r assets/*/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear the &amp;quot;auth cache&amp;quot;&lt;br /&gt;
: Flush the auth cache table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;&lt;br /&gt;
DELETE FROM `x2_auth_cache` WHERE 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;log all users out&lt;br /&gt;
: Flush the sessions table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;DELETE FROM `x2_sessions` WHERE 1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Upgrading =&lt;br /&gt;
&lt;br /&gt;
== Using the Web Interface ==&lt;br /&gt;
[[File:Software-Upgrade-Registration.png|400px|thumb|right|Software upgrade page, in the first step (registration)]]&lt;br /&gt;
To upgrade to Professional Edition&lt;br /&gt;
# Purchase X2Engine Professional Edition Download. Note the registration info in the activation email.&lt;br /&gt;
# Go to the Admin page in X2Engine and find &amp;quot;Upgrade X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot;.&lt;br /&gt;
# In the ''Upgrade'' page, enter your registration info into the form and click &amp;quot;Register&amp;quot; to continue. Once complete, you will see a list of changes similar to those displayed when performing an update.&lt;br /&gt;
# Review the changes to be applied and then proceed by clicking &amp;quot;Upgrade&amp;quot;, similar to performing an update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Offline&amp;quot; Upgrading ==&lt;br /&gt;
This works in exactly the same way as offline updating. However, to obtain your upgrade package, which you will extract into a folder called &amp;quot;update&amp;quot; on the X2Engine web server, contact [mailto:customersupport@x2engine.com customer support]. Furthermore, you will need to run the following database command on your X2Engine database when done with the upgrade, replacing &amp;lt;tt&amp;gt;{product key}&amp;lt;/tt&amp;gt; with your product key, to allow your installation to receive Professional Edition software updates:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{product key}' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Manually Upgrading ==&lt;br /&gt;
The process of upgrading is very similar to that of updating, and uses all of the same underpinnings as the updater. Thus, almost all of the above information and procedures will apply to upgrades. However, note the following crucial differences:&lt;br /&gt;
&lt;br /&gt;
* '''X2Engine must be at the latest available version in order to upgrade.'''&lt;br /&gt;
* '''To obtain the upgrade package, you must be an owner of an X2Engine Professional Edition Download-type license.''' Contact [mailto:customersupport@x2engine.com customer support] for further details.&lt;br /&gt;
* '''There will be only one &amp;quot;intermediate&amp;quot; version.''' The changes to apply will move X2Engine from a version to that same version, applying instead the necessary changes to switch its ''edition'' from Open Source to Professional.&lt;br /&gt;
* '''There are no file deletions.'''&lt;br /&gt;
* '''The properties of the package manifest differ slightly.''' In upgrades, the properties of the data object are &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, etc. Note also, the update data is in aptly-named sub-folders of &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt; as well, and there are no &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; lists to handle.&lt;br /&gt;
* '''The configuration is not changed.''' Instead, the value stored in the field &amp;lt;tt&amp;gt;unique_id&amp;lt;/tt&amp;gt; of the admin record in the database table &amp;lt;tt&amp;gt;x2_admin&amp;lt;/tt&amp;gt; must be changed to the license key, and the &amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt; field must be changed to &amp;quot;pro&amp;quot;. To perform these both, run the following command on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''It is not necessary to clear caches or user sessions when done.''' These operations are performed in updates to eliminate ephemeral (session-ingrained) data that may no longer be applicable and thus cause problems. Upgrades will rarely, if ever, invalidate this data.&lt;br /&gt;
&lt;br /&gt;
= Updating to Beta Versions = &lt;br /&gt;
Beginning at version 4.1.2, after much advocacy to adopt this practice, X2Engine began a two-phase release process in which each version would be released first as a beta, then to the broader public after initial bug finds. The purpose behind this is to avoid updating production installations of X2Engine to new versions that have had zero public exposure, and thus avoid introducing bugs that were not found in initial QA testing (due to a incompatibility with a particular server environment or configuration of X2Engine) into sensitive production environments.&lt;br /&gt;
&lt;br /&gt;
By default, the updater will not update to beta versions. The updater must be configured to use the proper URL on the update server for beta release data, by the following procedure:&lt;br /&gt;
# Create a file &amp;quot;constants-custom.php&amp;quot; in the root level of X2Engine. You can easily do this by renaming &amp;quot;constants-custom.example.php&amp;quot; to that name (remove &amp;quot;.custom&amp;quot;).&lt;br /&gt;
# Look for the following line, and in it, change &amp;quot;false&amp;quot; to &amp;quot;true&amp;quot;: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('X2_UPDATE_BETA') or define('X2_UPDATE_BETA',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The advantage of enabling beta releases is that new features will be available much sooner. Additionally, fixes to bugs will also be available sooner. The main disadvantage of enabling beta releases is that they are never guaranteed to be as stable as non-beta releases. We encourage users to clone their installations of X2Engine, if able, and use beta versions on them, and report any issues they encounter for their particular server environment/special configuration/custom implementation of X2Engine. This way, any issues that arise can be fixed in the stable release. The more users participate in beta testing, the more stable the non-beta releases will become.&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
== User interface (UI) &amp;quot;bugs&amp;quot; after the update ==&lt;br /&gt;
It is extremely common (and easily remedied) that after an important update, certain features of the user interface of X2Engine may not work properly. This is caused, in nearly all cases, by the web browser's cache, which contains out-of-date copies of X2Engine's essential user interface files. To solve these problems, the first step to try is clearing the cache. A step-by-step guide to doing so is given in [[wikihow:Clear-Your-Browser%27s-Cache]].&lt;br /&gt;
&lt;br /&gt;
== Errors in the updater itself == &lt;br /&gt;
When all else fails, the updater may have to be patched due to an irreconcilable backwards compatibility issue. Fortunately, if one has read/write access to the source files on the webserver, this is straightforward to fix. See [[#Refreshing_and_Updating_the_Updater_Utility|Refreshing and Updating the Updater Utility]].&lt;br /&gt;
&lt;br /&gt;
== Professional Edition not in effect after upgrading ==&lt;br /&gt;
This may be caused by Professional Edition not getting &amp;quot;switched on&amp;quot; properly, despite all the necessary filesystem and database changes already being in effect. First, to see if this scenario applies to you, look for a folder &amp;quot;&amp;lt;tt&amp;gt;reports&amp;lt;/tt&amp;gt;&amp;quot; inside &amp;lt;tt&amp;gt;protected/modules&amp;lt;/tt&amp;gt;, and a table &amp;lt;tt&amp;gt;x2_reports&amp;lt;/tt&amp;gt; in the X2Engine database. If you find these things, you can try the following to enable Professional Edition:&lt;br /&gt;
# Find the file &amp;lt;tt&amp;gt;constants.php&amp;lt;/tt&amp;gt; in the installation directory. Open it in a text editor, and look for the following line: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('PRO_VERSION') or define('PRO_VERSION',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; on that line, and save the file.&lt;br /&gt;
# Run the following SQL command (replacing &amp;lt;tt&amp;gt;{license key}&amp;lt;/tt&amp;gt; with your X2Engine license key) on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General Procedures to Try ==&lt;br /&gt;
It may sometimes be possible that the update did not finish cleanly. In such cases, it will be necessary to manually re-perform some of the final operations in the update, described in [[#Introduction|Introduction]]. &lt;br /&gt;
&lt;br /&gt;
=== Refresh the Source Code ===&lt;br /&gt;
This step is not really the best step to try first, as it is usually more time-consuming. It entails re-uploading the entire application, in order to reset all non-ephemeral (source code) files to what they should be in a fresh installation of X2Engine. To do this:&lt;br /&gt;
# Download the refresh package. There should be a download link for this in the ''Updater Settings'' page. Otherwise, if that doesn't work and you're using Open Source edition, use [https://x2planet.com/installs/refresh.zip this link], or if using Professional Edition, paste the following url (after replacing {key} with your license key) into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/refresh-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Upload the ''contents'' of the downloaded zip archive to the installation directory, overwriting existing files.&lt;br /&gt;
&lt;br /&gt;
=== Clear Caches ===&lt;br /&gt;
See &amp;quot;[[#Clearing_caches_and_refreshing_the_application|Clearing Caches...]]&amp;quot; for details.&lt;br /&gt;
&lt;br /&gt;
=== Regenerate the Configuration ===&lt;br /&gt;
Again, this should not be done unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a new installation at the same version as the one you updated to. Refer to [[#Editing_the_Configuration_File|Editing the Configuration File]].&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1848</id>
		<title>Software Updates and Upgrades</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1848"/>
				<updated>2014-06-26T23:33:10Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Using the Web Updater */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
[[File:Update_Process.svg|200px|thumb|right|Top-level overview of the update process]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
The following things happen in typical a software update (or upgrade):&lt;br /&gt;
* Files that are new in the new version are added&lt;br /&gt;
* Files that have been changed in the new version are replaced with the new versions of those files &lt;br /&gt;
* Files that are no longer used in the new version are deleted&lt;br /&gt;
* Changes to the database are made to make the database compatible with the new version of X2Engine&lt;br /&gt;
* The main configuration file, &amp;lt;tt&amp;gt;X2Config.php&amp;lt;/tt&amp;gt;, is edited to reflect the new state of the application.&lt;br /&gt;
* X2Engine data caches are cleared&lt;br /&gt;
&lt;br /&gt;
These operations are typically handled by the updater utility component [[x2doc:UpdaterBehavior|UpdaterBehavior]] and controlled via the in-app web-based updater or the command line updater. This article will cover both how to use said interfaces to the updater, and how to perform updates manually or with the &amp;quot;offline&amp;quot; method if the need arises, in addition to troubleshooting and recovering from an update that did not complete cleanly.&lt;br /&gt;
&lt;br /&gt;
= Disclaimer =&lt;br /&gt;
This article covers the standard and recommended update/upgrade methods, in addition to alternate methods and supplemental procedures. As general rules and precautions for using this article:&lt;br /&gt;
# ''If you do not fully and completely understand any step of any instructions given in this article, '''do not''' follow them''; ask for help instead on [http://x2community.com the forums] or through contacting X2Engine Technical Support if you are an existing customer.&lt;br /&gt;
# You should always make a backup of all of your files ''and'' a full backup copy of your database, preferably in a format (i.e. SQL script) that can be used to create a verbatim/carbon copy of the original database, with all of the features of the original schema, '''before''' attempting any steps of the update process manually.&lt;br /&gt;
# Some of the procedures outlined in this article involve manually creating or modifying files. Note that the same filesystem prerequisite for installation, namely that the file ownership must match the user under which PHP executes on the web server, applies to these steps.&lt;br /&gt;
&lt;br /&gt;
Furthermore, '''in regard to uploading files:''' note that any instructions to upload folders into X2Engine, and replace/merge contents, refer to replacing files in X2Engine with files in the archive at analogous paths. For example, if you have a zip file that contains a folder &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, and inside of that is a folder &amp;lt;tt&amp;gt;components&amp;lt;/tt&amp;gt;, and inside of that is a file &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt;, and the instructions are to upload/replace in X2Engine, you should upload &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt; to replace the existing file of the same name in &amp;lt;tt&amp;gt;protected/components&amp;lt;/tt&amp;gt; inside of X2Engine.&lt;br /&gt;
&lt;br /&gt;
X2Engine Inc. cannot be held accountable for damage/errors caused by any missteps or mistakes in manually-performed update operations. If in doubt, make a sandbox/clone of your X2Engine installation by copying the files and database to a different server, and test performing the operations on that before proceeding with your production install of X2Engine.&lt;br /&gt;
&lt;br /&gt;
= Update Notifications =&lt;br /&gt;
[[File:Update Notification.png|200px|frame|right|System flash message displaying software update notification.]]&lt;br /&gt;
To get notified of software updates, unless already using X2Engine Professional Edition, one must first &amp;quot;subscribe&amp;quot; to them. This can be done via checking the box &amp;quot;Notify me of software updates&amp;quot; in the installation form. If X2Engine is already installed, this can be done by going to &amp;quot;Updater Settings&amp;quot; from the Admin page and enabling them. If using Professional Edition, these steps are not not necessary.&lt;br /&gt;
&lt;br /&gt;
Once that has been completed, set the update notification interval as desired in &amp;quot;Updater Settings&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Update Methods =&lt;br /&gt;
There are, in essence, just three basic methods of updating, and variations on each. The three main methods are: &lt;br /&gt;
# web updater,&lt;br /&gt;
# command line updater, and&lt;br /&gt;
# manually.&lt;br /&gt;
In each case, an update package from the update data server is used as the authoritative list of changes to apply, but in the non-manual methods, X2Engine's own updater class does all the work applying changes from said package.&lt;br /&gt;
&lt;br /&gt;
In web and command line updates, there is also the option to first download and unpack the package manually (see [[#Performing_.22Offline.22_Updates|performing &amp;quot;offline&amp;quot; updates]]). This circumvents the need for the local server (on which X2Engine is hosted) to be connected to the internet. Similarly, in the case of manual updates, it is not necessary for the web server to actually have an internet connection.&lt;br /&gt;
&lt;br /&gt;
== Using the Web Updater ==&lt;br /&gt;
[[File:Updater-1.png|300px|thumb|right|The updater utility page.]]&lt;br /&gt;
The web updater can be accessed by any of the following methods:&lt;br /&gt;
* Using the link in the notification message at the top of the window in versions later than 3.5.5&lt;br /&gt;
* By going to the Admin page and clicking the &amp;quot;Update&amp;quot; button at the top, in earlier versions, with update notifications enabled&lt;br /&gt;
* Through &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
First, navigate to the web updater page, and '''do not''' cancel the request (stop or hit your browser's &amp;quot;back&amp;quot; button) until the page has loaded. If the updater utility has undergone changes and needs to self-update to be compatible with the software updates server, this is when it will do so. Thus, it is critical that you do not interrupt this process. If the page does not load, or displays the error message that dependencies could not be retrieved, you may have to [[#Performing_.22Offline.22_Updates|perform an &amp;quot;offline&amp;quot; update]].&lt;br /&gt;
&lt;br /&gt;
There are three ways of getting to the updater page. One is to simply click the link given in the update notification flash message, if available. The other is to click on the &amp;quot;Update&amp;quot; button at the top of the admin page. If software update notifications are not enabled, one can manually check for and install an update by going to &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
Next, make a backup of the database and files (recommended), follow the instructions on the page, and click the &amp;quot;Update&amp;quot; button when ready.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
It is generally quick and is the easiest method to use, and it does not require shell/SSH access to the web server.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
It is not recommended for use on large installations of X2Engine, i.e. containing over a million records, or for updating from very old versions (i.e. a version more than five months old). This is because the final update process may be cut short by server timeout or an interruption in the network connection. This can result in an incomplete/broken update that requires recovering manually (a potentially long process) at best, or actual loss of data at worst. &lt;br /&gt;
&lt;br /&gt;
==== Explanation ====&lt;br /&gt;
The inherent weakness of the web updater (or any web-based updater to any web application) is that it must always rely upon web requests to the server, and it thus must perform extremely critical operations for the update all within the scope of a web request. Web requests, it goes without saying, are not always reliable. For example, one's internet connection might get cut off due being on a wireless network and encountering interference. Furthermore, if any operations take an exceedingly long amount of time, e.g. because of the size of the data set (and thus indexes) on which the update must operate, or the number of database changes that need to be applied, the web request for those operations may take longer than the PHP execution time limit as configured on the webserver. This would potentially cut short the execution of a critical update stage, and thus the update would require manual recovery at best, or completely restoring  X2Engine to a backup at worst.&lt;br /&gt;
&lt;br /&gt;
==== How to Avoid Timeouts ====&lt;br /&gt;
You can usually get around this by setting the value of &amp;lt;tt&amp;gt;'''max_execution_time'''&amp;lt;/tt&amp;gt; to a higher value, i.e. 300 (five minutes), in the web server's PHP configuration. It is generally always a good idea to create a backup copy ''before'' updating, and to download it. It is highly recommended that you either disable the timeout or set it to a very generous value (such as 300 or more seconds) while attempting to update via the web updater, especially if you are many versions behind or have a high volume of data in your CRM.&lt;br /&gt;
&lt;br /&gt;
==== Recovering From Crashes ====&lt;br /&gt;
If you downloaded the backup copy of your database, then after you encounter a failure/timeout, you can use the .sql backup script to restore the database to its original form. However, be aware, that if there are any '''&amp;lt;tt&amp;gt;CREATE TABLE&amp;lt;/tt&amp;gt;''' commands in the update data (expand the ''Database Commands to Run'' list to see), you will need to manually drop them in order to once again make your installation compatible with the update.&lt;br /&gt;
&lt;br /&gt;
== Using the Command Line Updater ==&lt;br /&gt;
This update method uses all of the same thoroughly-tested underpinnings and processes as the web updater, but does so all within a system command to the Yii console.&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
While in via SSH, or in a terminal emulator, or in a script, run the following command in the &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt; subdirectory of X2Engine:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./yiic update app&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the following optional arguments to the command line updater:&lt;br /&gt;
;&amp;lt;tt&amp;gt;--force&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; for this option (i.e. &amp;lt;tt&amp;gt;--force=1&amp;lt;/tt&amp;gt;) to proceed with the update even if compatibility issues were detected. Default: 0 (halt if compatibility issues were detected).&lt;br /&gt;
;&amp;lt;tt&amp;gt;--backup&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that a database backup should be performed first before updating, &amp;quot;0&amp;quot; to proceed without creating a backup first. Default: 1.&lt;br /&gt;
;&amp;lt;tt&amp;gt;--lock&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that the application should be locked during the final installation of the update package to prevent data entry during the sensitive moments when the database is being altered and the files updated, &amp;quot;0&amp;quot; to permit use even when changes to the app are being applied. Default: 0.&lt;br /&gt;
&lt;br /&gt;
If running the command gives you the following message:&lt;br /&gt;
&amp;lt;pre&amp;gt;The updater is now up-to-date and compliant with the updates server. Re-run the command to proceed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
That indicates that the updater first needed to self-update, and succeeded in doing so. You should then run the same command again to complete the update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Safer to use on large, mission-critical systems. The success of a software update (or upgrade) does not hinge upon a single web request completing successfully.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
More difficult to use; requires shell access to the web server. The console command must be run as the same system user that owns all the files and directories of X2Engine, in order to circumvent permission issues when manipulating file system objects.&lt;br /&gt;
&lt;br /&gt;
== Using the Automatic Updater ==&lt;br /&gt;
[[File:Auto-Update.png|400px|thumb|right|Auto-update scheduler on the Updater Settings page]]&lt;br /&gt;
This method uses the command line updater, but runs the command on your behalf using the local cron daemon. To configure, go to the Updater Settings Page, check the box titled &amp;quot;Update Automatically&amp;quot;, and save. If you have Professional Edition, you can manage the update schedule in addition to other automated tasks from the Cron Table settings page, which can also be found under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Automates the entire process of updating.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
Only available on UNIX-like systems that have a cron daemon, and which permit user access to the cron table.&lt;br /&gt;
&lt;br /&gt;
== Performing &amp;quot;Offline&amp;quot; Updates ==&lt;br /&gt;
In some server environments, the updater may not be able to access the updates server by itself, due to (for example) the PHP or network configuration, or because the &amp;quot;zip&amp;quot; PHP extension (requried) is unavailable or has been disabled. It is in these situations necessary to download the update package manually, unpack on the server (or unpack and upload the files to the server), and then run the updater. When performing updates in this way, the updater will not need to access the updates server, but will use the manually-downloaded and extracted package files present in the local filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Refreshing and Updating the Updater Utility ===&lt;br /&gt;
Before starting, note that the updater itself may have undergone changes; the X2Engine update process is subject to change. To address this technical challenge, the updater utility within X2Engine would normally first attempt to download the latest version of itself, in order to be compatible with the updates server. However, this sometimes will not work properly. If your X2Engine installation is running in a hosting environment that prevents it from making outbound HTTP requests (the [[requirements:|requirements check script]] should tell you if this is the case), you will need to do this manually. Also, in version 3.5.5, there was a bug in the updater utility's self-update methods.&lt;br /&gt;
&lt;br /&gt;
To update the updater utility:&lt;br /&gt;
# '''Determine the version of the updater utility package''' by visiting [https://x2planet.com/installs/updates/updateCheck this link]. You should see a page that's entirely blank except for a version number.&lt;br /&gt;
# '''Download the updater package.''' In versions 3.6 and later, a download link for this should be available through the ''Updater Settings'' page. Otherwise, use the following URL (if in Open Source Edition): &amp;lt;pre&amp;gt;https://x2planet.com/installs/updater.zip&amp;lt;/pre&amp;gt; Otherwise, if using Professional Edition, paste the following url, after replacing {key} with your license key, into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/updater-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# '''Extract the downloaded zip archive and upload''' the &amp;quot;protected&amp;quot; folder to your server. Merge folders and overwrite existing files when prompted.&lt;br /&gt;
# '''Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor''' and find the line that looks like this:&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$updaterVersion = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# '''Change the text inside the pair of single quotes''' on that line so that it is the current updater version obtained in step #1. &lt;br /&gt;
# '''Save the file.'''&lt;br /&gt;
&lt;br /&gt;
=== Obtaining the Update Package ===&lt;br /&gt;
First, use the link in the ''Updater Settings'' page entitled &amp;quot;Latest Update Package for Version...&amp;quot;, to download the update package. If the link in the Updater Settings page does not work, use the following URL to download the update package (if using Open Source Edition), replacing &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; with your X2Engine installation's version:&lt;br /&gt;
&amp;lt;pre&amp;gt;https://x2planet.com/installs/updates/{version}/none&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should receive, from that URL, a zip archive file called &amp;quot;update.zip&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If using Professional Edition, and your download link is not working, contact [mailto:customersupport@x2engine.com customer support].&lt;br /&gt;
&lt;br /&gt;
=== Unpacking the Update ===&lt;br /&gt;
[[File:Update-in-Progress.png|200px|frame|right|System flash message displaying notification of pending update.]]&lt;br /&gt;
The update package will be a zip archive. The ''contents'' of this archive must be extracted ''into'' a folder called &amp;quot;update&amp;quot; in the installation directory. So, in other words, after extracting and uploading to the server, there should be a folder called &amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt; alongside &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;framework&amp;lt;/tt&amp;gt; (etc). Within that folder (&amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt;) should be the following contents:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A file containing important data to be used by the updater&lt;br /&gt;
;&amp;lt;tt&amp;gt;contents.md5&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A message digest file containing MD5 checksums of all files in the archive excluding itself.&lt;br /&gt;
;&amp;lt;tt&amp;gt;source&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A sub-folder/directory, whose internal structure mimics that of X2Engine, containing files that were changed in the update. If there were no files changed (i.e. only database changes were in the update), this directory will not be present.&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A directory containing flat data files. These files are typically not used by the updater, but are useful if performing manual updates or recovery.&lt;br /&gt;
&lt;br /&gt;
=== Applying the Update ===&lt;br /&gt;
[[File:Steps-post-Unpack.png|200px|frame|right|Steps of the update process in the event of manually-extracted package (or resuming a preexisting update)]]&lt;br /&gt;
After unpacking, if you run the command line updater, it should (if no problems arise, i.e. incompatible package version) apply the update straight away. Otherwise, log into X2Engine, and you should see a system flash message notifying you of an update in progress. This message shows up whenever there is an update package folder (&amp;quot;update&amp;quot;) present on the server. If you then go to the updater page, once the page has finished loading, you should see that there only remain the steps &amp;quot;Review and confirm changes&amp;quot;, &amp;quot;Apply changes&amp;quot; and &amp;quot;Reload&amp;quot; remain to be completed. To complete the update, when ready, click &amp;quot;Update&amp;quot;; otherwise, to delete the &amp;quot;update&amp;quot; folder and cancel the update, click &amp;quot;Cancel&amp;quot; either on the updater page or in the system flash message.&lt;br /&gt;
&lt;br /&gt;
== Performing Updates Manually ==&lt;br /&gt;
While it is preferable to avoid manually applying changes to X2Engine, and you may never need to, it may in some circumstance or another actually be necessary to do so. For all such situations, the raw update data files generated by the X2Engine update build process are bundled with every update package to help you perform the operations described in [[#Introduction|&amp;quot;Introduction&amp;quot;]]. This section will describe how to perform each of those operations. &lt;br /&gt;
&lt;br /&gt;
=== Prerequisites ===&lt;br /&gt;
First and foremost, all shell commands listed here, and in general, any operations that create or modify files, ''must be run as the same system user under which the server runs PHP scripts.'' Otherwise, the ownership and/or permissions of files and directories will have to be changed after completing all changes.&lt;br /&gt;
&lt;br /&gt;
To obtain the update package (which will be necessary), refer to &amp;quot;[[#Obtaining_the_Update_Package|Obtaining the Update Package]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Furthermore, it is strongly advised that you first make a back-up copy of all files and of X2Engine's database. Your database backup would best be a full, exact copy, such as one that would be made by [http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html mysqldump], or the built-in export tools of [http://www.phpmyadmin.net/home_page/index.php PHPMyAdmin].&lt;br /&gt;
&lt;br /&gt;
The X2Engine global import/export utility (&amp;quot;Import All Data&amp;quot;/&amp;quot;Export All Data&amp;quot;) in Admin '''is not recommended by itself as the sole backup tool'''. This is especially important to note because of how this tool cannot re-create custom fields; they must exist in the target installation before the import. However, using it to ''supplement'' your standard database backup method will prove useful in the event that you:&lt;br /&gt;
# don't have any custom fields,&lt;br /&gt;
# cannot manage to update X2Engine with success, and&lt;br /&gt;
# wish to start over with a fresh installation.&lt;br /&gt;
In such a case, you will then be able to import your old data into the new installation.&lt;br /&gt;
&lt;br /&gt;
=== Introduction: Data Files ===&lt;br /&gt;
In [[#Unpacking_the_Update|Unpacking the update]], a rough overview of an update package's contents were given. In this section, the anatomy and purpose of each of the data files will be introduced.&lt;br /&gt;
&lt;br /&gt;
Included in every update package is two copies of the following data:&lt;br /&gt;
* Files to copy&lt;br /&gt;
* Files to delete&lt;br /&gt;
* Database changes to enact&lt;br /&gt;
* Migration scripts to run, if any&lt;br /&gt;
&lt;br /&gt;
The first, immediately visible place where all of this data is stored is in the file &amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;, as a JSON-encoded array. See the next section for how to use this file.&lt;br /&gt;
&lt;br /&gt;
The second place, which can be useful to users who do not wish to automate or script any step of a manual update, is in the files contained in the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; directory. Each file is named after the version to which it corresponds, and contains the data required to update ''to'' that version ''from the version immediately preceding it.''' Thus, the versions you should see in each file name are the versions above your X2Engine installation's current version, up to and including the version being updated to.&lt;br /&gt;
&lt;br /&gt;
In an update, the directory will contain the following subdirectories:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of files to copy. The format is each line containing a file path in the list.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of obsolete files for that version which will need to be deleted. The format is the same as in &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts corresponding to each version. Each file is formatted such that it can be directly run on the database as an SQL script, although this is not generally recommended. The list of SQL commands to run is a of the following format: &lt;br /&gt;
  [query 1];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [query 2];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [etc]&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts with similar naming and formatting conventions to the &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; scripts. See [[#The_structure_of_the_.22data.22_array|The structure of the &amp;quot;data&amp;quot; array]] for explanation of the purpose of &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== How to use the manifest for updating manually ===&lt;br /&gt;
If you so choose, you may write a script to perform all of the operations for an update in one fell swoop. If so, and if the preferred scripting language of choice has a JSON-parsing library available, you will be able to save yourself a lot of effort by using the main data file (&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;) to load all of the necessary data for the update.&lt;br /&gt;
&lt;br /&gt;
==== The structure the manifest ====&lt;br /&gt;
In updates, the contents of the plain-text file manifest.json are a JSON-encoded object containing the following properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the data corresponding to each intermediate version. It is, in essence, all the data stored in the files in &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt;. If the update is for an installation of X2Engine that is only one version behind, it will contain only one element. Each element of this array is itself an array containing respective data for the intermediate version.&lt;br /&gt;
;&amp;lt;tt&amp;gt;versions&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array listing the version numbers of each intermediate version in the update. Similar to &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, this array will contain only one element if updating to the next version, and that version ''will be the next version''.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of deletions of obsolete files across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of files added or changed across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''from'', or in other words, the version of X2Engine before the package is applied. '''This must be the same as the version of installation to which the package is being applied.''' If this is not so, the updater utility would exit immediately, as applying the changes in the update would have potentially harmful results to data and/or compatibility with future updates.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''to'', or in other words, the version of X2Engine before the package is applied.&lt;br /&gt;
;&amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the version of the updater utility for which the package was compiled. If the structure of the update package or of the manifest changes, the updater must also change to accommodate and recognize the changes; else it might try to access properties that don't exist, or ignore important ones that do. This is the single property of manifest.json that should never change.&lt;br /&gt;
;&amp;lt;tt&amp;gt;scenario&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, which should be &amp;quot;update&amp;quot; if the package is for an update. In the case of upgrades, which use most of the same functionality of the updater, it should be &amp;quot;upgrade&amp;quot;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation after the package is applied. It should be &amp;quot;opensource&amp;quot; for open source edition, &amp;quot;pro&amp;quot; for professional edition. In the case of an upgrade package, it differs from the &amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation before the package is applied. &lt;br /&gt;
;&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An integer denoting the timestamp of the release date of the version of X2Engine denoted by &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The structure of the &amp;quot;data&amp;quot; array ====&lt;br /&gt;
In updates, each version's data, contained in each element of the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, contains the following nested array properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files added for the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files deleted in the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of SQL commands to bring the database structure and contents into compliance with the version from the previous version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A prerequisite list of SQL commands that should be run before those in &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, and if any fail, the failures should be ignored. These are added to updates whenever it is necessary to normalize the database schema, i.e. in the event that some installations' databases may not be consistent with others, but the commands might fail on every other system. These SQL commands were intended to be &amp;quot;attempted&amp;quot;; they are not guaranteed to succeed on all installations. An example of when this is necessary would be adding a foreign key constraint when in previous versions the storage engine was left to the database's default settings in old versions. MyISAM does not natively support foreign key constraints. Thus, to standardize this across all installations, including installations on servers where the storage engine defaulted to MyISAM, the constraint must first be dropped (in a &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; command) before the relevant tables are changed to the InnoDB storage engine, and the constraint is added back again, in the usual &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;version&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The version number&lt;br /&gt;
;&amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The edition&lt;br /&gt;
;&amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of PHP scripts that would be run in an update for this version. These are a subset of fileList and are typically used for advanced database operations, i.e. restructuring values of JSON-encoded fields, which are extremely impractical (or impossible) to perform using raw SQL commands. The files contained in the list all have paths that look like &amp;quot;&amp;lt;tt&amp;gt;protected/migrations/{version}/{timestamp}-name-of-operations.php&amp;lt;/tt&amp;gt;&amp;quot;. Note, if this array is not empty, you may need to reverse-engineer or otherwise copy and re-purpose/re-write any migration scripts listed in order to perform their necessary operations. This is because they may depend on running within a Yii Framework environment, i.e. will contain references to components of the Yii application singleton such as &amp;quot;db&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Note that in upgrades, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; are replaced by &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; (respectively), and &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; are missing (because they aren't necessary).&lt;br /&gt;
&lt;br /&gt;
==== What you will need for the update, if using the manifest ====&lt;br /&gt;
''Among this data, what you will need:'' &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, which will give you the full list of files that need to be deleted, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, which will give you the full list of files to copy from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory, &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;, which give you values with which to update the main X2Engine configuration file, and, within each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, the sub-array properties &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;, with which you will construct the list of database commands to run. &lt;br /&gt;
&lt;br /&gt;
A procedure for manually updating X2Engine, with data from manifest.json, thus might look like this:&lt;br /&gt;
# For each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; (or in other words, each version's changes):&lt;br /&gt;
## Run all the SQL commands in its corresponding &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; sub-array, ignoring errors&lt;br /&gt;
## Run all SQL commands in its &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; array&lt;br /&gt;
## Perform database operations that would be equivalent to those performed in migration scripts&lt;br /&gt;
# For each file in the cumulative list &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, copy it from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory into its analogous location in the installation directory of X2Engine, creating parent directories as necessary if they don't already exist.&lt;br /&gt;
# Delete all files in the cumulative list &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; from the installation directory&lt;br /&gt;
# Clear the data cache files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt; and the temporary asset folders in &amp;lt;tt&amp;gt;assets/&amp;lt;/tt&amp;gt;&lt;br /&gt;
# Clear all session data (&amp;lt;tt&amp;gt;x2_sessions&amp;lt;/tt&amp;gt;) and auth cache data (&amp;lt;tt&amp;gt;x2_auth_cache&amp;lt;/tt&amp;gt;).&lt;br /&gt;
# Update the main X2Engine configuration (&amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt;) file, setting:&lt;br /&gt;
## &amp;lt;tt&amp;gt;$buildDate&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt; property,&lt;br /&gt;
## &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt; property, and &lt;br /&gt;
## &amp;lt;tt&amp;gt;$updaterVersion&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
&lt;br /&gt;
=== Enacting Database Changes ===&lt;br /&gt;
Inside the &amp;lt;tt&amp;gt;update/sqlList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/sqlForce&amp;lt;/tt&amp;gt; folder is a list of &amp;lt;tt&amp;gt;.sql&amp;lt;/tt&amp;gt; files, named according to their corresponding version's update, that can essentially be run as SQL scripts to perform the update. Note, however, that '''if the update traverses more than one version, the scripts must be run in the order of their corresponding version.''' This is because, in the database changes for any version, it is assumed that the database schema and contents conform to the version that immediately preceded it. The PHP function [[phpfunc:version-compare|version_compare]], and the method &amp;quot;[http://pythonhosted.org/setuptools/pkg_resources.html#parsing-utilities parse_version]&amp;quot;, in the pkg_resources module of setuptools, should correctly identify which version should come first. Note, the [[#The_structure_the_manifest|&amp;quot;versions&amp;quot; property of the manifest]] will contain the list of versions in the correct order.&lt;br /&gt;
&lt;br /&gt;
Furthermore, note, &amp;quot;sqlList&amp;quot; and &amp;quot;sqlForce&amp;quot; may not encompass all necessary database changes; if there are any migration scripts in the update, the operations they perform will similarly need to be performed in the current update (see the description of &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; in [[#The_structure_of_the_.22data.22_array|&amp;quot;the structure of the 'data' array&amp;quot;]]).&lt;br /&gt;
&lt;br /&gt;
In summary, for each version traversed in the update, the following operations should be run in order, with &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version number of the version in question:&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlForce/{version}.sql&amp;lt;/tt&amp;gt; file,''' in the order they appear in the file. Ignore if any commands fail. Note, if passed to the MySQL client as a script, any failures might trigger a halt in execution and thus skip the remainder of the commands.&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlList/{version}.sql&amp;lt;/tt&amp;gt; file.'''&lt;br /&gt;
# '''Perform operations equivalent to those in each of the migration scripts, if any.''' The first part of a script file name is the timestamp it was added to the codebase, and this also dictates the order in which they should be run.&lt;br /&gt;
&lt;br /&gt;
=== Copying new files ===&lt;br /&gt;
Files added/changed in the new version are stored in the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory. In an update, they should be copied into the installation directory, replacing any existing files of the same name/path. If the &amp;lt;tt&amp;gt;rsync&amp;lt;/tt&amp;gt; utility is available on the system, you can use one command to safely copy these files into the installation directory as follows (executed in that same directory):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rsync -avc update/source/ ./&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, you will need to use &amp;lt;tt&amp;gt;cat&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;cp&amp;lt;/tt&amp;gt;, two utilities that should be available on every UNIX-like system, and the lists of files added in each intermediate version. These lists are stored in the folder &amp;lt;tt&amp;gt;update/data/fileList&amp;lt;/tt&amp;gt; and named like &amp;quot;{version}.txt&amp;quot; (&amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version to which it applies), each file on its own line in that file. Run the following command, ignoring errors:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/fileList/*; do for l in $(cat $f); do cp update/source/&amp;quot;$l&amp;quot; ./&amp;quot;$l&amp;quot; ; done ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting old files ===&lt;br /&gt;
Files to be deleted are listed in a similar manner as the files to be added: one file per line. The list of files to delete are located at files with paths that look like this: &amp;lt;tt&amp;gt;update/data/deletionList/{version}.txt&amp;lt;/tt&amp;gt;&lt;br /&gt;
So, for an update from 5.5 to 5.7 with intermediate version 5.6 (for instance), the files would be &amp;lt;tt&amp;gt;update/data/deletionList/5.6&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/data/deletionList/5.7&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is only one version through which to update, the operation is very simple, and can be performed in one UNIX/Linux (bash) shell command as follows (if the version is 3.6.3, for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
xargs -a update/data/deletionList/3.6.3.txt -d&amp;quot;\n&amp;quot; rm -fv&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, if there are multiple versions, this may be a more tricky proposition. The necessity arises to create a cumulative list, because there might be the possibility that a file is deleted in one version and re-established in a new version. Nevertheless, while this is possible, it is highly unlikely, and in most cases it is safe to simply iterate over all the &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; data files and delete all the files listed therein. As a bash command:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/deletionList/*; do xargs -a $f -d&amp;quot;\n&amp;quot; rm -fv ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Editing the Configuration File ===&lt;br /&gt;
This final step in the update process, before refreshing, should not be done manually unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a fresh installation at the same version as the one to which you have updated.&lt;br /&gt;
&lt;br /&gt;
To update the configuration manually:&lt;br /&gt;
# Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor&lt;br /&gt;
# Find the line with the &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; variable in it. It should look like this (but with the version from which you are updating): &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$version = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change the value in quotes to the current version being updated to, and save the file.&lt;br /&gt;
&lt;br /&gt;
=== Clearing caches and refreshing the application ===&lt;br /&gt;
It is generally always a good idea to clear the application's server-side cache and other ephemeral files/data after updating. This is taken care of automatically by the built-in updater, but barring proper use and functioning of that, the operations can be performed manually as follows: &lt;br /&gt;
;clear the data cache&lt;br /&gt;
: delete all &amp;quot;&amp;lt;tt&amp;gt;.bin&amp;lt;/tt&amp;gt;&amp;quot; files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -fv protected/runtime/cache/*.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear web assets&lt;br /&gt;
: delete all sub-folders of &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt; by running this command (in the installation directory): &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -r assets/*/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear the &amp;quot;auth cache&amp;quot;&lt;br /&gt;
: Flush the auth cache table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;&lt;br /&gt;
DELETE FROM `x2_auth_cache` WHERE 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;log all users out&lt;br /&gt;
: Flush the sessions table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;DELETE FROM `x2_sessions` WHERE 1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Upgrading =&lt;br /&gt;
&lt;br /&gt;
== Using the Web Interface ==&lt;br /&gt;
[[File:Software-Upgrade-Registration.png|400px|thumb|right|Software upgrade page, in the first step (registration)]]&lt;br /&gt;
To upgrade to Professional Edition&lt;br /&gt;
# Purchase X2Engine Professional Edition Download. Note the registration info in the activation email.&lt;br /&gt;
# Go to the Admin page in X2Engine and find &amp;quot;Upgrade X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot;.&lt;br /&gt;
# In the ''Upgrade'' page, enter your registration info into the form and click &amp;quot;Register&amp;quot; to continue. Once complete, you will see a list of changes similar to those displayed when performing an update.&lt;br /&gt;
# Review the changes to be applied and then proceed by clicking &amp;quot;Upgrade&amp;quot;, similar to performing an update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Offline&amp;quot; Upgrading ==&lt;br /&gt;
This works in exactly the same way as offline updating. However, to obtain your upgrade package, which you will extract into a folder called &amp;quot;update&amp;quot; on the X2Engine web server, contact [mailto:customersupport@x2engine.com customer support]. Furthermore, you will need to run the following database command on your X2Engine database when done with the upgrade, replacing &amp;lt;tt&amp;gt;{product key}&amp;lt;/tt&amp;gt; with your product key, to allow your installation to receive Professional Edition software updates:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{product key}' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Manually Upgrading ==&lt;br /&gt;
The process of upgrading is very similar to that of updating, and uses all of the same underpinnings as the updater. Thus, almost all of the above information and procedures will apply to upgrades. However, note the following crucial differences:&lt;br /&gt;
&lt;br /&gt;
* '''X2Engine must be at the latest available version in order to upgrade.'''&lt;br /&gt;
* '''To obtain the upgrade package, you must be an owner of an X2Engine Professional Edition Download-type license.''' Contact [mailto:customersupport@x2engine.com customer support] for further details.&lt;br /&gt;
* '''There will be only one &amp;quot;intermediate&amp;quot; version.''' The changes to apply will move X2Engine from a version to that same version, applying instead the necessary changes to switch its ''edition'' from Open Source to Professional.&lt;br /&gt;
* '''There are no file deletions.'''&lt;br /&gt;
* '''The properties of the package manifest differ slightly.''' In upgrades, the properties of the data object are &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, etc. Note also, the update data is in aptly-named sub-folders of &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt; as well, and there are no &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; lists to handle.&lt;br /&gt;
* '''The configuration is not changed.''' Instead, the value stored in the field &amp;lt;tt&amp;gt;unique_id&amp;lt;/tt&amp;gt; of the admin record in the database table &amp;lt;tt&amp;gt;x2_admin&amp;lt;/tt&amp;gt; must be changed to the license key, and the &amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt; field must be changed to &amp;quot;pro&amp;quot;. To perform these both, run the following command on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''It is not necessary to clear caches or user sessions when done.''' These operations are performed in updates to eliminate ephemeral (session-ingrained) data that may no longer be applicable and thus cause problems. Upgrades will rarely, if ever, invalidate this data.&lt;br /&gt;
&lt;br /&gt;
= Updating to Beta Versions = &lt;br /&gt;
Beginning at version 4.1.2, after much advocacy to adopt this practice, X2Engine began a two-phase release process in which each version would be released first as a beta, then to the broader public after initial bug finds. The purpose behind this is to avoid updating production installations of X2Engine to new versions that have had zero public exposure, and thus avoid introducing bugs that were not found in initial QA testing (due to a incompatibility with a particular server environment or configuration of X2Engine) into sensitive production environments.&lt;br /&gt;
&lt;br /&gt;
By default, the updater will not update to beta versions. The updater must be configured to use the proper URL on the update server for beta release data, by the following procedure:&lt;br /&gt;
# Create a file &amp;quot;constants-custom.php&amp;quot; in the root level of X2Engine. You can easily do this by renaming &amp;quot;constants-custom.example.php&amp;quot; to that name (remove &amp;quot;.custom&amp;quot;).&lt;br /&gt;
# Look for the following line, and in it, change &amp;quot;false&amp;quot; to &amp;quot;true&amp;quot;: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('X2_UPDATE_BETA') or define('X2_UPDATE_BETA',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The advantage of enabling beta releases is that new features will be available much sooner. Additionally, fixes to bugs will also be available sooner. The main disadvantage of enabling beta releases is that they are never guaranteed to be as stable as non-beta releases. We encourage users to clone their installations of X2Engine, if able, and use beta versions on them, and report any issues they encounter for their particular server environment/special configuration/custom implementation of X2Engine. This way, any issues that arise can be fixed in the stable release. The more users participate in beta testing, the more stable the non-beta releases will become.&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
== User interface (UI) &amp;quot;bugs&amp;quot; after the update ==&lt;br /&gt;
It is extremely common (and easily remedied) that after an important update, certain features of the user interface of X2Engine may not work properly. This is caused, in nearly all cases, by the web browser's cache, which contains out-of-date copies of X2Engine's essential user interface files. To solve these problems, the first step to try is clearing the cache. A step-by-step guide to doing so is given in [[wikihow:Clear-Your-Browser%27s-Cache]].&lt;br /&gt;
&lt;br /&gt;
== Errors in the updater itself == &lt;br /&gt;
When all else fails, the updater may have to be patched due to an irreconcilable backwards compatibility issue. Fortunately, if one has read/write access to the source files on the webserver, this is straightforward to fix. See [[#Refreshing_and_Updating_the_Updater_Utility|Refreshing and Updating the Updater Utility]].&lt;br /&gt;
&lt;br /&gt;
== Professional Edition not in effect after upgrading ==&lt;br /&gt;
This may be caused by Professional Edition not getting &amp;quot;switched on&amp;quot; properly, despite all the necessary filesystem and database changes already being in effect. First, to see if this scenario applies to you, look for a folder &amp;quot;&amp;lt;tt&amp;gt;reports&amp;lt;/tt&amp;gt;&amp;quot; inside &amp;lt;tt&amp;gt;protected/modules&amp;lt;/tt&amp;gt;, and a table &amp;lt;tt&amp;gt;x2_reports&amp;lt;/tt&amp;gt; in the X2Engine database. If you find these things, you can try the following to enable Professional Edition:&lt;br /&gt;
# Find the file &amp;lt;tt&amp;gt;constants.php&amp;lt;/tt&amp;gt; in the installation directory. Open it in a text editor, and look for the following line: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('PRO_VERSION') or define('PRO_VERSION',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; on that line, and save the file.&lt;br /&gt;
# Run the following SQL command (replacing &amp;lt;tt&amp;gt;{license key}&amp;lt;/tt&amp;gt; with your X2Engine license key) on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General Procedures to Try ==&lt;br /&gt;
It may sometimes be possible that the update did not finish cleanly. In such cases, it will be necessary to manually re-perform some of the final operations in the update, described in [[#Introduction|Introduction]]. &lt;br /&gt;
&lt;br /&gt;
=== Refresh the Source Code ===&lt;br /&gt;
This step is not really the best step to try first, as it is usually more time-consuming. It entails re-uploading the entire application, in order to reset all non-ephemeral (source code) files to what they should be in a fresh installation of X2Engine. To do this:&lt;br /&gt;
# Download the refresh package. There should be a download link for this in the ''Updater Settings'' page. Otherwise, if that doesn't work and you're using Open Source edition, use [https://x2planet.com/installs/refresh.zip this link], or if using Professional Edition, paste the following url (after replacing {key} with your license key) into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/refresh-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Upload the ''contents'' of the downloaded zip archive to the installation directory, overwriting existing files.&lt;br /&gt;
&lt;br /&gt;
=== Clear Caches ===&lt;br /&gt;
See &amp;quot;[[#Clearing_caches_and_refreshing_the_application|Clearing Caches...]]&amp;quot; for details.&lt;br /&gt;
&lt;br /&gt;
=== Regenerate the Configuration ===&lt;br /&gt;
Again, this should not be done unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a new installation at the same version as the one you updated to. Refer to [[#Editing_the_Configuration_File|Editing the Configuration File]].&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1847</id>
		<title>Software Updates and Upgrades</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1847"/>
				<updated>2014-06-26T23:32:24Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Using the Web Updater */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
[[File:Update_Process.svg|200px|thumb|right|Top-level overview of the update process]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
The following things happen in typical a software update (or upgrade):&lt;br /&gt;
* Files that are new in the new version are added&lt;br /&gt;
* Files that have been changed in the new version are replaced with the new versions of those files &lt;br /&gt;
* Files that are no longer used in the new version are deleted&lt;br /&gt;
* Changes to the database are made to make the database compatible with the new version of X2Engine&lt;br /&gt;
* The main configuration file, &amp;lt;tt&amp;gt;X2Config.php&amp;lt;/tt&amp;gt;, is edited to reflect the new state of the application.&lt;br /&gt;
* X2Engine data caches are cleared&lt;br /&gt;
&lt;br /&gt;
These operations are typically handled by the updater utility component [[x2doc:UpdaterBehavior|UpdaterBehavior]] and controlled via the in-app web-based updater or the command line updater. This article will cover both how to use said interfaces to the updater, and how to perform updates manually or with the &amp;quot;offline&amp;quot; method if the need arises, in addition to troubleshooting and recovering from an update that did not complete cleanly.&lt;br /&gt;
&lt;br /&gt;
= Disclaimer =&lt;br /&gt;
This article covers the standard and recommended update/upgrade methods, in addition to alternate methods and supplemental procedures. As general rules and precautions for using this article:&lt;br /&gt;
# ''If you do not fully and completely understand any step of any instructions given in this article, '''do not''' follow them''; ask for help instead on [http://x2community.com the forums] or through contacting X2Engine Technical Support if you are an existing customer.&lt;br /&gt;
# You should always make a backup of all of your files ''and'' a full backup copy of your database, preferably in a format (i.e. SQL script) that can be used to create a verbatim/carbon copy of the original database, with all of the features of the original schema, '''before''' attempting any steps of the update process manually.&lt;br /&gt;
# Some of the procedures outlined in this article involve manually creating or modifying files. Note that the same filesystem prerequisite for installation, namely that the file ownership must match the user under which PHP executes on the web server, applies to these steps.&lt;br /&gt;
&lt;br /&gt;
Furthermore, '''in regard to uploading files:''' note that any instructions to upload folders into X2Engine, and replace/merge contents, refer to replacing files in X2Engine with files in the archive at analogous paths. For example, if you have a zip file that contains a folder &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, and inside of that is a folder &amp;lt;tt&amp;gt;components&amp;lt;/tt&amp;gt;, and inside of that is a file &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt;, and the instructions are to upload/replace in X2Engine, you should upload &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt; to replace the existing file of the same name in &amp;lt;tt&amp;gt;protected/components&amp;lt;/tt&amp;gt; inside of X2Engine.&lt;br /&gt;
&lt;br /&gt;
X2Engine Inc. cannot be held accountable for damage/errors caused by any missteps or mistakes in manually-performed update operations. If in doubt, make a sandbox/clone of your X2Engine installation by copying the files and database to a different server, and test performing the operations on that before proceeding with your production install of X2Engine.&lt;br /&gt;
&lt;br /&gt;
= Update Notifications =&lt;br /&gt;
[[File:Update Notification.png|200px|frame|right|System flash message displaying software update notification.]]&lt;br /&gt;
To get notified of software updates, unless already using X2Engine Professional Edition, one must first &amp;quot;subscribe&amp;quot; to them. This can be done via checking the box &amp;quot;Notify me of software updates&amp;quot; in the installation form. If X2Engine is already installed, this can be done by going to &amp;quot;Updater Settings&amp;quot; from the Admin page and enabling them. If using Professional Edition, these steps are not not necessary.&lt;br /&gt;
&lt;br /&gt;
Once that has been completed, set the update notification interval as desired in &amp;quot;Updater Settings&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Update Methods =&lt;br /&gt;
There are, in essence, just three basic methods of updating, and variations on each. The three main methods are: &lt;br /&gt;
# web updater,&lt;br /&gt;
# command line updater, and&lt;br /&gt;
# manually.&lt;br /&gt;
In each case, an update package from the update data server is used as the authoritative list of changes to apply, but in the non-manual methods, X2Engine's own updater class does all the work applying changes from said package.&lt;br /&gt;
&lt;br /&gt;
In web and command line updates, there is also the option to first download and unpack the package manually (see [[#Performing_.22Offline.22_Updates|performing &amp;quot;offline&amp;quot; updates]]). This circumvents the need for the local server (on which X2Engine is hosted) to be connected to the internet. Similarly, in the case of manual updates, it is not necessary for the web server to actually have an internet connection.&lt;br /&gt;
&lt;br /&gt;
== Using the Web Updater ==&lt;br /&gt;
[[File:Updater-1.png|300px|thumb|right|The updater utility page.]]&lt;br /&gt;
The web updater can be accessed by any of the following methods:&lt;br /&gt;
* Using the link in the notification message at the top of the window in versions later than 3.5.5&lt;br /&gt;
* By going to the Admin page and clicking the &amp;quot;Update&amp;quot; button at the top, in earlier versions, with update notifications enabled&lt;br /&gt;
* Through &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
First, navigate to the web updater page, and '''do not''' cancel the request (stop or hit your browser's &amp;quot;back&amp;quot; button) until the page has loaded. If the updater utility has undergone changes and needs to self-update to be compatible with the software updates server, this is when it will do so. Thus, it is critical that you do not interrupt this process. If the page does not load, or displays the error message that dependencies could not be retrieved, you may have to [[#Performing_.22Offline.22_Updates|perform an &amp;quot;offline&amp;quot; update]].&lt;br /&gt;
&lt;br /&gt;
There are three ways of getting to the updater page. One is to simply click the link given in the update notification flash message, if available. The other is to click on the &amp;quot;Update&amp;quot; button at the top of the admin page. If software update notifications are not enabled, one can manually check for and install an update by going to &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
Next, make a backup of the database and files (recommended), follow the instructions on the page, and click the &amp;quot;Update&amp;quot; button when ready.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
It is generally quick and is the easiest method to use, and it does not require shell/SSH access to the web server.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
It is not recommended for use on large installations of X2Engine, i.e. containing over a million records, or for updating from very old versions (i.e. a version more than five months old). This is because the final update process may be cut short by server timeout or an interruption in the network connection. This can result in an incomplete/broken update that requires recovering manually (a potentially long process) at best, or actual loss of data at worst. &lt;br /&gt;
&lt;br /&gt;
==== Recovering From Crashes ====&lt;br /&gt;
If you downloaded the backup copy of your database, then after you encounter a failure/timeout, you can use the .sql backup script to restore the database to its original form. However, be aware, that if there are any '''&amp;lt;tt&amp;gt;CREATE TABLE&amp;lt;/tt&amp;gt;''' commands in the update data (expand the ''Database Commands to Run'' list to see), you will need to manually drop them in order to once again make your installation compatible with the update.&lt;br /&gt;
&lt;br /&gt;
==== Explanation ====&lt;br /&gt;
The inherent weakness of the web updater (or any web-based updater to any web application) is that it must always rely upon web requests to the server, and it thus must perform extremely critical operations for the update all within the scope of a web request. Web requests, it goes without saying, are not always reliable. For example, one's internet connection might get cut off due being on a wireless network and encountering interference. Furthermore, if any operations take an exceedingly long amount of time, e.g. because of the size of the data set (and thus indexes) on which the update must operate, or the number of database changes that need to be applied, the web request for those operations may take longer than the PHP execution time limit as configured on the webserver. This would potentially cut short the execution of a critical update stage, and thus the update would require manual recovery at best, or completely restoring  X2Engine to a backup at worst.&lt;br /&gt;
&lt;br /&gt;
==== How to Avoid Timeouts ====&lt;br /&gt;
You can usually get around this by setting the value of &amp;lt;tt&amp;gt;'''max_execution_time'''&amp;lt;/tt&amp;gt; to a higher value, i.e. 300 (five minutes), in the web server's PHP configuration. It is generally always a good idea to create a backup copy ''before'' updating, and to download it. It is highly recommended that you either disable the timeout or set it to a very generous value (such as 300 or more seconds) while attempting to update via the web updater, especially if you are many versions behind or have a high volume of data in your CRM.&lt;br /&gt;
&lt;br /&gt;
== Using the Command Line Updater ==&lt;br /&gt;
This update method uses all of the same thoroughly-tested underpinnings and processes as the web updater, but does so all within a system command to the Yii console.&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
While in via SSH, or in a terminal emulator, or in a script, run the following command in the &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt; subdirectory of X2Engine:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./yiic update app&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the following optional arguments to the command line updater:&lt;br /&gt;
;&amp;lt;tt&amp;gt;--force&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; for this option (i.e. &amp;lt;tt&amp;gt;--force=1&amp;lt;/tt&amp;gt;) to proceed with the update even if compatibility issues were detected. Default: 0 (halt if compatibility issues were detected).&lt;br /&gt;
;&amp;lt;tt&amp;gt;--backup&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that a database backup should be performed first before updating, &amp;quot;0&amp;quot; to proceed without creating a backup first. Default: 1.&lt;br /&gt;
;&amp;lt;tt&amp;gt;--lock&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that the application should be locked during the final installation of the update package to prevent data entry during the sensitive moments when the database is being altered and the files updated, &amp;quot;0&amp;quot; to permit use even when changes to the app are being applied. Default: 0.&lt;br /&gt;
&lt;br /&gt;
If running the command gives you the following message:&lt;br /&gt;
&amp;lt;pre&amp;gt;The updater is now up-to-date and compliant with the updates server. Re-run the command to proceed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
That indicates that the updater first needed to self-update, and succeeded in doing so. You should then run the same command again to complete the update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Safer to use on large, mission-critical systems. The success of a software update (or upgrade) does not hinge upon a single web request completing successfully.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
More difficult to use; requires shell access to the web server. The console command must be run as the same system user that owns all the files and directories of X2Engine, in order to circumvent permission issues when manipulating file system objects.&lt;br /&gt;
&lt;br /&gt;
== Using the Automatic Updater ==&lt;br /&gt;
[[File:Auto-Update.png|400px|thumb|right|Auto-update scheduler on the Updater Settings page]]&lt;br /&gt;
This method uses the command line updater, but runs the command on your behalf using the local cron daemon. To configure, go to the Updater Settings Page, check the box titled &amp;quot;Update Automatically&amp;quot;, and save. If you have Professional Edition, you can manage the update schedule in addition to other automated tasks from the Cron Table settings page, which can also be found under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Automates the entire process of updating.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
Only available on UNIX-like systems that have a cron daemon, and which permit user access to the cron table.&lt;br /&gt;
&lt;br /&gt;
== Performing &amp;quot;Offline&amp;quot; Updates ==&lt;br /&gt;
In some server environments, the updater may not be able to access the updates server by itself, due to (for example) the PHP or network configuration, or because the &amp;quot;zip&amp;quot; PHP extension (requried) is unavailable or has been disabled. It is in these situations necessary to download the update package manually, unpack on the server (or unpack and upload the files to the server), and then run the updater. When performing updates in this way, the updater will not need to access the updates server, but will use the manually-downloaded and extracted package files present in the local filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Refreshing and Updating the Updater Utility ===&lt;br /&gt;
Before starting, note that the updater itself may have undergone changes; the X2Engine update process is subject to change. To address this technical challenge, the updater utility within X2Engine would normally first attempt to download the latest version of itself, in order to be compatible with the updates server. However, this sometimes will not work properly. If your X2Engine installation is running in a hosting environment that prevents it from making outbound HTTP requests (the [[requirements:|requirements check script]] should tell you if this is the case), you will need to do this manually. Also, in version 3.5.5, there was a bug in the updater utility's self-update methods.&lt;br /&gt;
&lt;br /&gt;
To update the updater utility:&lt;br /&gt;
# '''Determine the version of the updater utility package''' by visiting [https://x2planet.com/installs/updates/updateCheck this link]. You should see a page that's entirely blank except for a version number.&lt;br /&gt;
# '''Download the updater package.''' In versions 3.6 and later, a download link for this should be available through the ''Updater Settings'' page. Otherwise, use the following URL (if in Open Source Edition): &amp;lt;pre&amp;gt;https://x2planet.com/installs/updater.zip&amp;lt;/pre&amp;gt; Otherwise, if using Professional Edition, paste the following url, after replacing {key} with your license key, into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/updater-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# '''Extract the downloaded zip archive and upload''' the &amp;quot;protected&amp;quot; folder to your server. Merge folders and overwrite existing files when prompted.&lt;br /&gt;
# '''Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor''' and find the line that looks like this:&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$updaterVersion = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# '''Change the text inside the pair of single quotes''' on that line so that it is the current updater version obtained in step #1. &lt;br /&gt;
# '''Save the file.'''&lt;br /&gt;
&lt;br /&gt;
=== Obtaining the Update Package ===&lt;br /&gt;
First, use the link in the ''Updater Settings'' page entitled &amp;quot;Latest Update Package for Version...&amp;quot;, to download the update package. If the link in the Updater Settings page does not work, use the following URL to download the update package (if using Open Source Edition), replacing &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; with your X2Engine installation's version:&lt;br /&gt;
&amp;lt;pre&amp;gt;https://x2planet.com/installs/updates/{version}/none&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should receive, from that URL, a zip archive file called &amp;quot;update.zip&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If using Professional Edition, and your download link is not working, contact [mailto:customersupport@x2engine.com customer support].&lt;br /&gt;
&lt;br /&gt;
=== Unpacking the Update ===&lt;br /&gt;
[[File:Update-in-Progress.png|200px|frame|right|System flash message displaying notification of pending update.]]&lt;br /&gt;
The update package will be a zip archive. The ''contents'' of this archive must be extracted ''into'' a folder called &amp;quot;update&amp;quot; in the installation directory. So, in other words, after extracting and uploading to the server, there should be a folder called &amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt; alongside &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;framework&amp;lt;/tt&amp;gt; (etc). Within that folder (&amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt;) should be the following contents:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A file containing important data to be used by the updater&lt;br /&gt;
;&amp;lt;tt&amp;gt;contents.md5&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A message digest file containing MD5 checksums of all files in the archive excluding itself.&lt;br /&gt;
;&amp;lt;tt&amp;gt;source&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A sub-folder/directory, whose internal structure mimics that of X2Engine, containing files that were changed in the update. If there were no files changed (i.e. only database changes were in the update), this directory will not be present.&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A directory containing flat data files. These files are typically not used by the updater, but are useful if performing manual updates or recovery.&lt;br /&gt;
&lt;br /&gt;
=== Applying the Update ===&lt;br /&gt;
[[File:Steps-post-Unpack.png|200px|frame|right|Steps of the update process in the event of manually-extracted package (or resuming a preexisting update)]]&lt;br /&gt;
After unpacking, if you run the command line updater, it should (if no problems arise, i.e. incompatible package version) apply the update straight away. Otherwise, log into X2Engine, and you should see a system flash message notifying you of an update in progress. This message shows up whenever there is an update package folder (&amp;quot;update&amp;quot;) present on the server. If you then go to the updater page, once the page has finished loading, you should see that there only remain the steps &amp;quot;Review and confirm changes&amp;quot;, &amp;quot;Apply changes&amp;quot; and &amp;quot;Reload&amp;quot; remain to be completed. To complete the update, when ready, click &amp;quot;Update&amp;quot;; otherwise, to delete the &amp;quot;update&amp;quot; folder and cancel the update, click &amp;quot;Cancel&amp;quot; either on the updater page or in the system flash message.&lt;br /&gt;
&lt;br /&gt;
== Performing Updates Manually ==&lt;br /&gt;
While it is preferable to avoid manually applying changes to X2Engine, and you may never need to, it may in some circumstance or another actually be necessary to do so. For all such situations, the raw update data files generated by the X2Engine update build process are bundled with every update package to help you perform the operations described in [[#Introduction|&amp;quot;Introduction&amp;quot;]]. This section will describe how to perform each of those operations. &lt;br /&gt;
&lt;br /&gt;
=== Prerequisites ===&lt;br /&gt;
First and foremost, all shell commands listed here, and in general, any operations that create or modify files, ''must be run as the same system user under which the server runs PHP scripts.'' Otherwise, the ownership and/or permissions of files and directories will have to be changed after completing all changes.&lt;br /&gt;
&lt;br /&gt;
To obtain the update package (which will be necessary), refer to &amp;quot;[[#Obtaining_the_Update_Package|Obtaining the Update Package]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Furthermore, it is strongly advised that you first make a back-up copy of all files and of X2Engine's database. Your database backup would best be a full, exact copy, such as one that would be made by [http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html mysqldump], or the built-in export tools of [http://www.phpmyadmin.net/home_page/index.php PHPMyAdmin].&lt;br /&gt;
&lt;br /&gt;
The X2Engine global import/export utility (&amp;quot;Import All Data&amp;quot;/&amp;quot;Export All Data&amp;quot;) in Admin '''is not recommended by itself as the sole backup tool'''. This is especially important to note because of how this tool cannot re-create custom fields; they must exist in the target installation before the import. However, using it to ''supplement'' your standard database backup method will prove useful in the event that you:&lt;br /&gt;
# don't have any custom fields,&lt;br /&gt;
# cannot manage to update X2Engine with success, and&lt;br /&gt;
# wish to start over with a fresh installation.&lt;br /&gt;
In such a case, you will then be able to import your old data into the new installation.&lt;br /&gt;
&lt;br /&gt;
=== Introduction: Data Files ===&lt;br /&gt;
In [[#Unpacking_the_Update|Unpacking the update]], a rough overview of an update package's contents were given. In this section, the anatomy and purpose of each of the data files will be introduced.&lt;br /&gt;
&lt;br /&gt;
Included in every update package is two copies of the following data:&lt;br /&gt;
* Files to copy&lt;br /&gt;
* Files to delete&lt;br /&gt;
* Database changes to enact&lt;br /&gt;
* Migration scripts to run, if any&lt;br /&gt;
&lt;br /&gt;
The first, immediately visible place where all of this data is stored is in the file &amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;, as a JSON-encoded array. See the next section for how to use this file.&lt;br /&gt;
&lt;br /&gt;
The second place, which can be useful to users who do not wish to automate or script any step of a manual update, is in the files contained in the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; directory. Each file is named after the version to which it corresponds, and contains the data required to update ''to'' that version ''from the version immediately preceding it.''' Thus, the versions you should see in each file name are the versions above your X2Engine installation's current version, up to and including the version being updated to.&lt;br /&gt;
&lt;br /&gt;
In an update, the directory will contain the following subdirectories:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of files to copy. The format is each line containing a file path in the list.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of obsolete files for that version which will need to be deleted. The format is the same as in &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts corresponding to each version. Each file is formatted such that it can be directly run on the database as an SQL script, although this is not generally recommended. The list of SQL commands to run is a of the following format: &lt;br /&gt;
  [query 1];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [query 2];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [etc]&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts with similar naming and formatting conventions to the &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; scripts. See [[#The_structure_of_the_.22data.22_array|The structure of the &amp;quot;data&amp;quot; array]] for explanation of the purpose of &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== How to use the manifest for updating manually ===&lt;br /&gt;
If you so choose, you may write a script to perform all of the operations for an update in one fell swoop. If so, and if the preferred scripting language of choice has a JSON-parsing library available, you will be able to save yourself a lot of effort by using the main data file (&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;) to load all of the necessary data for the update.&lt;br /&gt;
&lt;br /&gt;
==== The structure the manifest ====&lt;br /&gt;
In updates, the contents of the plain-text file manifest.json are a JSON-encoded object containing the following properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the data corresponding to each intermediate version. It is, in essence, all the data stored in the files in &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt;. If the update is for an installation of X2Engine that is only one version behind, it will contain only one element. Each element of this array is itself an array containing respective data for the intermediate version.&lt;br /&gt;
;&amp;lt;tt&amp;gt;versions&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array listing the version numbers of each intermediate version in the update. Similar to &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, this array will contain only one element if updating to the next version, and that version ''will be the next version''.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of deletions of obsolete files across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of files added or changed across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''from'', or in other words, the version of X2Engine before the package is applied. '''This must be the same as the version of installation to which the package is being applied.''' If this is not so, the updater utility would exit immediately, as applying the changes in the update would have potentially harmful results to data and/or compatibility with future updates.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''to'', or in other words, the version of X2Engine before the package is applied.&lt;br /&gt;
;&amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the version of the updater utility for which the package was compiled. If the structure of the update package or of the manifest changes, the updater must also change to accommodate and recognize the changes; else it might try to access properties that don't exist, or ignore important ones that do. This is the single property of manifest.json that should never change.&lt;br /&gt;
;&amp;lt;tt&amp;gt;scenario&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, which should be &amp;quot;update&amp;quot; if the package is for an update. In the case of upgrades, which use most of the same functionality of the updater, it should be &amp;quot;upgrade&amp;quot;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation after the package is applied. It should be &amp;quot;opensource&amp;quot; for open source edition, &amp;quot;pro&amp;quot; for professional edition. In the case of an upgrade package, it differs from the &amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation before the package is applied. &lt;br /&gt;
;&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An integer denoting the timestamp of the release date of the version of X2Engine denoted by &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The structure of the &amp;quot;data&amp;quot; array ====&lt;br /&gt;
In updates, each version's data, contained in each element of the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, contains the following nested array properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files added for the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files deleted in the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of SQL commands to bring the database structure and contents into compliance with the version from the previous version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A prerequisite list of SQL commands that should be run before those in &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, and if any fail, the failures should be ignored. These are added to updates whenever it is necessary to normalize the database schema, i.e. in the event that some installations' databases may not be consistent with others, but the commands might fail on every other system. These SQL commands were intended to be &amp;quot;attempted&amp;quot;; they are not guaranteed to succeed on all installations. An example of when this is necessary would be adding a foreign key constraint when in previous versions the storage engine was left to the database's default settings in old versions. MyISAM does not natively support foreign key constraints. Thus, to standardize this across all installations, including installations on servers where the storage engine defaulted to MyISAM, the constraint must first be dropped (in a &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; command) before the relevant tables are changed to the InnoDB storage engine, and the constraint is added back again, in the usual &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;version&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The version number&lt;br /&gt;
;&amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The edition&lt;br /&gt;
;&amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of PHP scripts that would be run in an update for this version. These are a subset of fileList and are typically used for advanced database operations, i.e. restructuring values of JSON-encoded fields, which are extremely impractical (or impossible) to perform using raw SQL commands. The files contained in the list all have paths that look like &amp;quot;&amp;lt;tt&amp;gt;protected/migrations/{version}/{timestamp}-name-of-operations.php&amp;lt;/tt&amp;gt;&amp;quot;. Note, if this array is not empty, you may need to reverse-engineer or otherwise copy and re-purpose/re-write any migration scripts listed in order to perform their necessary operations. This is because they may depend on running within a Yii Framework environment, i.e. will contain references to components of the Yii application singleton such as &amp;quot;db&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Note that in upgrades, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; are replaced by &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; (respectively), and &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; are missing (because they aren't necessary).&lt;br /&gt;
&lt;br /&gt;
==== What you will need for the update, if using the manifest ====&lt;br /&gt;
''Among this data, what you will need:'' &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, which will give you the full list of files that need to be deleted, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, which will give you the full list of files to copy from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory, &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;, which give you values with which to update the main X2Engine configuration file, and, within each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, the sub-array properties &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;, with which you will construct the list of database commands to run. &lt;br /&gt;
&lt;br /&gt;
A procedure for manually updating X2Engine, with data from manifest.json, thus might look like this:&lt;br /&gt;
# For each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; (or in other words, each version's changes):&lt;br /&gt;
## Run all the SQL commands in its corresponding &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; sub-array, ignoring errors&lt;br /&gt;
## Run all SQL commands in its &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; array&lt;br /&gt;
## Perform database operations that would be equivalent to those performed in migration scripts&lt;br /&gt;
# For each file in the cumulative list &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, copy it from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory into its analogous location in the installation directory of X2Engine, creating parent directories as necessary if they don't already exist.&lt;br /&gt;
# Delete all files in the cumulative list &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; from the installation directory&lt;br /&gt;
# Clear the data cache files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt; and the temporary asset folders in &amp;lt;tt&amp;gt;assets/&amp;lt;/tt&amp;gt;&lt;br /&gt;
# Clear all session data (&amp;lt;tt&amp;gt;x2_sessions&amp;lt;/tt&amp;gt;) and auth cache data (&amp;lt;tt&amp;gt;x2_auth_cache&amp;lt;/tt&amp;gt;).&lt;br /&gt;
# Update the main X2Engine configuration (&amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt;) file, setting:&lt;br /&gt;
## &amp;lt;tt&amp;gt;$buildDate&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt; property,&lt;br /&gt;
## &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt; property, and &lt;br /&gt;
## &amp;lt;tt&amp;gt;$updaterVersion&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
&lt;br /&gt;
=== Enacting Database Changes ===&lt;br /&gt;
Inside the &amp;lt;tt&amp;gt;update/sqlList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/sqlForce&amp;lt;/tt&amp;gt; folder is a list of &amp;lt;tt&amp;gt;.sql&amp;lt;/tt&amp;gt; files, named according to their corresponding version's update, that can essentially be run as SQL scripts to perform the update. Note, however, that '''if the update traverses more than one version, the scripts must be run in the order of their corresponding version.''' This is because, in the database changes for any version, it is assumed that the database schema and contents conform to the version that immediately preceded it. The PHP function [[phpfunc:version-compare|version_compare]], and the method &amp;quot;[http://pythonhosted.org/setuptools/pkg_resources.html#parsing-utilities parse_version]&amp;quot;, in the pkg_resources module of setuptools, should correctly identify which version should come first. Note, the [[#The_structure_the_manifest|&amp;quot;versions&amp;quot; property of the manifest]] will contain the list of versions in the correct order.&lt;br /&gt;
&lt;br /&gt;
Furthermore, note, &amp;quot;sqlList&amp;quot; and &amp;quot;sqlForce&amp;quot; may not encompass all necessary database changes; if there are any migration scripts in the update, the operations they perform will similarly need to be performed in the current update (see the description of &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; in [[#The_structure_of_the_.22data.22_array|&amp;quot;the structure of the 'data' array&amp;quot;]]).&lt;br /&gt;
&lt;br /&gt;
In summary, for each version traversed in the update, the following operations should be run in order, with &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version number of the version in question:&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlForce/{version}.sql&amp;lt;/tt&amp;gt; file,''' in the order they appear in the file. Ignore if any commands fail. Note, if passed to the MySQL client as a script, any failures might trigger a halt in execution and thus skip the remainder of the commands.&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlList/{version}.sql&amp;lt;/tt&amp;gt; file.'''&lt;br /&gt;
# '''Perform operations equivalent to those in each of the migration scripts, if any.''' The first part of a script file name is the timestamp it was added to the codebase, and this also dictates the order in which they should be run.&lt;br /&gt;
&lt;br /&gt;
=== Copying new files ===&lt;br /&gt;
Files added/changed in the new version are stored in the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory. In an update, they should be copied into the installation directory, replacing any existing files of the same name/path. If the &amp;lt;tt&amp;gt;rsync&amp;lt;/tt&amp;gt; utility is available on the system, you can use one command to safely copy these files into the installation directory as follows (executed in that same directory):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rsync -avc update/source/ ./&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, you will need to use &amp;lt;tt&amp;gt;cat&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;cp&amp;lt;/tt&amp;gt;, two utilities that should be available on every UNIX-like system, and the lists of files added in each intermediate version. These lists are stored in the folder &amp;lt;tt&amp;gt;update/data/fileList&amp;lt;/tt&amp;gt; and named like &amp;quot;{version}.txt&amp;quot; (&amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version to which it applies), each file on its own line in that file. Run the following command, ignoring errors:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/fileList/*; do for l in $(cat $f); do cp update/source/&amp;quot;$l&amp;quot; ./&amp;quot;$l&amp;quot; ; done ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting old files ===&lt;br /&gt;
Files to be deleted are listed in a similar manner as the files to be added: one file per line. The list of files to delete are located at files with paths that look like this: &amp;lt;tt&amp;gt;update/data/deletionList/{version}.txt&amp;lt;/tt&amp;gt;&lt;br /&gt;
So, for an update from 5.5 to 5.7 with intermediate version 5.6 (for instance), the files would be &amp;lt;tt&amp;gt;update/data/deletionList/5.6&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/data/deletionList/5.7&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is only one version through which to update, the operation is very simple, and can be performed in one UNIX/Linux (bash) shell command as follows (if the version is 3.6.3, for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
xargs -a update/data/deletionList/3.6.3.txt -d&amp;quot;\n&amp;quot; rm -fv&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, if there are multiple versions, this may be a more tricky proposition. The necessity arises to create a cumulative list, because there might be the possibility that a file is deleted in one version and re-established in a new version. Nevertheless, while this is possible, it is highly unlikely, and in most cases it is safe to simply iterate over all the &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; data files and delete all the files listed therein. As a bash command:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/deletionList/*; do xargs -a $f -d&amp;quot;\n&amp;quot; rm -fv ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Editing the Configuration File ===&lt;br /&gt;
This final step in the update process, before refreshing, should not be done manually unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a fresh installation at the same version as the one to which you have updated.&lt;br /&gt;
&lt;br /&gt;
To update the configuration manually:&lt;br /&gt;
# Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor&lt;br /&gt;
# Find the line with the &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; variable in it. It should look like this (but with the version from which you are updating): &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$version = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change the value in quotes to the current version being updated to, and save the file.&lt;br /&gt;
&lt;br /&gt;
=== Clearing caches and refreshing the application ===&lt;br /&gt;
It is generally always a good idea to clear the application's server-side cache and other ephemeral files/data after updating. This is taken care of automatically by the built-in updater, but barring proper use and functioning of that, the operations can be performed manually as follows: &lt;br /&gt;
;clear the data cache&lt;br /&gt;
: delete all &amp;quot;&amp;lt;tt&amp;gt;.bin&amp;lt;/tt&amp;gt;&amp;quot; files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -fv protected/runtime/cache/*.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear web assets&lt;br /&gt;
: delete all sub-folders of &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt; by running this command (in the installation directory): &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -r assets/*/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear the &amp;quot;auth cache&amp;quot;&lt;br /&gt;
: Flush the auth cache table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;&lt;br /&gt;
DELETE FROM `x2_auth_cache` WHERE 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;log all users out&lt;br /&gt;
: Flush the sessions table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;DELETE FROM `x2_sessions` WHERE 1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Upgrading =&lt;br /&gt;
&lt;br /&gt;
== Using the Web Interface ==&lt;br /&gt;
[[File:Software-Upgrade-Registration.png|400px|thumb|right|Software upgrade page, in the first step (registration)]]&lt;br /&gt;
To upgrade to Professional Edition&lt;br /&gt;
# Purchase X2Engine Professional Edition Download. Note the registration info in the activation email.&lt;br /&gt;
# Go to the Admin page in X2Engine and find &amp;quot;Upgrade X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot;.&lt;br /&gt;
# In the ''Upgrade'' page, enter your registration info into the form and click &amp;quot;Register&amp;quot; to continue. Once complete, you will see a list of changes similar to those displayed when performing an update.&lt;br /&gt;
# Review the changes to be applied and then proceed by clicking &amp;quot;Upgrade&amp;quot;, similar to performing an update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Offline&amp;quot; Upgrading ==&lt;br /&gt;
This works in exactly the same way as offline updating. However, to obtain your upgrade package, which you will extract into a folder called &amp;quot;update&amp;quot; on the X2Engine web server, contact [mailto:customersupport@x2engine.com customer support]. Furthermore, you will need to run the following database command on your X2Engine database when done with the upgrade, replacing &amp;lt;tt&amp;gt;{product key}&amp;lt;/tt&amp;gt; with your product key, to allow your installation to receive Professional Edition software updates:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{product key}' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Manually Upgrading ==&lt;br /&gt;
The process of upgrading is very similar to that of updating, and uses all of the same underpinnings as the updater. Thus, almost all of the above information and procedures will apply to upgrades. However, note the following crucial differences:&lt;br /&gt;
&lt;br /&gt;
* '''X2Engine must be at the latest available version in order to upgrade.'''&lt;br /&gt;
* '''To obtain the upgrade package, you must be an owner of an X2Engine Professional Edition Download-type license.''' Contact [mailto:customersupport@x2engine.com customer support] for further details.&lt;br /&gt;
* '''There will be only one &amp;quot;intermediate&amp;quot; version.''' The changes to apply will move X2Engine from a version to that same version, applying instead the necessary changes to switch its ''edition'' from Open Source to Professional.&lt;br /&gt;
* '''There are no file deletions.'''&lt;br /&gt;
* '''The properties of the package manifest differ slightly.''' In upgrades, the properties of the data object are &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, etc. Note also, the update data is in aptly-named sub-folders of &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt; as well, and there are no &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; lists to handle.&lt;br /&gt;
* '''The configuration is not changed.''' Instead, the value stored in the field &amp;lt;tt&amp;gt;unique_id&amp;lt;/tt&amp;gt; of the admin record in the database table &amp;lt;tt&amp;gt;x2_admin&amp;lt;/tt&amp;gt; must be changed to the license key, and the &amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt; field must be changed to &amp;quot;pro&amp;quot;. To perform these both, run the following command on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''It is not necessary to clear caches or user sessions when done.''' These operations are performed in updates to eliminate ephemeral (session-ingrained) data that may no longer be applicable and thus cause problems. Upgrades will rarely, if ever, invalidate this data.&lt;br /&gt;
&lt;br /&gt;
= Updating to Beta Versions = &lt;br /&gt;
Beginning at version 4.1.2, after much advocacy to adopt this practice, X2Engine began a two-phase release process in which each version would be released first as a beta, then to the broader public after initial bug finds. The purpose behind this is to avoid updating production installations of X2Engine to new versions that have had zero public exposure, and thus avoid introducing bugs that were not found in initial QA testing (due to a incompatibility with a particular server environment or configuration of X2Engine) into sensitive production environments.&lt;br /&gt;
&lt;br /&gt;
By default, the updater will not update to beta versions. The updater must be configured to use the proper URL on the update server for beta release data, by the following procedure:&lt;br /&gt;
# Create a file &amp;quot;constants-custom.php&amp;quot; in the root level of X2Engine. You can easily do this by renaming &amp;quot;constants-custom.example.php&amp;quot; to that name (remove &amp;quot;.custom&amp;quot;).&lt;br /&gt;
# Look for the following line, and in it, change &amp;quot;false&amp;quot; to &amp;quot;true&amp;quot;: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('X2_UPDATE_BETA') or define('X2_UPDATE_BETA',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The advantage of enabling beta releases is that new features will be available much sooner. Additionally, fixes to bugs will also be available sooner. The main disadvantage of enabling beta releases is that they are never guaranteed to be as stable as non-beta releases. We encourage users to clone their installations of X2Engine, if able, and use beta versions on them, and report any issues they encounter for their particular server environment/special configuration/custom implementation of X2Engine. This way, any issues that arise can be fixed in the stable release. The more users participate in beta testing, the more stable the non-beta releases will become.&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
== User interface (UI) &amp;quot;bugs&amp;quot; after the update ==&lt;br /&gt;
It is extremely common (and easily remedied) that after an important update, certain features of the user interface of X2Engine may not work properly. This is caused, in nearly all cases, by the web browser's cache, which contains out-of-date copies of X2Engine's essential user interface files. To solve these problems, the first step to try is clearing the cache. A step-by-step guide to doing so is given in [[wikihow:Clear-Your-Browser%27s-Cache]].&lt;br /&gt;
&lt;br /&gt;
== Errors in the updater itself == &lt;br /&gt;
When all else fails, the updater may have to be patched due to an irreconcilable backwards compatibility issue. Fortunately, if one has read/write access to the source files on the webserver, this is straightforward to fix. See [[#Refreshing_and_Updating_the_Updater_Utility|Refreshing and Updating the Updater Utility]].&lt;br /&gt;
&lt;br /&gt;
== Professional Edition not in effect after upgrading ==&lt;br /&gt;
This may be caused by Professional Edition not getting &amp;quot;switched on&amp;quot; properly, despite all the necessary filesystem and database changes already being in effect. First, to see if this scenario applies to you, look for a folder &amp;quot;&amp;lt;tt&amp;gt;reports&amp;lt;/tt&amp;gt;&amp;quot; inside &amp;lt;tt&amp;gt;protected/modules&amp;lt;/tt&amp;gt;, and a table &amp;lt;tt&amp;gt;x2_reports&amp;lt;/tt&amp;gt; in the X2Engine database. If you find these things, you can try the following to enable Professional Edition:&lt;br /&gt;
# Find the file &amp;lt;tt&amp;gt;constants.php&amp;lt;/tt&amp;gt; in the installation directory. Open it in a text editor, and look for the following line: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('PRO_VERSION') or define('PRO_VERSION',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; on that line, and save the file.&lt;br /&gt;
# Run the following SQL command (replacing &amp;lt;tt&amp;gt;{license key}&amp;lt;/tt&amp;gt; with your X2Engine license key) on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General Procedures to Try ==&lt;br /&gt;
It may sometimes be possible that the update did not finish cleanly. In such cases, it will be necessary to manually re-perform some of the final operations in the update, described in [[#Introduction|Introduction]]. &lt;br /&gt;
&lt;br /&gt;
=== Refresh the Source Code ===&lt;br /&gt;
This step is not really the best step to try first, as it is usually more time-consuming. It entails re-uploading the entire application, in order to reset all non-ephemeral (source code) files to what they should be in a fresh installation of X2Engine. To do this:&lt;br /&gt;
# Download the refresh package. There should be a download link for this in the ''Updater Settings'' page. Otherwise, if that doesn't work and you're using Open Source edition, use [https://x2planet.com/installs/refresh.zip this link], or if using Professional Edition, paste the following url (after replacing {key} with your license key) into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/refresh-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Upload the ''contents'' of the downloaded zip archive to the installation directory, overwriting existing files.&lt;br /&gt;
&lt;br /&gt;
=== Clear Caches ===&lt;br /&gt;
See &amp;quot;[[#Clearing_caches_and_refreshing_the_application|Clearing Caches...]]&amp;quot; for details.&lt;br /&gt;
&lt;br /&gt;
=== Regenerate the Configuration ===&lt;br /&gt;
Again, this should not be done unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a new installation at the same version as the one you updated to. Refer to [[#Editing_the_Configuration_File|Editing the Configuration File]].&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1846</id>
		<title>Software Updates and Upgrades</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1846"/>
				<updated>2014-06-26T23:31:27Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Using the Web Updater */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
[[File:Update_Process.svg|200px|thumb|right|Top-level overview of the update process]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
The following things happen in typical a software update (or upgrade):&lt;br /&gt;
* Files that are new in the new version are added&lt;br /&gt;
* Files that have been changed in the new version are replaced with the new versions of those files &lt;br /&gt;
* Files that are no longer used in the new version are deleted&lt;br /&gt;
* Changes to the database are made to make the database compatible with the new version of X2Engine&lt;br /&gt;
* The main configuration file, &amp;lt;tt&amp;gt;X2Config.php&amp;lt;/tt&amp;gt;, is edited to reflect the new state of the application.&lt;br /&gt;
* X2Engine data caches are cleared&lt;br /&gt;
&lt;br /&gt;
These operations are typically handled by the updater utility component [[x2doc:UpdaterBehavior|UpdaterBehavior]] and controlled via the in-app web-based updater or the command line updater. This article will cover both how to use said interfaces to the updater, and how to perform updates manually or with the &amp;quot;offline&amp;quot; method if the need arises, in addition to troubleshooting and recovering from an update that did not complete cleanly.&lt;br /&gt;
&lt;br /&gt;
= Disclaimer =&lt;br /&gt;
This article covers the standard and recommended update/upgrade methods, in addition to alternate methods and supplemental procedures. As general rules and precautions for using this article:&lt;br /&gt;
# ''If you do not fully and completely understand any step of any instructions given in this article, '''do not''' follow them''; ask for help instead on [http://x2community.com the forums] or through contacting X2Engine Technical Support if you are an existing customer.&lt;br /&gt;
# You should always make a backup of all of your files ''and'' a full backup copy of your database, preferably in a format (i.e. SQL script) that can be used to create a verbatim/carbon copy of the original database, with all of the features of the original schema, '''before''' attempting any steps of the update process manually.&lt;br /&gt;
# Some of the procedures outlined in this article involve manually creating or modifying files. Note that the same filesystem prerequisite for installation, namely that the file ownership must match the user under which PHP executes on the web server, applies to these steps.&lt;br /&gt;
&lt;br /&gt;
Furthermore, '''in regard to uploading files:''' note that any instructions to upload folders into X2Engine, and replace/merge contents, refer to replacing files in X2Engine with files in the archive at analogous paths. For example, if you have a zip file that contains a folder &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, and inside of that is a folder &amp;lt;tt&amp;gt;components&amp;lt;/tt&amp;gt;, and inside of that is a file &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt;, and the instructions are to upload/replace in X2Engine, you should upload &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt; to replace the existing file of the same name in &amp;lt;tt&amp;gt;protected/components&amp;lt;/tt&amp;gt; inside of X2Engine.&lt;br /&gt;
&lt;br /&gt;
X2Engine Inc. cannot be held accountable for damage/errors caused by any missteps or mistakes in manually-performed update operations. If in doubt, make a sandbox/clone of your X2Engine installation by copying the files and database to a different server, and test performing the operations on that before proceeding with your production install of X2Engine.&lt;br /&gt;
&lt;br /&gt;
= Update Notifications =&lt;br /&gt;
[[File:Update Notification.png|200px|frame|right|System flash message displaying software update notification.]]&lt;br /&gt;
To get notified of software updates, unless already using X2Engine Professional Edition, one must first &amp;quot;subscribe&amp;quot; to them. This can be done via checking the box &amp;quot;Notify me of software updates&amp;quot; in the installation form. If X2Engine is already installed, this can be done by going to &amp;quot;Updater Settings&amp;quot; from the Admin page and enabling them. If using Professional Edition, these steps are not not necessary.&lt;br /&gt;
&lt;br /&gt;
Once that has been completed, set the update notification interval as desired in &amp;quot;Updater Settings&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Update Methods =&lt;br /&gt;
There are, in essence, just three basic methods of updating, and variations on each. The three main methods are: &lt;br /&gt;
# web updater,&lt;br /&gt;
# command line updater, and&lt;br /&gt;
# manually.&lt;br /&gt;
In each case, an update package from the update data server is used as the authoritative list of changes to apply, but in the non-manual methods, X2Engine's own updater class does all the work applying changes from said package.&lt;br /&gt;
&lt;br /&gt;
In web and command line updates, there is also the option to first download and unpack the package manually (see [[#Performing_.22Offline.22_Updates|performing &amp;quot;offline&amp;quot; updates]]). This circumvents the need for the local server (on which X2Engine is hosted) to be connected to the internet. Similarly, in the case of manual updates, it is not necessary for the web server to actually have an internet connection.&lt;br /&gt;
&lt;br /&gt;
== Using the Web Updater ==&lt;br /&gt;
[[File:Updater-1.png|300px|thumb|right|The updater utility page.]]&lt;br /&gt;
The web updater can be accessed by any of the following methods:&lt;br /&gt;
* Using the link in the notification message at the top of the window in versions later than 3.5.5&lt;br /&gt;
* By going to the Admin page and clicking the &amp;quot;Update&amp;quot; button at the top, in earlier versions, with update notifications enabled&lt;br /&gt;
* Through &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
First, navigate to the web updater page, and '''do not''' cancel the request (stop or hit your browser's &amp;quot;back&amp;quot; button) until the page has loaded. If the updater utility has undergone changes and needs to self-update to be compatible with the software updates server, this is when it will do so. Thus, it is critical that you do not interrupt this process. If the page does not load, or displays the error message that dependencies could not be retrieved, you may have to [[#Performing_.22Offline.22_Updates|perform an &amp;quot;offline&amp;quot; update]].&lt;br /&gt;
&lt;br /&gt;
There are three ways of getting to the updater page. One is to simply click the link given in the update notification flash message, if available. The other is to click on the &amp;quot;Update&amp;quot; button at the top of the admin page. If software update notifications are not enabled, one can manually check for and install an update by going to &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
Next, make a backup of the database and files (recommended), follow the instructions on the page, and click the &amp;quot;Update&amp;quot; button when ready.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
It is generally quick and is the easiest method to use, and it does not require shell/SSH access to the web server.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
It is not recommended for use on large installations of X2Engine, i.e. containing over a million records, or for updating from very old versions (i.e. a version more than five months old). This is because the final update process may be cut short by server timeout or an interruption in the network connection. This can result in an incomplete/broken update that requires recovering manually (a potentially long process) at best, or actual loss of data at worst. &lt;br /&gt;
&lt;br /&gt;
You can usually get around this by setting the value of &amp;lt;tt&amp;gt;'''max_execution_time'''&amp;lt;/tt&amp;gt; to a higher value, i.e. 300 (five minutes), in the web server's PHP configuration. It is generally always a good idea to create a backup copy ''before'' updating, and to download it.&lt;br /&gt;
&lt;br /&gt;
==== Recovering From Crashes ====&lt;br /&gt;
If you downloaded the backup copy of your database, then after you encounter a failure/timeout, you can use the .sql backup script to restore the database to its original form. However, be aware, that if there are any '''&amp;lt;tt&amp;gt;CREATE TABLE&amp;lt;/tt&amp;gt;''' commands in the update data (expand the ''Database Commands to Run'' list to see), you will need to manually drop them in order to once again make your installation compatible with the update.&lt;br /&gt;
==== Explanation ====&lt;br /&gt;
The inherent weakness of the web updater (or any web-based updater to any web application) is that it must always rely upon web requests to the server, and it thus must perform extremely critical operations for the update all within the scope of a web request. Web requests, it goes without saying, are not always reliable. For example, one's internet connection might get cut off due being on a wireless network and encountering interference. Furthermore, if any operations take an exceedingly long amount of time, e.g. because of the size of the data set (and thus indexes) on which the update must operate, or the number of database changes that need to be applied, the web request for those operations may take longer than the PHP execution time limit as configured on the webserver. This would potentially cut short the execution of a critical update stage, and thus the update would require manual recovery at best, or completely restoring  X2Engine to a backup at worst.&lt;br /&gt;
&lt;br /&gt;
==== Ways to get around this ====&lt;br /&gt;
In most cases, including on shared hosting, it should be possible to change the time limit on PHP. It is highly recommended that you either disable the timeout or set it to a very generous value (such as 300 seconds) while attempting to update via the web updater, especially if you are many versions behind or have a high volume of data in your CRM.&lt;br /&gt;
&lt;br /&gt;
== Using the Command Line Updater ==&lt;br /&gt;
This update method uses all of the same thoroughly-tested underpinnings and processes as the web updater, but does so all within a system command to the Yii console.&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
While in via SSH, or in a terminal emulator, or in a script, run the following command in the &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt; subdirectory of X2Engine:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./yiic update app&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the following optional arguments to the command line updater:&lt;br /&gt;
;&amp;lt;tt&amp;gt;--force&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; for this option (i.e. &amp;lt;tt&amp;gt;--force=1&amp;lt;/tt&amp;gt;) to proceed with the update even if compatibility issues were detected. Default: 0 (halt if compatibility issues were detected).&lt;br /&gt;
;&amp;lt;tt&amp;gt;--backup&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that a database backup should be performed first before updating, &amp;quot;0&amp;quot; to proceed without creating a backup first. Default: 1.&lt;br /&gt;
;&amp;lt;tt&amp;gt;--lock&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that the application should be locked during the final installation of the update package to prevent data entry during the sensitive moments when the database is being altered and the files updated, &amp;quot;0&amp;quot; to permit use even when changes to the app are being applied. Default: 0.&lt;br /&gt;
&lt;br /&gt;
If running the command gives you the following message:&lt;br /&gt;
&amp;lt;pre&amp;gt;The updater is now up-to-date and compliant with the updates server. Re-run the command to proceed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
That indicates that the updater first needed to self-update, and succeeded in doing so. You should then run the same command again to complete the update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Safer to use on large, mission-critical systems. The success of a software update (or upgrade) does not hinge upon a single web request completing successfully.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
More difficult to use; requires shell access to the web server. The console command must be run as the same system user that owns all the files and directories of X2Engine, in order to circumvent permission issues when manipulating file system objects.&lt;br /&gt;
&lt;br /&gt;
== Using the Automatic Updater ==&lt;br /&gt;
[[File:Auto-Update.png|400px|thumb|right|Auto-update scheduler on the Updater Settings page]]&lt;br /&gt;
This method uses the command line updater, but runs the command on your behalf using the local cron daemon. To configure, go to the Updater Settings Page, check the box titled &amp;quot;Update Automatically&amp;quot;, and save. If you have Professional Edition, you can manage the update schedule in addition to other automated tasks from the Cron Table settings page, which can also be found under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Automates the entire process of updating.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
Only available on UNIX-like systems that have a cron daemon, and which permit user access to the cron table.&lt;br /&gt;
&lt;br /&gt;
== Performing &amp;quot;Offline&amp;quot; Updates ==&lt;br /&gt;
In some server environments, the updater may not be able to access the updates server by itself, due to (for example) the PHP or network configuration, or because the &amp;quot;zip&amp;quot; PHP extension (requried) is unavailable or has been disabled. It is in these situations necessary to download the update package manually, unpack on the server (or unpack and upload the files to the server), and then run the updater. When performing updates in this way, the updater will not need to access the updates server, but will use the manually-downloaded and extracted package files present in the local filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Refreshing and Updating the Updater Utility ===&lt;br /&gt;
Before starting, note that the updater itself may have undergone changes; the X2Engine update process is subject to change. To address this technical challenge, the updater utility within X2Engine would normally first attempt to download the latest version of itself, in order to be compatible with the updates server. However, this sometimes will not work properly. If your X2Engine installation is running in a hosting environment that prevents it from making outbound HTTP requests (the [[requirements:|requirements check script]] should tell you if this is the case), you will need to do this manually. Also, in version 3.5.5, there was a bug in the updater utility's self-update methods.&lt;br /&gt;
&lt;br /&gt;
To update the updater utility:&lt;br /&gt;
# '''Determine the version of the updater utility package''' by visiting [https://x2planet.com/installs/updates/updateCheck this link]. You should see a page that's entirely blank except for a version number.&lt;br /&gt;
# '''Download the updater package.''' In versions 3.6 and later, a download link for this should be available through the ''Updater Settings'' page. Otherwise, use the following URL (if in Open Source Edition): &amp;lt;pre&amp;gt;https://x2planet.com/installs/updater.zip&amp;lt;/pre&amp;gt; Otherwise, if using Professional Edition, paste the following url, after replacing {key} with your license key, into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/updater-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# '''Extract the downloaded zip archive and upload''' the &amp;quot;protected&amp;quot; folder to your server. Merge folders and overwrite existing files when prompted.&lt;br /&gt;
# '''Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor''' and find the line that looks like this:&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$updaterVersion = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# '''Change the text inside the pair of single quotes''' on that line so that it is the current updater version obtained in step #1. &lt;br /&gt;
# '''Save the file.'''&lt;br /&gt;
&lt;br /&gt;
=== Obtaining the Update Package ===&lt;br /&gt;
First, use the link in the ''Updater Settings'' page entitled &amp;quot;Latest Update Package for Version...&amp;quot;, to download the update package. If the link in the Updater Settings page does not work, use the following URL to download the update package (if using Open Source Edition), replacing &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; with your X2Engine installation's version:&lt;br /&gt;
&amp;lt;pre&amp;gt;https://x2planet.com/installs/updates/{version}/none&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should receive, from that URL, a zip archive file called &amp;quot;update.zip&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If using Professional Edition, and your download link is not working, contact [mailto:customersupport@x2engine.com customer support].&lt;br /&gt;
&lt;br /&gt;
=== Unpacking the Update ===&lt;br /&gt;
[[File:Update-in-Progress.png|200px|frame|right|System flash message displaying notification of pending update.]]&lt;br /&gt;
The update package will be a zip archive. The ''contents'' of this archive must be extracted ''into'' a folder called &amp;quot;update&amp;quot; in the installation directory. So, in other words, after extracting and uploading to the server, there should be a folder called &amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt; alongside &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;framework&amp;lt;/tt&amp;gt; (etc). Within that folder (&amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt;) should be the following contents:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A file containing important data to be used by the updater&lt;br /&gt;
;&amp;lt;tt&amp;gt;contents.md5&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A message digest file containing MD5 checksums of all files in the archive excluding itself.&lt;br /&gt;
;&amp;lt;tt&amp;gt;source&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A sub-folder/directory, whose internal structure mimics that of X2Engine, containing files that were changed in the update. If there were no files changed (i.e. only database changes were in the update), this directory will not be present.&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A directory containing flat data files. These files are typically not used by the updater, but are useful if performing manual updates or recovery.&lt;br /&gt;
&lt;br /&gt;
=== Applying the Update ===&lt;br /&gt;
[[File:Steps-post-Unpack.png|200px|frame|right|Steps of the update process in the event of manually-extracted package (or resuming a preexisting update)]]&lt;br /&gt;
After unpacking, if you run the command line updater, it should (if no problems arise, i.e. incompatible package version) apply the update straight away. Otherwise, log into X2Engine, and you should see a system flash message notifying you of an update in progress. This message shows up whenever there is an update package folder (&amp;quot;update&amp;quot;) present on the server. If you then go to the updater page, once the page has finished loading, you should see that there only remain the steps &amp;quot;Review and confirm changes&amp;quot;, &amp;quot;Apply changes&amp;quot; and &amp;quot;Reload&amp;quot; remain to be completed. To complete the update, when ready, click &amp;quot;Update&amp;quot;; otherwise, to delete the &amp;quot;update&amp;quot; folder and cancel the update, click &amp;quot;Cancel&amp;quot; either on the updater page or in the system flash message.&lt;br /&gt;
&lt;br /&gt;
== Performing Updates Manually ==&lt;br /&gt;
While it is preferable to avoid manually applying changes to X2Engine, and you may never need to, it may in some circumstance or another actually be necessary to do so. For all such situations, the raw update data files generated by the X2Engine update build process are bundled with every update package to help you perform the operations described in [[#Introduction|&amp;quot;Introduction&amp;quot;]]. This section will describe how to perform each of those operations. &lt;br /&gt;
&lt;br /&gt;
=== Prerequisites ===&lt;br /&gt;
First and foremost, all shell commands listed here, and in general, any operations that create or modify files, ''must be run as the same system user under which the server runs PHP scripts.'' Otherwise, the ownership and/or permissions of files and directories will have to be changed after completing all changes.&lt;br /&gt;
&lt;br /&gt;
To obtain the update package (which will be necessary), refer to &amp;quot;[[#Obtaining_the_Update_Package|Obtaining the Update Package]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Furthermore, it is strongly advised that you first make a back-up copy of all files and of X2Engine's database. Your database backup would best be a full, exact copy, such as one that would be made by [http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html mysqldump], or the built-in export tools of [http://www.phpmyadmin.net/home_page/index.php PHPMyAdmin].&lt;br /&gt;
&lt;br /&gt;
The X2Engine global import/export utility (&amp;quot;Import All Data&amp;quot;/&amp;quot;Export All Data&amp;quot;) in Admin '''is not recommended by itself as the sole backup tool'''. This is especially important to note because of how this tool cannot re-create custom fields; they must exist in the target installation before the import. However, using it to ''supplement'' your standard database backup method will prove useful in the event that you:&lt;br /&gt;
# don't have any custom fields,&lt;br /&gt;
# cannot manage to update X2Engine with success, and&lt;br /&gt;
# wish to start over with a fresh installation.&lt;br /&gt;
In such a case, you will then be able to import your old data into the new installation.&lt;br /&gt;
&lt;br /&gt;
=== Introduction: Data Files ===&lt;br /&gt;
In [[#Unpacking_the_Update|Unpacking the update]], a rough overview of an update package's contents were given. In this section, the anatomy and purpose of each of the data files will be introduced.&lt;br /&gt;
&lt;br /&gt;
Included in every update package is two copies of the following data:&lt;br /&gt;
* Files to copy&lt;br /&gt;
* Files to delete&lt;br /&gt;
* Database changes to enact&lt;br /&gt;
* Migration scripts to run, if any&lt;br /&gt;
&lt;br /&gt;
The first, immediately visible place where all of this data is stored is in the file &amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;, as a JSON-encoded array. See the next section for how to use this file.&lt;br /&gt;
&lt;br /&gt;
The second place, which can be useful to users who do not wish to automate or script any step of a manual update, is in the files contained in the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; directory. Each file is named after the version to which it corresponds, and contains the data required to update ''to'' that version ''from the version immediately preceding it.''' Thus, the versions you should see in each file name are the versions above your X2Engine installation's current version, up to and including the version being updated to.&lt;br /&gt;
&lt;br /&gt;
In an update, the directory will contain the following subdirectories:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of files to copy. The format is each line containing a file path in the list.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of obsolete files for that version which will need to be deleted. The format is the same as in &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts corresponding to each version. Each file is formatted such that it can be directly run on the database as an SQL script, although this is not generally recommended. The list of SQL commands to run is a of the following format: &lt;br /&gt;
  [query 1];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [query 2];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [etc]&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts with similar naming and formatting conventions to the &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; scripts. See [[#The_structure_of_the_.22data.22_array|The structure of the &amp;quot;data&amp;quot; array]] for explanation of the purpose of &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== How to use the manifest for updating manually ===&lt;br /&gt;
If you so choose, you may write a script to perform all of the operations for an update in one fell swoop. If so, and if the preferred scripting language of choice has a JSON-parsing library available, you will be able to save yourself a lot of effort by using the main data file (&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;) to load all of the necessary data for the update.&lt;br /&gt;
&lt;br /&gt;
==== The structure the manifest ====&lt;br /&gt;
In updates, the contents of the plain-text file manifest.json are a JSON-encoded object containing the following properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the data corresponding to each intermediate version. It is, in essence, all the data stored in the files in &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt;. If the update is for an installation of X2Engine that is only one version behind, it will contain only one element. Each element of this array is itself an array containing respective data for the intermediate version.&lt;br /&gt;
;&amp;lt;tt&amp;gt;versions&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array listing the version numbers of each intermediate version in the update. Similar to &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, this array will contain only one element if updating to the next version, and that version ''will be the next version''.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of deletions of obsolete files across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of files added or changed across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''from'', or in other words, the version of X2Engine before the package is applied. '''This must be the same as the version of installation to which the package is being applied.''' If this is not so, the updater utility would exit immediately, as applying the changes in the update would have potentially harmful results to data and/or compatibility with future updates.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''to'', or in other words, the version of X2Engine before the package is applied.&lt;br /&gt;
;&amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the version of the updater utility for which the package was compiled. If the structure of the update package or of the manifest changes, the updater must also change to accommodate and recognize the changes; else it might try to access properties that don't exist, or ignore important ones that do. This is the single property of manifest.json that should never change.&lt;br /&gt;
;&amp;lt;tt&amp;gt;scenario&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, which should be &amp;quot;update&amp;quot; if the package is for an update. In the case of upgrades, which use most of the same functionality of the updater, it should be &amp;quot;upgrade&amp;quot;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation after the package is applied. It should be &amp;quot;opensource&amp;quot; for open source edition, &amp;quot;pro&amp;quot; for professional edition. In the case of an upgrade package, it differs from the &amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation before the package is applied. &lt;br /&gt;
;&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An integer denoting the timestamp of the release date of the version of X2Engine denoted by &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The structure of the &amp;quot;data&amp;quot; array ====&lt;br /&gt;
In updates, each version's data, contained in each element of the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, contains the following nested array properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files added for the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files deleted in the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of SQL commands to bring the database structure and contents into compliance with the version from the previous version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A prerequisite list of SQL commands that should be run before those in &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, and if any fail, the failures should be ignored. These are added to updates whenever it is necessary to normalize the database schema, i.e. in the event that some installations' databases may not be consistent with others, but the commands might fail on every other system. These SQL commands were intended to be &amp;quot;attempted&amp;quot;; they are not guaranteed to succeed on all installations. An example of when this is necessary would be adding a foreign key constraint when in previous versions the storage engine was left to the database's default settings in old versions. MyISAM does not natively support foreign key constraints. Thus, to standardize this across all installations, including installations on servers where the storage engine defaulted to MyISAM, the constraint must first be dropped (in a &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; command) before the relevant tables are changed to the InnoDB storage engine, and the constraint is added back again, in the usual &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;version&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The version number&lt;br /&gt;
;&amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The edition&lt;br /&gt;
;&amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of PHP scripts that would be run in an update for this version. These are a subset of fileList and are typically used for advanced database operations, i.e. restructuring values of JSON-encoded fields, which are extremely impractical (or impossible) to perform using raw SQL commands. The files contained in the list all have paths that look like &amp;quot;&amp;lt;tt&amp;gt;protected/migrations/{version}/{timestamp}-name-of-operations.php&amp;lt;/tt&amp;gt;&amp;quot;. Note, if this array is not empty, you may need to reverse-engineer or otherwise copy and re-purpose/re-write any migration scripts listed in order to perform their necessary operations. This is because they may depend on running within a Yii Framework environment, i.e. will contain references to components of the Yii application singleton such as &amp;quot;db&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Note that in upgrades, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; are replaced by &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; (respectively), and &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; are missing (because they aren't necessary).&lt;br /&gt;
&lt;br /&gt;
==== What you will need for the update, if using the manifest ====&lt;br /&gt;
''Among this data, what you will need:'' &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, which will give you the full list of files that need to be deleted, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, which will give you the full list of files to copy from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory, &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;, which give you values with which to update the main X2Engine configuration file, and, within each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, the sub-array properties &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;, with which you will construct the list of database commands to run. &lt;br /&gt;
&lt;br /&gt;
A procedure for manually updating X2Engine, with data from manifest.json, thus might look like this:&lt;br /&gt;
# For each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; (or in other words, each version's changes):&lt;br /&gt;
## Run all the SQL commands in its corresponding &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; sub-array, ignoring errors&lt;br /&gt;
## Run all SQL commands in its &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; array&lt;br /&gt;
## Perform database operations that would be equivalent to those performed in migration scripts&lt;br /&gt;
# For each file in the cumulative list &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, copy it from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory into its analogous location in the installation directory of X2Engine, creating parent directories as necessary if they don't already exist.&lt;br /&gt;
# Delete all files in the cumulative list &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; from the installation directory&lt;br /&gt;
# Clear the data cache files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt; and the temporary asset folders in &amp;lt;tt&amp;gt;assets/&amp;lt;/tt&amp;gt;&lt;br /&gt;
# Clear all session data (&amp;lt;tt&amp;gt;x2_sessions&amp;lt;/tt&amp;gt;) and auth cache data (&amp;lt;tt&amp;gt;x2_auth_cache&amp;lt;/tt&amp;gt;).&lt;br /&gt;
# Update the main X2Engine configuration (&amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt;) file, setting:&lt;br /&gt;
## &amp;lt;tt&amp;gt;$buildDate&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt; property,&lt;br /&gt;
## &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt; property, and &lt;br /&gt;
## &amp;lt;tt&amp;gt;$updaterVersion&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
&lt;br /&gt;
=== Enacting Database Changes ===&lt;br /&gt;
Inside the &amp;lt;tt&amp;gt;update/sqlList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/sqlForce&amp;lt;/tt&amp;gt; folder is a list of &amp;lt;tt&amp;gt;.sql&amp;lt;/tt&amp;gt; files, named according to their corresponding version's update, that can essentially be run as SQL scripts to perform the update. Note, however, that '''if the update traverses more than one version, the scripts must be run in the order of their corresponding version.''' This is because, in the database changes for any version, it is assumed that the database schema and contents conform to the version that immediately preceded it. The PHP function [[phpfunc:version-compare|version_compare]], and the method &amp;quot;[http://pythonhosted.org/setuptools/pkg_resources.html#parsing-utilities parse_version]&amp;quot;, in the pkg_resources module of setuptools, should correctly identify which version should come first. Note, the [[#The_structure_the_manifest|&amp;quot;versions&amp;quot; property of the manifest]] will contain the list of versions in the correct order.&lt;br /&gt;
&lt;br /&gt;
Furthermore, note, &amp;quot;sqlList&amp;quot; and &amp;quot;sqlForce&amp;quot; may not encompass all necessary database changes; if there are any migration scripts in the update, the operations they perform will similarly need to be performed in the current update (see the description of &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; in [[#The_structure_of_the_.22data.22_array|&amp;quot;the structure of the 'data' array&amp;quot;]]).&lt;br /&gt;
&lt;br /&gt;
In summary, for each version traversed in the update, the following operations should be run in order, with &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version number of the version in question:&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlForce/{version}.sql&amp;lt;/tt&amp;gt; file,''' in the order they appear in the file. Ignore if any commands fail. Note, if passed to the MySQL client as a script, any failures might trigger a halt in execution and thus skip the remainder of the commands.&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlList/{version}.sql&amp;lt;/tt&amp;gt; file.'''&lt;br /&gt;
# '''Perform operations equivalent to those in each of the migration scripts, if any.''' The first part of a script file name is the timestamp it was added to the codebase, and this also dictates the order in which they should be run.&lt;br /&gt;
&lt;br /&gt;
=== Copying new files ===&lt;br /&gt;
Files added/changed in the new version are stored in the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory. In an update, they should be copied into the installation directory, replacing any existing files of the same name/path. If the &amp;lt;tt&amp;gt;rsync&amp;lt;/tt&amp;gt; utility is available on the system, you can use one command to safely copy these files into the installation directory as follows (executed in that same directory):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rsync -avc update/source/ ./&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, you will need to use &amp;lt;tt&amp;gt;cat&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;cp&amp;lt;/tt&amp;gt;, two utilities that should be available on every UNIX-like system, and the lists of files added in each intermediate version. These lists are stored in the folder &amp;lt;tt&amp;gt;update/data/fileList&amp;lt;/tt&amp;gt; and named like &amp;quot;{version}.txt&amp;quot; (&amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version to which it applies), each file on its own line in that file. Run the following command, ignoring errors:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/fileList/*; do for l in $(cat $f); do cp update/source/&amp;quot;$l&amp;quot; ./&amp;quot;$l&amp;quot; ; done ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting old files ===&lt;br /&gt;
Files to be deleted are listed in a similar manner as the files to be added: one file per line. The list of files to delete are located at files with paths that look like this: &amp;lt;tt&amp;gt;update/data/deletionList/{version}.txt&amp;lt;/tt&amp;gt;&lt;br /&gt;
So, for an update from 5.5 to 5.7 with intermediate version 5.6 (for instance), the files would be &amp;lt;tt&amp;gt;update/data/deletionList/5.6&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/data/deletionList/5.7&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is only one version through which to update, the operation is very simple, and can be performed in one UNIX/Linux (bash) shell command as follows (if the version is 3.6.3, for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
xargs -a update/data/deletionList/3.6.3.txt -d&amp;quot;\n&amp;quot; rm -fv&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, if there are multiple versions, this may be a more tricky proposition. The necessity arises to create a cumulative list, because there might be the possibility that a file is deleted in one version and re-established in a new version. Nevertheless, while this is possible, it is highly unlikely, and in most cases it is safe to simply iterate over all the &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; data files and delete all the files listed therein. As a bash command:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/deletionList/*; do xargs -a $f -d&amp;quot;\n&amp;quot; rm -fv ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Editing the Configuration File ===&lt;br /&gt;
This final step in the update process, before refreshing, should not be done manually unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a fresh installation at the same version as the one to which you have updated.&lt;br /&gt;
&lt;br /&gt;
To update the configuration manually:&lt;br /&gt;
# Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor&lt;br /&gt;
# Find the line with the &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; variable in it. It should look like this (but with the version from which you are updating): &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$version = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change the value in quotes to the current version being updated to, and save the file.&lt;br /&gt;
&lt;br /&gt;
=== Clearing caches and refreshing the application ===&lt;br /&gt;
It is generally always a good idea to clear the application's server-side cache and other ephemeral files/data after updating. This is taken care of automatically by the built-in updater, but barring proper use and functioning of that, the operations can be performed manually as follows: &lt;br /&gt;
;clear the data cache&lt;br /&gt;
: delete all &amp;quot;&amp;lt;tt&amp;gt;.bin&amp;lt;/tt&amp;gt;&amp;quot; files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -fv protected/runtime/cache/*.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear web assets&lt;br /&gt;
: delete all sub-folders of &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt; by running this command (in the installation directory): &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -r assets/*/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear the &amp;quot;auth cache&amp;quot;&lt;br /&gt;
: Flush the auth cache table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;&lt;br /&gt;
DELETE FROM `x2_auth_cache` WHERE 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;log all users out&lt;br /&gt;
: Flush the sessions table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;DELETE FROM `x2_sessions` WHERE 1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Upgrading =&lt;br /&gt;
&lt;br /&gt;
== Using the Web Interface ==&lt;br /&gt;
[[File:Software-Upgrade-Registration.png|400px|thumb|right|Software upgrade page, in the first step (registration)]]&lt;br /&gt;
To upgrade to Professional Edition&lt;br /&gt;
# Purchase X2Engine Professional Edition Download. Note the registration info in the activation email.&lt;br /&gt;
# Go to the Admin page in X2Engine and find &amp;quot;Upgrade X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot;.&lt;br /&gt;
# In the ''Upgrade'' page, enter your registration info into the form and click &amp;quot;Register&amp;quot; to continue. Once complete, you will see a list of changes similar to those displayed when performing an update.&lt;br /&gt;
# Review the changes to be applied and then proceed by clicking &amp;quot;Upgrade&amp;quot;, similar to performing an update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Offline&amp;quot; Upgrading ==&lt;br /&gt;
This works in exactly the same way as offline updating. However, to obtain your upgrade package, which you will extract into a folder called &amp;quot;update&amp;quot; on the X2Engine web server, contact [mailto:customersupport@x2engine.com customer support]. Furthermore, you will need to run the following database command on your X2Engine database when done with the upgrade, replacing &amp;lt;tt&amp;gt;{product key}&amp;lt;/tt&amp;gt; with your product key, to allow your installation to receive Professional Edition software updates:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{product key}' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Manually Upgrading ==&lt;br /&gt;
The process of upgrading is very similar to that of updating, and uses all of the same underpinnings as the updater. Thus, almost all of the above information and procedures will apply to upgrades. However, note the following crucial differences:&lt;br /&gt;
&lt;br /&gt;
* '''X2Engine must be at the latest available version in order to upgrade.'''&lt;br /&gt;
* '''To obtain the upgrade package, you must be an owner of an X2Engine Professional Edition Download-type license.''' Contact [mailto:customersupport@x2engine.com customer support] for further details.&lt;br /&gt;
* '''There will be only one &amp;quot;intermediate&amp;quot; version.''' The changes to apply will move X2Engine from a version to that same version, applying instead the necessary changes to switch its ''edition'' from Open Source to Professional.&lt;br /&gt;
* '''There are no file deletions.'''&lt;br /&gt;
* '''The properties of the package manifest differ slightly.''' In upgrades, the properties of the data object are &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, etc. Note also, the update data is in aptly-named sub-folders of &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt; as well, and there are no &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; lists to handle.&lt;br /&gt;
* '''The configuration is not changed.''' Instead, the value stored in the field &amp;lt;tt&amp;gt;unique_id&amp;lt;/tt&amp;gt; of the admin record in the database table &amp;lt;tt&amp;gt;x2_admin&amp;lt;/tt&amp;gt; must be changed to the license key, and the &amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt; field must be changed to &amp;quot;pro&amp;quot;. To perform these both, run the following command on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''It is not necessary to clear caches or user sessions when done.''' These operations are performed in updates to eliminate ephemeral (session-ingrained) data that may no longer be applicable and thus cause problems. Upgrades will rarely, if ever, invalidate this data.&lt;br /&gt;
&lt;br /&gt;
= Updating to Beta Versions = &lt;br /&gt;
Beginning at version 4.1.2, after much advocacy to adopt this practice, X2Engine began a two-phase release process in which each version would be released first as a beta, then to the broader public after initial bug finds. The purpose behind this is to avoid updating production installations of X2Engine to new versions that have had zero public exposure, and thus avoid introducing bugs that were not found in initial QA testing (due to a incompatibility with a particular server environment or configuration of X2Engine) into sensitive production environments.&lt;br /&gt;
&lt;br /&gt;
By default, the updater will not update to beta versions. The updater must be configured to use the proper URL on the update server for beta release data, by the following procedure:&lt;br /&gt;
# Create a file &amp;quot;constants-custom.php&amp;quot; in the root level of X2Engine. You can easily do this by renaming &amp;quot;constants-custom.example.php&amp;quot; to that name (remove &amp;quot;.custom&amp;quot;).&lt;br /&gt;
# Look for the following line, and in it, change &amp;quot;false&amp;quot; to &amp;quot;true&amp;quot;: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('X2_UPDATE_BETA') or define('X2_UPDATE_BETA',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The advantage of enabling beta releases is that new features will be available much sooner. Additionally, fixes to bugs will also be available sooner. The main disadvantage of enabling beta releases is that they are never guaranteed to be as stable as non-beta releases. We encourage users to clone their installations of X2Engine, if able, and use beta versions on them, and report any issues they encounter for their particular server environment/special configuration/custom implementation of X2Engine. This way, any issues that arise can be fixed in the stable release. The more users participate in beta testing, the more stable the non-beta releases will become.&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
== User interface (UI) &amp;quot;bugs&amp;quot; after the update ==&lt;br /&gt;
It is extremely common (and easily remedied) that after an important update, certain features of the user interface of X2Engine may not work properly. This is caused, in nearly all cases, by the web browser's cache, which contains out-of-date copies of X2Engine's essential user interface files. To solve these problems, the first step to try is clearing the cache. A step-by-step guide to doing so is given in [[wikihow:Clear-Your-Browser%27s-Cache]].&lt;br /&gt;
&lt;br /&gt;
== Errors in the updater itself == &lt;br /&gt;
When all else fails, the updater may have to be patched due to an irreconcilable backwards compatibility issue. Fortunately, if one has read/write access to the source files on the webserver, this is straightforward to fix. See [[#Refreshing_and_Updating_the_Updater_Utility|Refreshing and Updating the Updater Utility]].&lt;br /&gt;
&lt;br /&gt;
== Professional Edition not in effect after upgrading ==&lt;br /&gt;
This may be caused by Professional Edition not getting &amp;quot;switched on&amp;quot; properly, despite all the necessary filesystem and database changes already being in effect. First, to see if this scenario applies to you, look for a folder &amp;quot;&amp;lt;tt&amp;gt;reports&amp;lt;/tt&amp;gt;&amp;quot; inside &amp;lt;tt&amp;gt;protected/modules&amp;lt;/tt&amp;gt;, and a table &amp;lt;tt&amp;gt;x2_reports&amp;lt;/tt&amp;gt; in the X2Engine database. If you find these things, you can try the following to enable Professional Edition:&lt;br /&gt;
# Find the file &amp;lt;tt&amp;gt;constants.php&amp;lt;/tt&amp;gt; in the installation directory. Open it in a text editor, and look for the following line: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('PRO_VERSION') or define('PRO_VERSION',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; on that line, and save the file.&lt;br /&gt;
# Run the following SQL command (replacing &amp;lt;tt&amp;gt;{license key}&amp;lt;/tt&amp;gt; with your X2Engine license key) on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General Procedures to Try ==&lt;br /&gt;
It may sometimes be possible that the update did not finish cleanly. In such cases, it will be necessary to manually re-perform some of the final operations in the update, described in [[#Introduction|Introduction]]. &lt;br /&gt;
&lt;br /&gt;
=== Refresh the Source Code ===&lt;br /&gt;
This step is not really the best step to try first, as it is usually more time-consuming. It entails re-uploading the entire application, in order to reset all non-ephemeral (source code) files to what they should be in a fresh installation of X2Engine. To do this:&lt;br /&gt;
# Download the refresh package. There should be a download link for this in the ''Updater Settings'' page. Otherwise, if that doesn't work and you're using Open Source edition, use [https://x2planet.com/installs/refresh.zip this link], or if using Professional Edition, paste the following url (after replacing {key} with your license key) into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/refresh-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Upload the ''contents'' of the downloaded zip archive to the installation directory, overwriting existing files.&lt;br /&gt;
&lt;br /&gt;
=== Clear Caches ===&lt;br /&gt;
See &amp;quot;[[#Clearing_caches_and_refreshing_the_application|Clearing Caches...]]&amp;quot; for details.&lt;br /&gt;
&lt;br /&gt;
=== Regenerate the Configuration ===&lt;br /&gt;
Again, this should not be done unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a new installation at the same version as the one you updated to. Refer to [[#Editing_the_Configuration_File|Editing the Configuration File]].&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1845</id>
		<title>Software Updates and Upgrades</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1845"/>
				<updated>2014-06-24T21:16:53Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Disadvantages */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
[[File:Update_Process.svg|200px|thumb|right|Top-level overview of the update process]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
The following things happen in typical a software update (or upgrade):&lt;br /&gt;
* Files that are new in the new version are added&lt;br /&gt;
* Files that have been changed in the new version are replaced with the new versions of those files &lt;br /&gt;
* Files that are no longer used in the new version are deleted&lt;br /&gt;
* Changes to the database are made to make the database compatible with the new version of X2Engine&lt;br /&gt;
* The main configuration file, &amp;lt;tt&amp;gt;X2Config.php&amp;lt;/tt&amp;gt;, is edited to reflect the new state of the application.&lt;br /&gt;
* X2Engine data caches are cleared&lt;br /&gt;
&lt;br /&gt;
These operations are typically handled by the updater utility component [[x2doc:UpdaterBehavior|UpdaterBehavior]] and controlled via the in-app web-based updater or the command line updater. This article will cover both how to use said interfaces to the updater, and how to perform updates manually or with the &amp;quot;offline&amp;quot; method if the need arises, in addition to troubleshooting and recovering from an update that did not complete cleanly.&lt;br /&gt;
&lt;br /&gt;
= Disclaimer =&lt;br /&gt;
This article covers the standard and recommended update/upgrade methods, in addition to alternate methods and supplemental procedures. As general rules and precautions for using this article:&lt;br /&gt;
# ''If you do not fully and completely understand any step of any instructions given in this article, '''do not''' follow them''; ask for help instead on [http://x2community.com the forums] or through contacting X2Engine Technical Support if you are an existing customer.&lt;br /&gt;
# You should always make a backup of all of your files ''and'' a full backup copy of your database, preferably in a format (i.e. SQL script) that can be used to create a verbatim/carbon copy of the original database, with all of the features of the original schema, '''before''' attempting any steps of the update process manually.&lt;br /&gt;
# Some of the procedures outlined in this article involve manually creating or modifying files. Note that the same filesystem prerequisite for installation, namely that the file ownership must match the user under which PHP executes on the web server, applies to these steps.&lt;br /&gt;
&lt;br /&gt;
Furthermore, '''in regard to uploading files:''' note that any instructions to upload folders into X2Engine, and replace/merge contents, refer to replacing files in X2Engine with files in the archive at analogous paths. For example, if you have a zip file that contains a folder &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, and inside of that is a folder &amp;lt;tt&amp;gt;components&amp;lt;/tt&amp;gt;, and inside of that is a file &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt;, and the instructions are to upload/replace in X2Engine, you should upload &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt; to replace the existing file of the same name in &amp;lt;tt&amp;gt;protected/components&amp;lt;/tt&amp;gt; inside of X2Engine.&lt;br /&gt;
&lt;br /&gt;
X2Engine Inc. cannot be held accountable for damage/errors caused by any missteps or mistakes in manually-performed update operations. If in doubt, make a sandbox/clone of your X2Engine installation by copying the files and database to a different server, and test performing the operations on that before proceeding with your production install of X2Engine.&lt;br /&gt;
&lt;br /&gt;
= Update Notifications =&lt;br /&gt;
[[File:Update Notification.png|200px|frame|right|System flash message displaying software update notification.]]&lt;br /&gt;
To get notified of software updates, unless already using X2Engine Professional Edition, one must first &amp;quot;subscribe&amp;quot; to them. This can be done via checking the box &amp;quot;Notify me of software updates&amp;quot; in the installation form. If X2Engine is already installed, this can be done by going to &amp;quot;Updater Settings&amp;quot; from the Admin page and enabling them. If using Professional Edition, these steps are not not necessary.&lt;br /&gt;
&lt;br /&gt;
Once that has been completed, set the update notification interval as desired in &amp;quot;Updater Settings&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Update Methods =&lt;br /&gt;
There are, in essence, just three basic methods of updating, and variations on each. The three main methods are: &lt;br /&gt;
# web updater,&lt;br /&gt;
# command line updater, and&lt;br /&gt;
# manually.&lt;br /&gt;
In each case, an update package from the update data server is used as the authoritative list of changes to apply, but in the non-manual methods, X2Engine's own updater class does all the work applying changes from said package.&lt;br /&gt;
&lt;br /&gt;
In web and command line updates, there is also the option to first download and unpack the package manually (see [[#Performing_.22Offline.22_Updates|performing &amp;quot;offline&amp;quot; updates]]). This circumvents the need for the local server (on which X2Engine is hosted) to be connected to the internet. Similarly, in the case of manual updates, it is not necessary for the web server to actually have an internet connection.&lt;br /&gt;
&lt;br /&gt;
== Using the Web Updater ==&lt;br /&gt;
[[File:Updater-1.png|300px|thumb|right|The updater utility page.]]&lt;br /&gt;
The web updater can be accessed by any of the following methods:&lt;br /&gt;
* Using the link in the notification message at the top of the window in versions later than 3.5.5&lt;br /&gt;
* By going to the Admin page and clicking the &amp;quot;Update&amp;quot; button at the top, in earlier versions, with update notifications enabled&lt;br /&gt;
* Through &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
First, navigate to the web updater page, and '''do not''' cancel the request (stop or hit your browser's &amp;quot;back&amp;quot; button) until the page has loaded. If the updater utility has undergone changes and needs to self-update to be compatible with the software updates server, this is when it will do so. Thus, it is critical that you do not interrupt this process. If the page does not load, or displays the error message that dependencies could not be retrieved, you may have to [[#Performing_.22Offline.22_Updates|perform an &amp;quot;offline&amp;quot; update]].&lt;br /&gt;
&lt;br /&gt;
There are three ways of getting to the updater page. One is to simply click the link given in the update notification flash message, if available. The other is to click on the &amp;quot;Update&amp;quot; button at the top of the admin page. If software update notifications are not enabled, one can manually check for and install an update by going to &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
Next, make a backup of the database and files (recommended), follow the instructions on the page, and click the &amp;quot;Update&amp;quot; button when ready.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
It is generally quick and is the easiest method to use, and it does not require shell/SSH access to the web server.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
It is not recommended for use on large installations of X2Engine, i.e. containing over a million records, or for updating from very old versions (i.e. a version more than five months old). This is because the final update process may be cut short by server timeout or an interruption in the network connection. This can result in an incomplete/broken update that requires recovering manually (a potentially long process).&lt;br /&gt;
&lt;br /&gt;
==== Explanation ====&lt;br /&gt;
The inherent weakness of the web updater (or any web-based updater to any web application) is that it must always rely upon web requests to the server, and it thus must perform extremely critical operations for the update all within the scope of a web request. Web requests, it goes without saying, are not always reliable. For example, one's internet connection might get cut off due being on a wireless network and encountering interference. Furthermore, if any operations take an exceedingly long amount of time, e.g. because of the size of the data set (and thus indexes) on which the update must operate, or the number of database changes that need to be applied, the web request for those operations may take longer than the PHP execution time limit as configured on the webserver. This would potentially cut short the execution of a critical update stage, and thus the update would require manual recovery at best, or completely restoring  X2Engine to a backup at worst.&lt;br /&gt;
&lt;br /&gt;
==== Ways to get around this ====&lt;br /&gt;
In most cases, including on shared hosting, it should be possible to change the time limit on PHP. It is highly recommended that you either disable the timeout or set it to a very generous value (such as 300 seconds) while attempting to update via the web updater, especially if you are many versions behind or have a high volume of data in your CRM.&lt;br /&gt;
&lt;br /&gt;
== Using the Command Line Updater ==&lt;br /&gt;
This update method uses all of the same thoroughly-tested underpinnings and processes as the web updater, but does so all within a system command to the Yii console.&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
While in via SSH, or in a terminal emulator, or in a script, run the following command in the &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt; subdirectory of X2Engine:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./yiic update app&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the following optional arguments to the command line updater:&lt;br /&gt;
;&amp;lt;tt&amp;gt;--force&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; for this option (i.e. &amp;lt;tt&amp;gt;--force=1&amp;lt;/tt&amp;gt;) to proceed with the update even if compatibility issues were detected. Default: 0 (halt if compatibility issues were detected).&lt;br /&gt;
;&amp;lt;tt&amp;gt;--backup&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that a database backup should be performed first before updating, &amp;quot;0&amp;quot; to proceed without creating a backup first. Default: 1.&lt;br /&gt;
;&amp;lt;tt&amp;gt;--lock&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that the application should be locked during the final installation of the update package to prevent data entry during the sensitive moments when the database is being altered and the files updated, &amp;quot;0&amp;quot; to permit use even when changes to the app are being applied. Default: 0.&lt;br /&gt;
&lt;br /&gt;
If running the command gives you the following message:&lt;br /&gt;
&amp;lt;pre&amp;gt;The updater is now up-to-date and compliant with the updates server. Re-run the command to proceed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
That indicates that the updater first needed to self-update, and succeeded in doing so. You should then run the same command again to complete the update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Safer to use on large, mission-critical systems. The success of a software update (or upgrade) does not hinge upon a single web request completing successfully.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
More difficult to use; requires shell access to the web server. The console command must be run as the same system user that owns all the files and directories of X2Engine, in order to circumvent permission issues when manipulating file system objects.&lt;br /&gt;
&lt;br /&gt;
== Using the Automatic Updater ==&lt;br /&gt;
[[File:Auto-Update.png|400px|thumb|right|Auto-update scheduler on the Updater Settings page]]&lt;br /&gt;
This method uses the command line updater, but runs the command on your behalf using the local cron daemon. To configure, go to the Updater Settings Page, check the box titled &amp;quot;Update Automatically&amp;quot;, and save. If you have Professional Edition, you can manage the update schedule in addition to other automated tasks from the Cron Table settings page, which can also be found under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Automates the entire process of updating.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
Only available on UNIX-like systems that have a cron daemon, and which permit user access to the cron table.&lt;br /&gt;
&lt;br /&gt;
== Performing &amp;quot;Offline&amp;quot; Updates ==&lt;br /&gt;
In some server environments, the updater may not be able to access the updates server by itself, due to (for example) the PHP or network configuration, or because the &amp;quot;zip&amp;quot; PHP extension (requried) is unavailable or has been disabled. It is in these situations necessary to download the update package manually, unpack on the server (or unpack and upload the files to the server), and then run the updater. When performing updates in this way, the updater will not need to access the updates server, but will use the manually-downloaded and extracted package files present in the local filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Refreshing and Updating the Updater Utility ===&lt;br /&gt;
Before starting, note that the updater itself may have undergone changes; the X2Engine update process is subject to change. To address this technical challenge, the updater utility within X2Engine would normally first attempt to download the latest version of itself, in order to be compatible with the updates server. However, this sometimes will not work properly. If your X2Engine installation is running in a hosting environment that prevents it from making outbound HTTP requests (the [[requirements:|requirements check script]] should tell you if this is the case), you will need to do this manually. Also, in version 3.5.5, there was a bug in the updater utility's self-update methods.&lt;br /&gt;
&lt;br /&gt;
To update the updater utility:&lt;br /&gt;
# '''Determine the version of the updater utility package''' by visiting [https://x2planet.com/installs/updates/updateCheck this link]. You should see a page that's entirely blank except for a version number.&lt;br /&gt;
# '''Download the updater package.''' In versions 3.6 and later, a download link for this should be available through the ''Updater Settings'' page. Otherwise, use the following URL (if in Open Source Edition): &amp;lt;pre&amp;gt;https://x2planet.com/installs/updater.zip&amp;lt;/pre&amp;gt; Otherwise, if using Professional Edition, paste the following url, after replacing {key} with your license key, into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/updater-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# '''Extract the downloaded zip archive and upload''' the &amp;quot;protected&amp;quot; folder to your server. Merge folders and overwrite existing files when prompted.&lt;br /&gt;
# '''Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor''' and find the line that looks like this:&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$updaterVersion = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# '''Change the text inside the pair of single quotes''' on that line so that it is the current updater version obtained in step #1. &lt;br /&gt;
# '''Save the file.'''&lt;br /&gt;
&lt;br /&gt;
=== Obtaining the Update Package ===&lt;br /&gt;
First, use the link in the ''Updater Settings'' page entitled &amp;quot;Latest Update Package for Version...&amp;quot;, to download the update package. If the link in the Updater Settings page does not work, use the following URL to download the update package (if using Open Source Edition), replacing &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; with your X2Engine installation's version:&lt;br /&gt;
&amp;lt;pre&amp;gt;https://x2planet.com/installs/updates/{version}/none&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should receive, from that URL, a zip archive file called &amp;quot;update.zip&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If using Professional Edition, and your download link is not working, contact [mailto:customersupport@x2engine.com customer support].&lt;br /&gt;
&lt;br /&gt;
=== Unpacking the Update ===&lt;br /&gt;
[[File:Update-in-Progress.png|200px|frame|right|System flash message displaying notification of pending update.]]&lt;br /&gt;
The update package will be a zip archive. The ''contents'' of this archive must be extracted ''into'' a folder called &amp;quot;update&amp;quot; in the installation directory. So, in other words, after extracting and uploading to the server, there should be a folder called &amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt; alongside &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;framework&amp;lt;/tt&amp;gt; (etc). Within that folder (&amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt;) should be the following contents:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A file containing important data to be used by the updater&lt;br /&gt;
;&amp;lt;tt&amp;gt;contents.md5&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A message digest file containing MD5 checksums of all files in the archive excluding itself.&lt;br /&gt;
;&amp;lt;tt&amp;gt;source&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A sub-folder/directory, whose internal structure mimics that of X2Engine, containing files that were changed in the update. If there were no files changed (i.e. only database changes were in the update), this directory will not be present.&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A directory containing flat data files. These files are typically not used by the updater, but are useful if performing manual updates or recovery.&lt;br /&gt;
&lt;br /&gt;
=== Applying the Update ===&lt;br /&gt;
[[File:Steps-post-Unpack.png|200px|frame|right|Steps of the update process in the event of manually-extracted package (or resuming a preexisting update)]]&lt;br /&gt;
After unpacking, if you run the command line updater, it should (if no problems arise, i.e. incompatible package version) apply the update straight away. Otherwise, log into X2Engine, and you should see a system flash message notifying you of an update in progress. This message shows up whenever there is an update package folder (&amp;quot;update&amp;quot;) present on the server. If you then go to the updater page, once the page has finished loading, you should see that there only remain the steps &amp;quot;Review and confirm changes&amp;quot;, &amp;quot;Apply changes&amp;quot; and &amp;quot;Reload&amp;quot; remain to be completed. To complete the update, when ready, click &amp;quot;Update&amp;quot;; otherwise, to delete the &amp;quot;update&amp;quot; folder and cancel the update, click &amp;quot;Cancel&amp;quot; either on the updater page or in the system flash message.&lt;br /&gt;
&lt;br /&gt;
== Performing Updates Manually ==&lt;br /&gt;
While it is preferable to avoid manually applying changes to X2Engine, and you may never need to, it may in some circumstance or another actually be necessary to do so. For all such situations, the raw update data files generated by the X2Engine update build process are bundled with every update package to help you perform the operations described in [[#Introduction|&amp;quot;Introduction&amp;quot;]]. This section will describe how to perform each of those operations. &lt;br /&gt;
&lt;br /&gt;
=== Prerequisites ===&lt;br /&gt;
First and foremost, all shell commands listed here, and in general, any operations that create or modify files, ''must be run as the same system user under which the server runs PHP scripts.'' Otherwise, the ownership and/or permissions of files and directories will have to be changed after completing all changes.&lt;br /&gt;
&lt;br /&gt;
To obtain the update package (which will be necessary), refer to &amp;quot;[[#Obtaining_the_Update_Package|Obtaining the Update Package]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Furthermore, it is strongly advised that you first make a back-up copy of all files and of X2Engine's database. Your database backup would best be a full, exact copy, such as one that would be made by [http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html mysqldump], or the built-in export tools of [http://www.phpmyadmin.net/home_page/index.php PHPMyAdmin].&lt;br /&gt;
&lt;br /&gt;
The X2Engine global import/export utility (&amp;quot;Import All Data&amp;quot;/&amp;quot;Export All Data&amp;quot;) in Admin '''is not recommended by itself as the sole backup tool'''. This is especially important to note because of how this tool cannot re-create custom fields; they must exist in the target installation before the import. However, using it to ''supplement'' your standard database backup method will prove useful in the event that you:&lt;br /&gt;
# don't have any custom fields,&lt;br /&gt;
# cannot manage to update X2Engine with success, and&lt;br /&gt;
# wish to start over with a fresh installation.&lt;br /&gt;
In such a case, you will then be able to import your old data into the new installation.&lt;br /&gt;
&lt;br /&gt;
=== Introduction: Data Files ===&lt;br /&gt;
In [[#Unpacking_the_Update|Unpacking the update]], a rough overview of an update package's contents were given. In this section, the anatomy and purpose of each of the data files will be introduced.&lt;br /&gt;
&lt;br /&gt;
Included in every update package is two copies of the following data:&lt;br /&gt;
* Files to copy&lt;br /&gt;
* Files to delete&lt;br /&gt;
* Database changes to enact&lt;br /&gt;
* Migration scripts to run, if any&lt;br /&gt;
&lt;br /&gt;
The first, immediately visible place where all of this data is stored is in the file &amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;, as a JSON-encoded array. See the next section for how to use this file.&lt;br /&gt;
&lt;br /&gt;
The second place, which can be useful to users who do not wish to automate or script any step of a manual update, is in the files contained in the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; directory. Each file is named after the version to which it corresponds, and contains the data required to update ''to'' that version ''from the version immediately preceding it.''' Thus, the versions you should see in each file name are the versions above your X2Engine installation's current version, up to and including the version being updated to.&lt;br /&gt;
&lt;br /&gt;
In an update, the directory will contain the following subdirectories:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of files to copy. The format is each line containing a file path in the list.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of obsolete files for that version which will need to be deleted. The format is the same as in &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts corresponding to each version. Each file is formatted such that it can be directly run on the database as an SQL script, although this is not generally recommended. The list of SQL commands to run is a of the following format: &lt;br /&gt;
  [query 1];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [query 2];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [etc]&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts with similar naming and formatting conventions to the &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; scripts. See [[#The_structure_of_the_.22data.22_array|The structure of the &amp;quot;data&amp;quot; array]] for explanation of the purpose of &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== How to use the manifest for updating manually ===&lt;br /&gt;
If you so choose, you may write a script to perform all of the operations for an update in one fell swoop. If so, and if the preferred scripting language of choice has a JSON-parsing library available, you will be able to save yourself a lot of effort by using the main data file (&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;) to load all of the necessary data for the update.&lt;br /&gt;
&lt;br /&gt;
==== The structure the manifest ====&lt;br /&gt;
In updates, the contents of the plain-text file manifest.json are a JSON-encoded object containing the following properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the data corresponding to each intermediate version. It is, in essence, all the data stored in the files in &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt;. If the update is for an installation of X2Engine that is only one version behind, it will contain only one element. Each element of this array is itself an array containing respective data for the intermediate version.&lt;br /&gt;
;&amp;lt;tt&amp;gt;versions&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array listing the version numbers of each intermediate version in the update. Similar to &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, this array will contain only one element if updating to the next version, and that version ''will be the next version''.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of deletions of obsolete files across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of files added or changed across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''from'', or in other words, the version of X2Engine before the package is applied. '''This must be the same as the version of installation to which the package is being applied.''' If this is not so, the updater utility would exit immediately, as applying the changes in the update would have potentially harmful results to data and/or compatibility with future updates.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''to'', or in other words, the version of X2Engine before the package is applied.&lt;br /&gt;
;&amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the version of the updater utility for which the package was compiled. If the structure of the update package or of the manifest changes, the updater must also change to accommodate and recognize the changes; else it might try to access properties that don't exist, or ignore important ones that do. This is the single property of manifest.json that should never change.&lt;br /&gt;
;&amp;lt;tt&amp;gt;scenario&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, which should be &amp;quot;update&amp;quot; if the package is for an update. In the case of upgrades, which use most of the same functionality of the updater, it should be &amp;quot;upgrade&amp;quot;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation after the package is applied. It should be &amp;quot;opensource&amp;quot; for open source edition, &amp;quot;pro&amp;quot; for professional edition. In the case of an upgrade package, it differs from the &amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation before the package is applied. &lt;br /&gt;
;&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An integer denoting the timestamp of the release date of the version of X2Engine denoted by &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The structure of the &amp;quot;data&amp;quot; array ====&lt;br /&gt;
In updates, each version's data, contained in each element of the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, contains the following nested array properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files added for the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files deleted in the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of SQL commands to bring the database structure and contents into compliance with the version from the previous version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A prerequisite list of SQL commands that should be run before those in &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, and if any fail, the failures should be ignored. These are added to updates whenever it is necessary to normalize the database schema, i.e. in the event that some installations' databases may not be consistent with others, but the commands might fail on every other system. These SQL commands were intended to be &amp;quot;attempted&amp;quot;; they are not guaranteed to succeed on all installations. An example of when this is necessary would be adding a foreign key constraint when in previous versions the storage engine was left to the database's default settings in old versions. MyISAM does not natively support foreign key constraints. Thus, to standardize this across all installations, including installations on servers where the storage engine defaulted to MyISAM, the constraint must first be dropped (in a &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; command) before the relevant tables are changed to the InnoDB storage engine, and the constraint is added back again, in the usual &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;version&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The version number&lt;br /&gt;
;&amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The edition&lt;br /&gt;
;&amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of PHP scripts that would be run in an update for this version. These are a subset of fileList and are typically used for advanced database operations, i.e. restructuring values of JSON-encoded fields, which are extremely impractical (or impossible) to perform using raw SQL commands. The files contained in the list all have paths that look like &amp;quot;&amp;lt;tt&amp;gt;protected/migrations/{version}/{timestamp}-name-of-operations.php&amp;lt;/tt&amp;gt;&amp;quot;. Note, if this array is not empty, you may need to reverse-engineer or otherwise copy and re-purpose/re-write any migration scripts listed in order to perform their necessary operations. This is because they may depend on running within a Yii Framework environment, i.e. will contain references to components of the Yii application singleton such as &amp;quot;db&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Note that in upgrades, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; are replaced by &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; (respectively), and &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; are missing (because they aren't necessary).&lt;br /&gt;
&lt;br /&gt;
==== What you will need for the update, if using the manifest ====&lt;br /&gt;
''Among this data, what you will need:'' &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, which will give you the full list of files that need to be deleted, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, which will give you the full list of files to copy from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory, &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;, which give you values with which to update the main X2Engine configuration file, and, within each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, the sub-array properties &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;, with which you will construct the list of database commands to run. &lt;br /&gt;
&lt;br /&gt;
A procedure for manually updating X2Engine, with data from manifest.json, thus might look like this:&lt;br /&gt;
# For each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; (or in other words, each version's changes):&lt;br /&gt;
## Run all the SQL commands in its corresponding &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; sub-array, ignoring errors&lt;br /&gt;
## Run all SQL commands in its &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; array&lt;br /&gt;
## Perform database operations that would be equivalent to those performed in migration scripts&lt;br /&gt;
# For each file in the cumulative list &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, copy it from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory into its analogous location in the installation directory of X2Engine, creating parent directories as necessary if they don't already exist.&lt;br /&gt;
# Delete all files in the cumulative list &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; from the installation directory&lt;br /&gt;
# Clear the data cache files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt; and the temporary asset folders in &amp;lt;tt&amp;gt;assets/&amp;lt;/tt&amp;gt;&lt;br /&gt;
# Clear all session data (&amp;lt;tt&amp;gt;x2_sessions&amp;lt;/tt&amp;gt;) and auth cache data (&amp;lt;tt&amp;gt;x2_auth_cache&amp;lt;/tt&amp;gt;).&lt;br /&gt;
# Update the main X2Engine configuration (&amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt;) file, setting:&lt;br /&gt;
## &amp;lt;tt&amp;gt;$buildDate&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt; property,&lt;br /&gt;
## &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt; property, and &lt;br /&gt;
## &amp;lt;tt&amp;gt;$updaterVersion&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
&lt;br /&gt;
=== Enacting Database Changes ===&lt;br /&gt;
Inside the &amp;lt;tt&amp;gt;update/sqlList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/sqlForce&amp;lt;/tt&amp;gt; folder is a list of &amp;lt;tt&amp;gt;.sql&amp;lt;/tt&amp;gt; files, named according to their corresponding version's update, that can essentially be run as SQL scripts to perform the update. Note, however, that '''if the update traverses more than one version, the scripts must be run in the order of their corresponding version.''' This is because, in the database changes for any version, it is assumed that the database schema and contents conform to the version that immediately preceded it. The PHP function [[phpfunc:version-compare|version_compare]], and the method &amp;quot;[http://pythonhosted.org/setuptools/pkg_resources.html#parsing-utilities parse_version]&amp;quot;, in the pkg_resources module of setuptools, should correctly identify which version should come first. Note, the [[#The_structure_the_manifest|&amp;quot;versions&amp;quot; property of the manifest]] will contain the list of versions in the correct order.&lt;br /&gt;
&lt;br /&gt;
Furthermore, note, &amp;quot;sqlList&amp;quot; and &amp;quot;sqlForce&amp;quot; may not encompass all necessary database changes; if there are any migration scripts in the update, the operations they perform will similarly need to be performed in the current update (see the description of &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; in [[#The_structure_of_the_.22data.22_array|&amp;quot;the structure of the 'data' array&amp;quot;]]).&lt;br /&gt;
&lt;br /&gt;
In summary, for each version traversed in the update, the following operations should be run in order, with &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version number of the version in question:&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlForce/{version}.sql&amp;lt;/tt&amp;gt; file,''' in the order they appear in the file. Ignore if any commands fail. Note, if passed to the MySQL client as a script, any failures might trigger a halt in execution and thus skip the remainder of the commands.&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlList/{version}.sql&amp;lt;/tt&amp;gt; file.'''&lt;br /&gt;
# '''Perform operations equivalent to those in each of the migration scripts, if any.''' The first part of a script file name is the timestamp it was added to the codebase, and this also dictates the order in which they should be run.&lt;br /&gt;
&lt;br /&gt;
=== Copying new files ===&lt;br /&gt;
Files added/changed in the new version are stored in the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory. In an update, they should be copied into the installation directory, replacing any existing files of the same name/path. If the &amp;lt;tt&amp;gt;rsync&amp;lt;/tt&amp;gt; utility is available on the system, you can use one command to safely copy these files into the installation directory as follows (executed in that same directory):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rsync -avc update/source/ ./&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, you will need to use &amp;lt;tt&amp;gt;cat&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;cp&amp;lt;/tt&amp;gt;, two utilities that should be available on every UNIX-like system, and the lists of files added in each intermediate version. These lists are stored in the folder &amp;lt;tt&amp;gt;update/data/fileList&amp;lt;/tt&amp;gt; and named like &amp;quot;{version}.txt&amp;quot; (&amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version to which it applies), each file on its own line in that file. Run the following command, ignoring errors:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/fileList/*; do for l in $(cat $f); do cp update/source/&amp;quot;$l&amp;quot; ./&amp;quot;$l&amp;quot; ; done ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting old files ===&lt;br /&gt;
Files to be deleted are listed in a similar manner as the files to be added: one file per line. The list of files to delete are located at files with paths that look like this: &amp;lt;tt&amp;gt;update/data/deletionList/{version}.txt&amp;lt;/tt&amp;gt;&lt;br /&gt;
So, for an update from 5.5 to 5.7 with intermediate version 5.6 (for instance), the files would be &amp;lt;tt&amp;gt;update/data/deletionList/5.6&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/data/deletionList/5.7&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is only one version through which to update, the operation is very simple, and can be performed in one UNIX/Linux (bash) shell command as follows (if the version is 3.6.3, for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
xargs -a update/data/deletionList/3.6.3.txt -d&amp;quot;\n&amp;quot; rm -fv&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, if there are multiple versions, this may be a more tricky proposition. The necessity arises to create a cumulative list, because there might be the possibility that a file is deleted in one version and re-established in a new version. Nevertheless, while this is possible, it is highly unlikely, and in most cases it is safe to simply iterate over all the &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; data files and delete all the files listed therein. As a bash command:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/deletionList/*; do xargs -a $f -d&amp;quot;\n&amp;quot; rm -fv ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Editing the Configuration File ===&lt;br /&gt;
This final step in the update process, before refreshing, should not be done manually unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a fresh installation at the same version as the one to which you have updated.&lt;br /&gt;
&lt;br /&gt;
To update the configuration manually:&lt;br /&gt;
# Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor&lt;br /&gt;
# Find the line with the &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; variable in it. It should look like this (but with the version from which you are updating): &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$version = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change the value in quotes to the current version being updated to, and save the file.&lt;br /&gt;
&lt;br /&gt;
=== Clearing caches and refreshing the application ===&lt;br /&gt;
It is generally always a good idea to clear the application's server-side cache and other ephemeral files/data after updating. This is taken care of automatically by the built-in updater, but barring proper use and functioning of that, the operations can be performed manually as follows: &lt;br /&gt;
;clear the data cache&lt;br /&gt;
: delete all &amp;quot;&amp;lt;tt&amp;gt;.bin&amp;lt;/tt&amp;gt;&amp;quot; files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -fv protected/runtime/cache/*.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear web assets&lt;br /&gt;
: delete all sub-folders of &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt; by running this command (in the installation directory): &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -r assets/*/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear the &amp;quot;auth cache&amp;quot;&lt;br /&gt;
: Flush the auth cache table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;&lt;br /&gt;
DELETE FROM `x2_auth_cache` WHERE 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;log all users out&lt;br /&gt;
: Flush the sessions table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;DELETE FROM `x2_sessions` WHERE 1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Upgrading =&lt;br /&gt;
&lt;br /&gt;
== Using the Web Interface ==&lt;br /&gt;
[[File:Software-Upgrade-Registration.png|400px|thumb|right|Software upgrade page, in the first step (registration)]]&lt;br /&gt;
To upgrade to Professional Edition&lt;br /&gt;
# Purchase X2Engine Professional Edition Download. Note the registration info in the activation email.&lt;br /&gt;
# Go to the Admin page in X2Engine and find &amp;quot;Upgrade X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot;.&lt;br /&gt;
# In the ''Upgrade'' page, enter your registration info into the form and click &amp;quot;Register&amp;quot; to continue. Once complete, you will see a list of changes similar to those displayed when performing an update.&lt;br /&gt;
# Review the changes to be applied and then proceed by clicking &amp;quot;Upgrade&amp;quot;, similar to performing an update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Offline&amp;quot; Upgrading ==&lt;br /&gt;
This works in exactly the same way as offline updating. However, to obtain your upgrade package, which you will extract into a folder called &amp;quot;update&amp;quot; on the X2Engine web server, contact [mailto:customersupport@x2engine.com customer support]. Furthermore, you will need to run the following database command on your X2Engine database when done with the upgrade, replacing &amp;lt;tt&amp;gt;{product key}&amp;lt;/tt&amp;gt; with your product key, to allow your installation to receive Professional Edition software updates:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{product key}' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Manually Upgrading ==&lt;br /&gt;
The process of upgrading is very similar to that of updating, and uses all of the same underpinnings as the updater. Thus, almost all of the above information and procedures will apply to upgrades. However, note the following crucial differences:&lt;br /&gt;
&lt;br /&gt;
* '''X2Engine must be at the latest available version in order to upgrade.'''&lt;br /&gt;
* '''To obtain the upgrade package, you must be an owner of an X2Engine Professional Edition Download-type license.''' Contact [mailto:customersupport@x2engine.com customer support] for further details.&lt;br /&gt;
* '''There will be only one &amp;quot;intermediate&amp;quot; version.''' The changes to apply will move X2Engine from a version to that same version, applying instead the necessary changes to switch its ''edition'' from Open Source to Professional.&lt;br /&gt;
* '''There are no file deletions.'''&lt;br /&gt;
* '''The properties of the package manifest differ slightly.''' In upgrades, the properties of the data object are &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, etc. Note also, the update data is in aptly-named sub-folders of &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt; as well, and there are no &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; lists to handle.&lt;br /&gt;
* '''The configuration is not changed.''' Instead, the value stored in the field &amp;lt;tt&amp;gt;unique_id&amp;lt;/tt&amp;gt; of the admin record in the database table &amp;lt;tt&amp;gt;x2_admin&amp;lt;/tt&amp;gt; must be changed to the license key, and the &amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt; field must be changed to &amp;quot;pro&amp;quot;. To perform these both, run the following command on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''It is not necessary to clear caches or user sessions when done.''' These operations are performed in updates to eliminate ephemeral (session-ingrained) data that may no longer be applicable and thus cause problems. Upgrades will rarely, if ever, invalidate this data.&lt;br /&gt;
&lt;br /&gt;
= Updating to Beta Versions = &lt;br /&gt;
Beginning at version 4.1.2, after much advocacy to adopt this practice, X2Engine began a two-phase release process in which each version would be released first as a beta, then to the broader public after initial bug finds. The purpose behind this is to avoid updating production installations of X2Engine to new versions that have had zero public exposure, and thus avoid introducing bugs that were not found in initial QA testing (due to a incompatibility with a particular server environment or configuration of X2Engine) into sensitive production environments.&lt;br /&gt;
&lt;br /&gt;
By default, the updater will not update to beta versions. The updater must be configured to use the proper URL on the update server for beta release data, by the following procedure:&lt;br /&gt;
# Create a file &amp;quot;constants-custom.php&amp;quot; in the root level of X2Engine. You can easily do this by renaming &amp;quot;constants-custom.example.php&amp;quot; to that name (remove &amp;quot;.custom&amp;quot;).&lt;br /&gt;
# Look for the following line, and in it, change &amp;quot;false&amp;quot; to &amp;quot;true&amp;quot;: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('X2_UPDATE_BETA') or define('X2_UPDATE_BETA',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The advantage of enabling beta releases is that new features will be available much sooner. Additionally, fixes to bugs will also be available sooner. The main disadvantage of enabling beta releases is that they are never guaranteed to be as stable as non-beta releases. We encourage users to clone their installations of X2Engine, if able, and use beta versions on them, and report any issues they encounter for their particular server environment/special configuration/custom implementation of X2Engine. This way, any issues that arise can be fixed in the stable release. The more users participate in beta testing, the more stable the non-beta releases will become.&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
== User interface (UI) &amp;quot;bugs&amp;quot; after the update ==&lt;br /&gt;
It is extremely common (and easily remedied) that after an important update, certain features of the user interface of X2Engine may not work properly. This is caused, in nearly all cases, by the web browser's cache, which contains out-of-date copies of X2Engine's essential user interface files. To solve these problems, the first step to try is clearing the cache. A step-by-step guide to doing so is given in [[wikihow:Clear-Your-Browser%27s-Cache]].&lt;br /&gt;
&lt;br /&gt;
== Errors in the updater itself == &lt;br /&gt;
When all else fails, the updater may have to be patched due to an irreconcilable backwards compatibility issue. Fortunately, if one has read/write access to the source files on the webserver, this is straightforward to fix. See [[#Refreshing_and_Updating_the_Updater_Utility|Refreshing and Updating the Updater Utility]].&lt;br /&gt;
&lt;br /&gt;
== Professional Edition not in effect after upgrading ==&lt;br /&gt;
This may be caused by Professional Edition not getting &amp;quot;switched on&amp;quot; properly, despite all the necessary filesystem and database changes already being in effect. First, to see if this scenario applies to you, look for a folder &amp;quot;&amp;lt;tt&amp;gt;reports&amp;lt;/tt&amp;gt;&amp;quot; inside &amp;lt;tt&amp;gt;protected/modules&amp;lt;/tt&amp;gt;, and a table &amp;lt;tt&amp;gt;x2_reports&amp;lt;/tt&amp;gt; in the X2Engine database. If you find these things, you can try the following to enable Professional Edition:&lt;br /&gt;
# Find the file &amp;lt;tt&amp;gt;constants.php&amp;lt;/tt&amp;gt; in the installation directory. Open it in a text editor, and look for the following line: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('PRO_VERSION') or define('PRO_VERSION',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; on that line, and save the file.&lt;br /&gt;
# Run the following SQL command (replacing &amp;lt;tt&amp;gt;{license key}&amp;lt;/tt&amp;gt; with your X2Engine license key) on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General Procedures to Try ==&lt;br /&gt;
It may sometimes be possible that the update did not finish cleanly. In such cases, it will be necessary to manually re-perform some of the final operations in the update, described in [[#Introduction|Introduction]]. &lt;br /&gt;
&lt;br /&gt;
=== Refresh the Source Code ===&lt;br /&gt;
This step is not really the best step to try first, as it is usually more time-consuming. It entails re-uploading the entire application, in order to reset all non-ephemeral (source code) files to what they should be in a fresh installation of X2Engine. To do this:&lt;br /&gt;
# Download the refresh package. There should be a download link for this in the ''Updater Settings'' page. Otherwise, if that doesn't work and you're using Open Source edition, use [https://x2planet.com/installs/refresh.zip this link], or if using Professional Edition, paste the following url (after replacing {key} with your license key) into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/refresh-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Upload the ''contents'' of the downloaded zip archive to the installation directory, overwriting existing files.&lt;br /&gt;
&lt;br /&gt;
=== Clear Caches ===&lt;br /&gt;
See &amp;quot;[[#Clearing_caches_and_refreshing_the_application|Clearing Caches...]]&amp;quot; for details.&lt;br /&gt;
&lt;br /&gt;
=== Regenerate the Configuration ===&lt;br /&gt;
Again, this should not be done unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a new installation at the same version as the one you updated to. Refer to [[#Editing_the_Configuration_File|Editing the Configuration File]].&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1844</id>
		<title>Software Updates and Upgrades</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1844"/>
				<updated>2014-06-24T21:15:05Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Disadvantages */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
[[File:Update_Process.svg|200px|thumb|right|Top-level overview of the update process]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
The following things happen in typical a software update (or upgrade):&lt;br /&gt;
* Files that are new in the new version are added&lt;br /&gt;
* Files that have been changed in the new version are replaced with the new versions of those files &lt;br /&gt;
* Files that are no longer used in the new version are deleted&lt;br /&gt;
* Changes to the database are made to make the database compatible with the new version of X2Engine&lt;br /&gt;
* The main configuration file, &amp;lt;tt&amp;gt;X2Config.php&amp;lt;/tt&amp;gt;, is edited to reflect the new state of the application.&lt;br /&gt;
* X2Engine data caches are cleared&lt;br /&gt;
&lt;br /&gt;
These operations are typically handled by the updater utility component [[x2doc:UpdaterBehavior|UpdaterBehavior]] and controlled via the in-app web-based updater or the command line updater. This article will cover both how to use said interfaces to the updater, and how to perform updates manually or with the &amp;quot;offline&amp;quot; method if the need arises, in addition to troubleshooting and recovering from an update that did not complete cleanly.&lt;br /&gt;
&lt;br /&gt;
= Disclaimer =&lt;br /&gt;
This article covers the standard and recommended update/upgrade methods, in addition to alternate methods and supplemental procedures. As general rules and precautions for using this article:&lt;br /&gt;
# ''If you do not fully and completely understand any step of any instructions given in this article, '''do not''' follow them''; ask for help instead on [http://x2community.com the forums] or through contacting X2Engine Technical Support if you are an existing customer.&lt;br /&gt;
# You should always make a backup of all of your files ''and'' a full backup copy of your database, preferably in a format (i.e. SQL script) that can be used to create a verbatim/carbon copy of the original database, with all of the features of the original schema, '''before''' attempting any steps of the update process manually.&lt;br /&gt;
# Some of the procedures outlined in this article involve manually creating or modifying files. Note that the same filesystem prerequisite for installation, namely that the file ownership must match the user under which PHP executes on the web server, applies to these steps.&lt;br /&gt;
&lt;br /&gt;
Furthermore, '''in regard to uploading files:''' note that any instructions to upload folders into X2Engine, and replace/merge contents, refer to replacing files in X2Engine with files in the archive at analogous paths. For example, if you have a zip file that contains a folder &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, and inside of that is a folder &amp;lt;tt&amp;gt;components&amp;lt;/tt&amp;gt;, and inside of that is a file &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt;, and the instructions are to upload/replace in X2Engine, you should upload &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt; to replace the existing file of the same name in &amp;lt;tt&amp;gt;protected/components&amp;lt;/tt&amp;gt; inside of X2Engine.&lt;br /&gt;
&lt;br /&gt;
X2Engine Inc. cannot be held accountable for damage/errors caused by any missteps or mistakes in manually-performed update operations. If in doubt, make a sandbox/clone of your X2Engine installation by copying the files and database to a different server, and test performing the operations on that before proceeding with your production install of X2Engine.&lt;br /&gt;
&lt;br /&gt;
= Update Notifications =&lt;br /&gt;
[[File:Update Notification.png|200px|frame|right|System flash message displaying software update notification.]]&lt;br /&gt;
To get notified of software updates, unless already using X2Engine Professional Edition, one must first &amp;quot;subscribe&amp;quot; to them. This can be done via checking the box &amp;quot;Notify me of software updates&amp;quot; in the installation form. If X2Engine is already installed, this can be done by going to &amp;quot;Updater Settings&amp;quot; from the Admin page and enabling them. If using Professional Edition, these steps are not not necessary.&lt;br /&gt;
&lt;br /&gt;
Once that has been completed, set the update notification interval as desired in &amp;quot;Updater Settings&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Update Methods =&lt;br /&gt;
There are, in essence, just three basic methods of updating, and variations on each. The three main methods are: &lt;br /&gt;
# web updater,&lt;br /&gt;
# command line updater, and&lt;br /&gt;
# manually.&lt;br /&gt;
In each case, an update package from the update data server is used as the authoritative list of changes to apply, but in the non-manual methods, X2Engine's own updater class does all the work applying changes from said package.&lt;br /&gt;
&lt;br /&gt;
In web and command line updates, there is also the option to first download and unpack the package manually (see [[#Performing_.22Offline.22_Updates|performing &amp;quot;offline&amp;quot; updates]]). This circumvents the need for the local server (on which X2Engine is hosted) to be connected to the internet. Similarly, in the case of manual updates, it is not necessary for the web server to actually have an internet connection.&lt;br /&gt;
&lt;br /&gt;
== Using the Web Updater ==&lt;br /&gt;
[[File:Updater-1.png|300px|thumb|right|The updater utility page.]]&lt;br /&gt;
The web updater can be accessed by any of the following methods:&lt;br /&gt;
* Using the link in the notification message at the top of the window in versions later than 3.5.5&lt;br /&gt;
* By going to the Admin page and clicking the &amp;quot;Update&amp;quot; button at the top, in earlier versions, with update notifications enabled&lt;br /&gt;
* Through &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
First, navigate to the web updater page, and '''do not''' cancel the request (stop or hit your browser's &amp;quot;back&amp;quot; button) until the page has loaded. If the updater utility has undergone changes and needs to self-update to be compatible with the software updates server, this is when it will do so. Thus, it is critical that you do not interrupt this process. If the page does not load, or displays the error message that dependencies could not be retrieved, you may have to [[#Performing_.22Offline.22_Updates|perform an &amp;quot;offline&amp;quot; update]].&lt;br /&gt;
&lt;br /&gt;
There are three ways of getting to the updater page. One is to simply click the link given in the update notification flash message, if available. The other is to click on the &amp;quot;Update&amp;quot; button at the top of the admin page. If software update notifications are not enabled, one can manually check for and install an update by going to &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
Next, make a backup of the database and files (recommended), follow the instructions on the page, and click the &amp;quot;Update&amp;quot; button when ready.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
It is generally quick and is the easiest method to use, and it does not require shell/SSH access to the web server.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
It is not recommended for use on large installations of X2Engine, i.e. containing over a million records, or for updating from very old versions. This is because the final update process may be cut short by server timeout or an interruption in the network connection. This can result in an incomplete/broken update that requires recovering manually (a potentially very long process).&lt;br /&gt;
&lt;br /&gt;
==== Explanation ====&lt;br /&gt;
The inherent weakness of the web updater (or any web-based updater to any web application) is that it must always rely upon web requests to the server, and it thus must perform extremely critical operations for the update all within the scope of a web request. Web requests, it goes without saying, are not always reliable. For example, one's internet connection might get cut off due being on a wireless network and encountering interference. Furthermore, if any operations take an exceedingly long amount of time, e.g. because of the size of the data set (and thus indexes) on which the update must operate, or the number of database changes that need to be applied, the web request for those operations may take longer than the PHP execution time limit as configured on the webserver. This would potentially cut short the execution of a critical update stage, and thus the update would require manual recovery at best, or completely restoring  X2Engine to a backup at worst.&lt;br /&gt;
&lt;br /&gt;
==== Ways to get around this ====&lt;br /&gt;
In most cases, including on shared hosting, it should be possible to change the time limit on PHP. It is highly recommended that you either disable the timeout or set it to a very generous value (such as 300 seconds) while attempting to update via the web updater, especially if you are many versions behind (i.e. on a version more than five months old) or have a very high record count in any of your modules.&lt;br /&gt;
&lt;br /&gt;
== Using the Command Line Updater ==&lt;br /&gt;
This update method uses all of the same thoroughly-tested underpinnings and processes as the web updater, but does so all within a system command to the Yii console.&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
While in via SSH, or in a terminal emulator, or in a script, run the following command in the &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt; subdirectory of X2Engine:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./yiic update app&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the following optional arguments to the command line updater:&lt;br /&gt;
;&amp;lt;tt&amp;gt;--force&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; for this option (i.e. &amp;lt;tt&amp;gt;--force=1&amp;lt;/tt&amp;gt;) to proceed with the update even if compatibility issues were detected. Default: 0 (halt if compatibility issues were detected).&lt;br /&gt;
;&amp;lt;tt&amp;gt;--backup&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that a database backup should be performed first before updating, &amp;quot;0&amp;quot; to proceed without creating a backup first. Default: 1.&lt;br /&gt;
;&amp;lt;tt&amp;gt;--lock&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that the application should be locked during the final installation of the update package to prevent data entry during the sensitive moments when the database is being altered and the files updated, &amp;quot;0&amp;quot; to permit use even when changes to the app are being applied. Default: 0.&lt;br /&gt;
&lt;br /&gt;
If running the command gives you the following message:&lt;br /&gt;
&amp;lt;pre&amp;gt;The updater is now up-to-date and compliant with the updates server. Re-run the command to proceed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
That indicates that the updater first needed to self-update, and succeeded in doing so. You should then run the same command again to complete the update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Safer to use on large, mission-critical systems. The success of a software update (or upgrade) does not hinge upon a single web request completing successfully.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
More difficult to use; requires shell access to the web server. The console command must be run as the same system user that owns all the files and directories of X2Engine, in order to circumvent permission issues when manipulating file system objects.&lt;br /&gt;
&lt;br /&gt;
== Using the Automatic Updater ==&lt;br /&gt;
[[File:Auto-Update.png|400px|thumb|right|Auto-update scheduler on the Updater Settings page]]&lt;br /&gt;
This method uses the command line updater, but runs the command on your behalf using the local cron daemon. To configure, go to the Updater Settings Page, check the box titled &amp;quot;Update Automatically&amp;quot;, and save. If you have Professional Edition, you can manage the update schedule in addition to other automated tasks from the Cron Table settings page, which can also be found under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Automates the entire process of updating.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
Only available on UNIX-like systems that have a cron daemon, and which permit user access to the cron table.&lt;br /&gt;
&lt;br /&gt;
== Performing &amp;quot;Offline&amp;quot; Updates ==&lt;br /&gt;
In some server environments, the updater may not be able to access the updates server by itself, due to (for example) the PHP or network configuration, or because the &amp;quot;zip&amp;quot; PHP extension (requried) is unavailable or has been disabled. It is in these situations necessary to download the update package manually, unpack on the server (or unpack and upload the files to the server), and then run the updater. When performing updates in this way, the updater will not need to access the updates server, but will use the manually-downloaded and extracted package files present in the local filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Refreshing and Updating the Updater Utility ===&lt;br /&gt;
Before starting, note that the updater itself may have undergone changes; the X2Engine update process is subject to change. To address this technical challenge, the updater utility within X2Engine would normally first attempt to download the latest version of itself, in order to be compatible with the updates server. However, this sometimes will not work properly. If your X2Engine installation is running in a hosting environment that prevents it from making outbound HTTP requests (the [[requirements:|requirements check script]] should tell you if this is the case), you will need to do this manually. Also, in version 3.5.5, there was a bug in the updater utility's self-update methods.&lt;br /&gt;
&lt;br /&gt;
To update the updater utility:&lt;br /&gt;
# '''Determine the version of the updater utility package''' by visiting [https://x2planet.com/installs/updates/updateCheck this link]. You should see a page that's entirely blank except for a version number.&lt;br /&gt;
# '''Download the updater package.''' In versions 3.6 and later, a download link for this should be available through the ''Updater Settings'' page. Otherwise, use the following URL (if in Open Source Edition): &amp;lt;pre&amp;gt;https://x2planet.com/installs/updater.zip&amp;lt;/pre&amp;gt; Otherwise, if using Professional Edition, paste the following url, after replacing {key} with your license key, into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/updater-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# '''Extract the downloaded zip archive and upload''' the &amp;quot;protected&amp;quot; folder to your server. Merge folders and overwrite existing files when prompted.&lt;br /&gt;
# '''Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor''' and find the line that looks like this:&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$updaterVersion = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# '''Change the text inside the pair of single quotes''' on that line so that it is the current updater version obtained in step #1. &lt;br /&gt;
# '''Save the file.'''&lt;br /&gt;
&lt;br /&gt;
=== Obtaining the Update Package ===&lt;br /&gt;
First, use the link in the ''Updater Settings'' page entitled &amp;quot;Latest Update Package for Version...&amp;quot;, to download the update package. If the link in the Updater Settings page does not work, use the following URL to download the update package (if using Open Source Edition), replacing &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; with your X2Engine installation's version:&lt;br /&gt;
&amp;lt;pre&amp;gt;https://x2planet.com/installs/updates/{version}/none&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should receive, from that URL, a zip archive file called &amp;quot;update.zip&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If using Professional Edition, and your download link is not working, contact [mailto:customersupport@x2engine.com customer support].&lt;br /&gt;
&lt;br /&gt;
=== Unpacking the Update ===&lt;br /&gt;
[[File:Update-in-Progress.png|200px|frame|right|System flash message displaying notification of pending update.]]&lt;br /&gt;
The update package will be a zip archive. The ''contents'' of this archive must be extracted ''into'' a folder called &amp;quot;update&amp;quot; in the installation directory. So, in other words, after extracting and uploading to the server, there should be a folder called &amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt; alongside &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;framework&amp;lt;/tt&amp;gt; (etc). Within that folder (&amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt;) should be the following contents:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A file containing important data to be used by the updater&lt;br /&gt;
;&amp;lt;tt&amp;gt;contents.md5&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A message digest file containing MD5 checksums of all files in the archive excluding itself.&lt;br /&gt;
;&amp;lt;tt&amp;gt;source&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A sub-folder/directory, whose internal structure mimics that of X2Engine, containing files that were changed in the update. If there were no files changed (i.e. only database changes were in the update), this directory will not be present.&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A directory containing flat data files. These files are typically not used by the updater, but are useful if performing manual updates or recovery.&lt;br /&gt;
&lt;br /&gt;
=== Applying the Update ===&lt;br /&gt;
[[File:Steps-post-Unpack.png|200px|frame|right|Steps of the update process in the event of manually-extracted package (or resuming a preexisting update)]]&lt;br /&gt;
After unpacking, if you run the command line updater, it should (if no problems arise, i.e. incompatible package version) apply the update straight away. Otherwise, log into X2Engine, and you should see a system flash message notifying you of an update in progress. This message shows up whenever there is an update package folder (&amp;quot;update&amp;quot;) present on the server. If you then go to the updater page, once the page has finished loading, you should see that there only remain the steps &amp;quot;Review and confirm changes&amp;quot;, &amp;quot;Apply changes&amp;quot; and &amp;quot;Reload&amp;quot; remain to be completed. To complete the update, when ready, click &amp;quot;Update&amp;quot;; otherwise, to delete the &amp;quot;update&amp;quot; folder and cancel the update, click &amp;quot;Cancel&amp;quot; either on the updater page or in the system flash message.&lt;br /&gt;
&lt;br /&gt;
== Performing Updates Manually ==&lt;br /&gt;
While it is preferable to avoid manually applying changes to X2Engine, and you may never need to, it may in some circumstance or another actually be necessary to do so. For all such situations, the raw update data files generated by the X2Engine update build process are bundled with every update package to help you perform the operations described in [[#Introduction|&amp;quot;Introduction&amp;quot;]]. This section will describe how to perform each of those operations. &lt;br /&gt;
&lt;br /&gt;
=== Prerequisites ===&lt;br /&gt;
First and foremost, all shell commands listed here, and in general, any operations that create or modify files, ''must be run as the same system user under which the server runs PHP scripts.'' Otherwise, the ownership and/or permissions of files and directories will have to be changed after completing all changes.&lt;br /&gt;
&lt;br /&gt;
To obtain the update package (which will be necessary), refer to &amp;quot;[[#Obtaining_the_Update_Package|Obtaining the Update Package]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Furthermore, it is strongly advised that you first make a back-up copy of all files and of X2Engine's database. Your database backup would best be a full, exact copy, such as one that would be made by [http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html mysqldump], or the built-in export tools of [http://www.phpmyadmin.net/home_page/index.php PHPMyAdmin].&lt;br /&gt;
&lt;br /&gt;
The X2Engine global import/export utility (&amp;quot;Import All Data&amp;quot;/&amp;quot;Export All Data&amp;quot;) in Admin '''is not recommended by itself as the sole backup tool'''. This is especially important to note because of how this tool cannot re-create custom fields; they must exist in the target installation before the import. However, using it to ''supplement'' your standard database backup method will prove useful in the event that you:&lt;br /&gt;
# don't have any custom fields,&lt;br /&gt;
# cannot manage to update X2Engine with success, and&lt;br /&gt;
# wish to start over with a fresh installation.&lt;br /&gt;
In such a case, you will then be able to import your old data into the new installation.&lt;br /&gt;
&lt;br /&gt;
=== Introduction: Data Files ===&lt;br /&gt;
In [[#Unpacking_the_Update|Unpacking the update]], a rough overview of an update package's contents were given. In this section, the anatomy and purpose of each of the data files will be introduced.&lt;br /&gt;
&lt;br /&gt;
Included in every update package is two copies of the following data:&lt;br /&gt;
* Files to copy&lt;br /&gt;
* Files to delete&lt;br /&gt;
* Database changes to enact&lt;br /&gt;
* Migration scripts to run, if any&lt;br /&gt;
&lt;br /&gt;
The first, immediately visible place where all of this data is stored is in the file &amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;, as a JSON-encoded array. See the next section for how to use this file.&lt;br /&gt;
&lt;br /&gt;
The second place, which can be useful to users who do not wish to automate or script any step of a manual update, is in the files contained in the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; directory. Each file is named after the version to which it corresponds, and contains the data required to update ''to'' that version ''from the version immediately preceding it.''' Thus, the versions you should see in each file name are the versions above your X2Engine installation's current version, up to and including the version being updated to.&lt;br /&gt;
&lt;br /&gt;
In an update, the directory will contain the following subdirectories:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of files to copy. The format is each line containing a file path in the list.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of obsolete files for that version which will need to be deleted. The format is the same as in &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts corresponding to each version. Each file is formatted such that it can be directly run on the database as an SQL script, although this is not generally recommended. The list of SQL commands to run is a of the following format: &lt;br /&gt;
  [query 1];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [query 2];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [etc]&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts with similar naming and formatting conventions to the &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; scripts. See [[#The_structure_of_the_.22data.22_array|The structure of the &amp;quot;data&amp;quot; array]] for explanation of the purpose of &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== How to use the manifest for updating manually ===&lt;br /&gt;
If you so choose, you may write a script to perform all of the operations for an update in one fell swoop. If so, and if the preferred scripting language of choice has a JSON-parsing library available, you will be able to save yourself a lot of effort by using the main data file (&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;) to load all of the necessary data for the update.&lt;br /&gt;
&lt;br /&gt;
==== The structure the manifest ====&lt;br /&gt;
In updates, the contents of the plain-text file manifest.json are a JSON-encoded object containing the following properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the data corresponding to each intermediate version. It is, in essence, all the data stored in the files in &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt;. If the update is for an installation of X2Engine that is only one version behind, it will contain only one element. Each element of this array is itself an array containing respective data for the intermediate version.&lt;br /&gt;
;&amp;lt;tt&amp;gt;versions&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array listing the version numbers of each intermediate version in the update. Similar to &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, this array will contain only one element if updating to the next version, and that version ''will be the next version''.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of deletions of obsolete files across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of files added or changed across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''from'', or in other words, the version of X2Engine before the package is applied. '''This must be the same as the version of installation to which the package is being applied.''' If this is not so, the updater utility would exit immediately, as applying the changes in the update would have potentially harmful results to data and/or compatibility with future updates.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''to'', or in other words, the version of X2Engine before the package is applied.&lt;br /&gt;
;&amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the version of the updater utility for which the package was compiled. If the structure of the update package or of the manifest changes, the updater must also change to accommodate and recognize the changes; else it might try to access properties that don't exist, or ignore important ones that do. This is the single property of manifest.json that should never change.&lt;br /&gt;
;&amp;lt;tt&amp;gt;scenario&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, which should be &amp;quot;update&amp;quot; if the package is for an update. In the case of upgrades, which use most of the same functionality of the updater, it should be &amp;quot;upgrade&amp;quot;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation after the package is applied. It should be &amp;quot;opensource&amp;quot; for open source edition, &amp;quot;pro&amp;quot; for professional edition. In the case of an upgrade package, it differs from the &amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation before the package is applied. &lt;br /&gt;
;&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An integer denoting the timestamp of the release date of the version of X2Engine denoted by &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The structure of the &amp;quot;data&amp;quot; array ====&lt;br /&gt;
In updates, each version's data, contained in each element of the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, contains the following nested array properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files added for the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files deleted in the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of SQL commands to bring the database structure and contents into compliance with the version from the previous version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A prerequisite list of SQL commands that should be run before those in &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, and if any fail, the failures should be ignored. These are added to updates whenever it is necessary to normalize the database schema, i.e. in the event that some installations' databases may not be consistent with others, but the commands might fail on every other system. These SQL commands were intended to be &amp;quot;attempted&amp;quot;; they are not guaranteed to succeed on all installations. An example of when this is necessary would be adding a foreign key constraint when in previous versions the storage engine was left to the database's default settings in old versions. MyISAM does not natively support foreign key constraints. Thus, to standardize this across all installations, including installations on servers where the storage engine defaulted to MyISAM, the constraint must first be dropped (in a &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; command) before the relevant tables are changed to the InnoDB storage engine, and the constraint is added back again, in the usual &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;version&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The version number&lt;br /&gt;
;&amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The edition&lt;br /&gt;
;&amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of PHP scripts that would be run in an update for this version. These are a subset of fileList and are typically used for advanced database operations, i.e. restructuring values of JSON-encoded fields, which are extremely impractical (or impossible) to perform using raw SQL commands. The files contained in the list all have paths that look like &amp;quot;&amp;lt;tt&amp;gt;protected/migrations/{version}/{timestamp}-name-of-operations.php&amp;lt;/tt&amp;gt;&amp;quot;. Note, if this array is not empty, you may need to reverse-engineer or otherwise copy and re-purpose/re-write any migration scripts listed in order to perform their necessary operations. This is because they may depend on running within a Yii Framework environment, i.e. will contain references to components of the Yii application singleton such as &amp;quot;db&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Note that in upgrades, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; are replaced by &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; (respectively), and &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; are missing (because they aren't necessary).&lt;br /&gt;
&lt;br /&gt;
==== What you will need for the update, if using the manifest ====&lt;br /&gt;
''Among this data, what you will need:'' &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, which will give you the full list of files that need to be deleted, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, which will give you the full list of files to copy from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory, &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;, which give you values with which to update the main X2Engine configuration file, and, within each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, the sub-array properties &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;, with which you will construct the list of database commands to run. &lt;br /&gt;
&lt;br /&gt;
A procedure for manually updating X2Engine, with data from manifest.json, thus might look like this:&lt;br /&gt;
# For each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; (or in other words, each version's changes):&lt;br /&gt;
## Run all the SQL commands in its corresponding &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; sub-array, ignoring errors&lt;br /&gt;
## Run all SQL commands in its &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; array&lt;br /&gt;
## Perform database operations that would be equivalent to those performed in migration scripts&lt;br /&gt;
# For each file in the cumulative list &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, copy it from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory into its analogous location in the installation directory of X2Engine, creating parent directories as necessary if they don't already exist.&lt;br /&gt;
# Delete all files in the cumulative list &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; from the installation directory&lt;br /&gt;
# Clear the data cache files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt; and the temporary asset folders in &amp;lt;tt&amp;gt;assets/&amp;lt;/tt&amp;gt;&lt;br /&gt;
# Clear all session data (&amp;lt;tt&amp;gt;x2_sessions&amp;lt;/tt&amp;gt;) and auth cache data (&amp;lt;tt&amp;gt;x2_auth_cache&amp;lt;/tt&amp;gt;).&lt;br /&gt;
# Update the main X2Engine configuration (&amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt;) file, setting:&lt;br /&gt;
## &amp;lt;tt&amp;gt;$buildDate&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt; property,&lt;br /&gt;
## &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt; property, and &lt;br /&gt;
## &amp;lt;tt&amp;gt;$updaterVersion&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
&lt;br /&gt;
=== Enacting Database Changes ===&lt;br /&gt;
Inside the &amp;lt;tt&amp;gt;update/sqlList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/sqlForce&amp;lt;/tt&amp;gt; folder is a list of &amp;lt;tt&amp;gt;.sql&amp;lt;/tt&amp;gt; files, named according to their corresponding version's update, that can essentially be run as SQL scripts to perform the update. Note, however, that '''if the update traverses more than one version, the scripts must be run in the order of their corresponding version.''' This is because, in the database changes for any version, it is assumed that the database schema and contents conform to the version that immediately preceded it. The PHP function [[phpfunc:version-compare|version_compare]], and the method &amp;quot;[http://pythonhosted.org/setuptools/pkg_resources.html#parsing-utilities parse_version]&amp;quot;, in the pkg_resources module of setuptools, should correctly identify which version should come first. Note, the [[#The_structure_the_manifest|&amp;quot;versions&amp;quot; property of the manifest]] will contain the list of versions in the correct order.&lt;br /&gt;
&lt;br /&gt;
Furthermore, note, &amp;quot;sqlList&amp;quot; and &amp;quot;sqlForce&amp;quot; may not encompass all necessary database changes; if there are any migration scripts in the update, the operations they perform will similarly need to be performed in the current update (see the description of &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; in [[#The_structure_of_the_.22data.22_array|&amp;quot;the structure of the 'data' array&amp;quot;]]).&lt;br /&gt;
&lt;br /&gt;
In summary, for each version traversed in the update, the following operations should be run in order, with &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version number of the version in question:&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlForce/{version}.sql&amp;lt;/tt&amp;gt; file,''' in the order they appear in the file. Ignore if any commands fail. Note, if passed to the MySQL client as a script, any failures might trigger a halt in execution and thus skip the remainder of the commands.&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlList/{version}.sql&amp;lt;/tt&amp;gt; file.'''&lt;br /&gt;
# '''Perform operations equivalent to those in each of the migration scripts, if any.''' The first part of a script file name is the timestamp it was added to the codebase, and this also dictates the order in which they should be run.&lt;br /&gt;
&lt;br /&gt;
=== Copying new files ===&lt;br /&gt;
Files added/changed in the new version are stored in the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory. In an update, they should be copied into the installation directory, replacing any existing files of the same name/path. If the &amp;lt;tt&amp;gt;rsync&amp;lt;/tt&amp;gt; utility is available on the system, you can use one command to safely copy these files into the installation directory as follows (executed in that same directory):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rsync -avc update/source/ ./&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, you will need to use &amp;lt;tt&amp;gt;cat&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;cp&amp;lt;/tt&amp;gt;, two utilities that should be available on every UNIX-like system, and the lists of files added in each intermediate version. These lists are stored in the folder &amp;lt;tt&amp;gt;update/data/fileList&amp;lt;/tt&amp;gt; and named like &amp;quot;{version}.txt&amp;quot; (&amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version to which it applies), each file on its own line in that file. Run the following command, ignoring errors:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/fileList/*; do for l in $(cat $f); do cp update/source/&amp;quot;$l&amp;quot; ./&amp;quot;$l&amp;quot; ; done ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting old files ===&lt;br /&gt;
Files to be deleted are listed in a similar manner as the files to be added: one file per line. The list of files to delete are located at files with paths that look like this: &amp;lt;tt&amp;gt;update/data/deletionList/{version}.txt&amp;lt;/tt&amp;gt;&lt;br /&gt;
So, for an update from 5.5 to 5.7 with intermediate version 5.6 (for instance), the files would be &amp;lt;tt&amp;gt;update/data/deletionList/5.6&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/data/deletionList/5.7&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is only one version through which to update, the operation is very simple, and can be performed in one UNIX/Linux (bash) shell command as follows (if the version is 3.6.3, for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
xargs -a update/data/deletionList/3.6.3.txt -d&amp;quot;\n&amp;quot; rm -fv&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, if there are multiple versions, this may be a more tricky proposition. The necessity arises to create a cumulative list, because there might be the possibility that a file is deleted in one version and re-established in a new version. Nevertheless, while this is possible, it is highly unlikely, and in most cases it is safe to simply iterate over all the &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; data files and delete all the files listed therein. As a bash command:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/deletionList/*; do xargs -a $f -d&amp;quot;\n&amp;quot; rm -fv ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Editing the Configuration File ===&lt;br /&gt;
This final step in the update process, before refreshing, should not be done manually unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a fresh installation at the same version as the one to which you have updated.&lt;br /&gt;
&lt;br /&gt;
To update the configuration manually:&lt;br /&gt;
# Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor&lt;br /&gt;
# Find the line with the &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; variable in it. It should look like this (but with the version from which you are updating): &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$version = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change the value in quotes to the current version being updated to, and save the file.&lt;br /&gt;
&lt;br /&gt;
=== Clearing caches and refreshing the application ===&lt;br /&gt;
It is generally always a good idea to clear the application's server-side cache and other ephemeral files/data after updating. This is taken care of automatically by the built-in updater, but barring proper use and functioning of that, the operations can be performed manually as follows: &lt;br /&gt;
;clear the data cache&lt;br /&gt;
: delete all &amp;quot;&amp;lt;tt&amp;gt;.bin&amp;lt;/tt&amp;gt;&amp;quot; files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -fv protected/runtime/cache/*.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear web assets&lt;br /&gt;
: delete all sub-folders of &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt; by running this command (in the installation directory): &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -r assets/*/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear the &amp;quot;auth cache&amp;quot;&lt;br /&gt;
: Flush the auth cache table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;&lt;br /&gt;
DELETE FROM `x2_auth_cache` WHERE 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;log all users out&lt;br /&gt;
: Flush the sessions table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;DELETE FROM `x2_sessions` WHERE 1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Upgrading =&lt;br /&gt;
&lt;br /&gt;
== Using the Web Interface ==&lt;br /&gt;
[[File:Software-Upgrade-Registration.png|400px|thumb|right|Software upgrade page, in the first step (registration)]]&lt;br /&gt;
To upgrade to Professional Edition&lt;br /&gt;
# Purchase X2Engine Professional Edition Download. Note the registration info in the activation email.&lt;br /&gt;
# Go to the Admin page in X2Engine and find &amp;quot;Upgrade X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot;.&lt;br /&gt;
# In the ''Upgrade'' page, enter your registration info into the form and click &amp;quot;Register&amp;quot; to continue. Once complete, you will see a list of changes similar to those displayed when performing an update.&lt;br /&gt;
# Review the changes to be applied and then proceed by clicking &amp;quot;Upgrade&amp;quot;, similar to performing an update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Offline&amp;quot; Upgrading ==&lt;br /&gt;
This works in exactly the same way as offline updating. However, to obtain your upgrade package, which you will extract into a folder called &amp;quot;update&amp;quot; on the X2Engine web server, contact [mailto:customersupport@x2engine.com customer support]. Furthermore, you will need to run the following database command on your X2Engine database when done with the upgrade, replacing &amp;lt;tt&amp;gt;{product key}&amp;lt;/tt&amp;gt; with your product key, to allow your installation to receive Professional Edition software updates:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{product key}' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Manually Upgrading ==&lt;br /&gt;
The process of upgrading is very similar to that of updating, and uses all of the same underpinnings as the updater. Thus, almost all of the above information and procedures will apply to upgrades. However, note the following crucial differences:&lt;br /&gt;
&lt;br /&gt;
* '''X2Engine must be at the latest available version in order to upgrade.'''&lt;br /&gt;
* '''To obtain the upgrade package, you must be an owner of an X2Engine Professional Edition Download-type license.''' Contact [mailto:customersupport@x2engine.com customer support] for further details.&lt;br /&gt;
* '''There will be only one &amp;quot;intermediate&amp;quot; version.''' The changes to apply will move X2Engine from a version to that same version, applying instead the necessary changes to switch its ''edition'' from Open Source to Professional.&lt;br /&gt;
* '''There are no file deletions.'''&lt;br /&gt;
* '''The properties of the package manifest differ slightly.''' In upgrades, the properties of the data object are &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, etc. Note also, the update data is in aptly-named sub-folders of &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt; as well, and there are no &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; lists to handle.&lt;br /&gt;
* '''The configuration is not changed.''' Instead, the value stored in the field &amp;lt;tt&amp;gt;unique_id&amp;lt;/tt&amp;gt; of the admin record in the database table &amp;lt;tt&amp;gt;x2_admin&amp;lt;/tt&amp;gt; must be changed to the license key, and the &amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt; field must be changed to &amp;quot;pro&amp;quot;. To perform these both, run the following command on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''It is not necessary to clear caches or user sessions when done.''' These operations are performed in updates to eliminate ephemeral (session-ingrained) data that may no longer be applicable and thus cause problems. Upgrades will rarely, if ever, invalidate this data.&lt;br /&gt;
&lt;br /&gt;
= Updating to Beta Versions = &lt;br /&gt;
Beginning at version 4.1.2, after much advocacy to adopt this practice, X2Engine began a two-phase release process in which each version would be released first as a beta, then to the broader public after initial bug finds. The purpose behind this is to avoid updating production installations of X2Engine to new versions that have had zero public exposure, and thus avoid introducing bugs that were not found in initial QA testing (due to a incompatibility with a particular server environment or configuration of X2Engine) into sensitive production environments.&lt;br /&gt;
&lt;br /&gt;
By default, the updater will not update to beta versions. The updater must be configured to use the proper URL on the update server for beta release data, by the following procedure:&lt;br /&gt;
# Create a file &amp;quot;constants-custom.php&amp;quot; in the root level of X2Engine. You can easily do this by renaming &amp;quot;constants-custom.example.php&amp;quot; to that name (remove &amp;quot;.custom&amp;quot;).&lt;br /&gt;
# Look for the following line, and in it, change &amp;quot;false&amp;quot; to &amp;quot;true&amp;quot;: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('X2_UPDATE_BETA') or define('X2_UPDATE_BETA',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The advantage of enabling beta releases is that new features will be available much sooner. Additionally, fixes to bugs will also be available sooner. The main disadvantage of enabling beta releases is that they are never guaranteed to be as stable as non-beta releases. We encourage users to clone their installations of X2Engine, if able, and use beta versions on them, and report any issues they encounter for their particular server environment/special configuration/custom implementation of X2Engine. This way, any issues that arise can be fixed in the stable release. The more users participate in beta testing, the more stable the non-beta releases will become.&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
== User interface (UI) &amp;quot;bugs&amp;quot; after the update ==&lt;br /&gt;
It is extremely common (and easily remedied) that after an important update, certain features of the user interface of X2Engine may not work properly. This is caused, in nearly all cases, by the web browser's cache, which contains out-of-date copies of X2Engine's essential user interface files. To solve these problems, the first step to try is clearing the cache. A step-by-step guide to doing so is given in [[wikihow:Clear-Your-Browser%27s-Cache]].&lt;br /&gt;
&lt;br /&gt;
== Errors in the updater itself == &lt;br /&gt;
When all else fails, the updater may have to be patched due to an irreconcilable backwards compatibility issue. Fortunately, if one has read/write access to the source files on the webserver, this is straightforward to fix. See [[#Refreshing_and_Updating_the_Updater_Utility|Refreshing and Updating the Updater Utility]].&lt;br /&gt;
&lt;br /&gt;
== Professional Edition not in effect after upgrading ==&lt;br /&gt;
This may be caused by Professional Edition not getting &amp;quot;switched on&amp;quot; properly, despite all the necessary filesystem and database changes already being in effect. First, to see if this scenario applies to you, look for a folder &amp;quot;&amp;lt;tt&amp;gt;reports&amp;lt;/tt&amp;gt;&amp;quot; inside &amp;lt;tt&amp;gt;protected/modules&amp;lt;/tt&amp;gt;, and a table &amp;lt;tt&amp;gt;x2_reports&amp;lt;/tt&amp;gt; in the X2Engine database. If you find these things, you can try the following to enable Professional Edition:&lt;br /&gt;
# Find the file &amp;lt;tt&amp;gt;constants.php&amp;lt;/tt&amp;gt; in the installation directory. Open it in a text editor, and look for the following line: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('PRO_VERSION') or define('PRO_VERSION',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; on that line, and save the file.&lt;br /&gt;
# Run the following SQL command (replacing &amp;lt;tt&amp;gt;{license key}&amp;lt;/tt&amp;gt; with your X2Engine license key) on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General Procedures to Try ==&lt;br /&gt;
It may sometimes be possible that the update did not finish cleanly. In such cases, it will be necessary to manually re-perform some of the final operations in the update, described in [[#Introduction|Introduction]]. &lt;br /&gt;
&lt;br /&gt;
=== Refresh the Source Code ===&lt;br /&gt;
This step is not really the best step to try first, as it is usually more time-consuming. It entails re-uploading the entire application, in order to reset all non-ephemeral (source code) files to what they should be in a fresh installation of X2Engine. To do this:&lt;br /&gt;
# Download the refresh package. There should be a download link for this in the ''Updater Settings'' page. Otherwise, if that doesn't work and you're using Open Source edition, use [https://x2planet.com/installs/refresh.zip this link], or if using Professional Edition, paste the following url (after replacing {key} with your license key) into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/refresh-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Upload the ''contents'' of the downloaded zip archive to the installation directory, overwriting existing files.&lt;br /&gt;
&lt;br /&gt;
=== Clear Caches ===&lt;br /&gt;
See &amp;quot;[[#Clearing_caches_and_refreshing_the_application|Clearing Caches...]]&amp;quot; for details.&lt;br /&gt;
&lt;br /&gt;
=== Regenerate the Configuration ===&lt;br /&gt;
Again, this should not be done unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a new installation at the same version as the one you updated to. Refer to [[#Editing_the_Configuration_File|Editing the Configuration File]].&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1843</id>
		<title>Software Updates and Upgrades</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1843"/>
				<updated>2014-06-24T21:12:28Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Ways to get around this */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
[[File:Update_Process.svg|200px|thumb|right|Top-level overview of the update process]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
The following things happen in typical a software update (or upgrade):&lt;br /&gt;
* Files that are new in the new version are added&lt;br /&gt;
* Files that have been changed in the new version are replaced with the new versions of those files &lt;br /&gt;
* Files that are no longer used in the new version are deleted&lt;br /&gt;
* Changes to the database are made to make the database compatible with the new version of X2Engine&lt;br /&gt;
* The main configuration file, &amp;lt;tt&amp;gt;X2Config.php&amp;lt;/tt&amp;gt;, is edited to reflect the new state of the application.&lt;br /&gt;
* X2Engine data caches are cleared&lt;br /&gt;
&lt;br /&gt;
These operations are typically handled by the updater utility component [[x2doc:UpdaterBehavior|UpdaterBehavior]] and controlled via the in-app web-based updater or the command line updater. This article will cover both how to use said interfaces to the updater, and how to perform updates manually or with the &amp;quot;offline&amp;quot; method if the need arises, in addition to troubleshooting and recovering from an update that did not complete cleanly.&lt;br /&gt;
&lt;br /&gt;
= Disclaimer =&lt;br /&gt;
This article covers the standard and recommended update/upgrade methods, in addition to alternate methods and supplemental procedures. As general rules and precautions for using this article:&lt;br /&gt;
# ''If you do not fully and completely understand any step of any instructions given in this article, '''do not''' follow them''; ask for help instead on [http://x2community.com the forums] or through contacting X2Engine Technical Support if you are an existing customer.&lt;br /&gt;
# You should always make a backup of all of your files ''and'' a full backup copy of your database, preferably in a format (i.e. SQL script) that can be used to create a verbatim/carbon copy of the original database, with all of the features of the original schema, '''before''' attempting any steps of the update process manually.&lt;br /&gt;
# Some of the procedures outlined in this article involve manually creating or modifying files. Note that the same filesystem prerequisite for installation, namely that the file ownership must match the user under which PHP executes on the web server, applies to these steps.&lt;br /&gt;
&lt;br /&gt;
Furthermore, '''in regard to uploading files:''' note that any instructions to upload folders into X2Engine, and replace/merge contents, refer to replacing files in X2Engine with files in the archive at analogous paths. For example, if you have a zip file that contains a folder &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, and inside of that is a folder &amp;lt;tt&amp;gt;components&amp;lt;/tt&amp;gt;, and inside of that is a file &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt;, and the instructions are to upload/replace in X2Engine, you should upload &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt; to replace the existing file of the same name in &amp;lt;tt&amp;gt;protected/components&amp;lt;/tt&amp;gt; inside of X2Engine.&lt;br /&gt;
&lt;br /&gt;
X2Engine Inc. cannot be held accountable for damage/errors caused by any missteps or mistakes in manually-performed update operations. If in doubt, make a sandbox/clone of your X2Engine installation by copying the files and database to a different server, and test performing the operations on that before proceeding with your production install of X2Engine.&lt;br /&gt;
&lt;br /&gt;
= Update Notifications =&lt;br /&gt;
[[File:Update Notification.png|200px|frame|right|System flash message displaying software update notification.]]&lt;br /&gt;
To get notified of software updates, unless already using X2Engine Professional Edition, one must first &amp;quot;subscribe&amp;quot; to them. This can be done via checking the box &amp;quot;Notify me of software updates&amp;quot; in the installation form. If X2Engine is already installed, this can be done by going to &amp;quot;Updater Settings&amp;quot; from the Admin page and enabling them. If using Professional Edition, these steps are not not necessary.&lt;br /&gt;
&lt;br /&gt;
Once that has been completed, set the update notification interval as desired in &amp;quot;Updater Settings&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Update Methods =&lt;br /&gt;
There are, in essence, just three basic methods of updating, and variations on each. The three main methods are: &lt;br /&gt;
# web updater,&lt;br /&gt;
# command line updater, and&lt;br /&gt;
# manually.&lt;br /&gt;
In each case, an update package from the update data server is used as the authoritative list of changes to apply, but in the non-manual methods, X2Engine's own updater class does all the work applying changes from said package.&lt;br /&gt;
&lt;br /&gt;
In web and command line updates, there is also the option to first download and unpack the package manually (see [[#Performing_.22Offline.22_Updates|performing &amp;quot;offline&amp;quot; updates]]). This circumvents the need for the local server (on which X2Engine is hosted) to be connected to the internet. Similarly, in the case of manual updates, it is not necessary for the web server to actually have an internet connection.&lt;br /&gt;
&lt;br /&gt;
== Using the Web Updater ==&lt;br /&gt;
[[File:Updater-1.png|300px|thumb|right|The updater utility page.]]&lt;br /&gt;
The web updater can be accessed by any of the following methods:&lt;br /&gt;
* Using the link in the notification message at the top of the window in versions later than 3.5.5&lt;br /&gt;
* By going to the Admin page and clicking the &amp;quot;Update&amp;quot; button at the top, in earlier versions, with update notifications enabled&lt;br /&gt;
* Through &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
First, navigate to the web updater page, and '''do not''' cancel the request (stop or hit your browser's &amp;quot;back&amp;quot; button) until the page has loaded. If the updater utility has undergone changes and needs to self-update to be compatible with the software updates server, this is when it will do so. Thus, it is critical that you do not interrupt this process. If the page does not load, or displays the error message that dependencies could not be retrieved, you may have to [[#Performing_.22Offline.22_Updates|perform an &amp;quot;offline&amp;quot; update]].&lt;br /&gt;
&lt;br /&gt;
There are three ways of getting to the updater page. One is to simply click the link given in the update notification flash message, if available. The other is to click on the &amp;quot;Update&amp;quot; button at the top of the admin page. If software update notifications are not enabled, one can manually check for and install an update by going to &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
Next, make a backup of the database and files (recommended), follow the instructions on the page, and click the &amp;quot;Update&amp;quot; button when ready.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
It is generally quick and is the easiest method to use, and it does not require shell/SSH access to the web server.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
It is not recommended for use on large installations of X2Engine, i.e. containing over a million records, or for updating from very old versions.&lt;br /&gt;
&lt;br /&gt;
==== Explanation ====&lt;br /&gt;
The inherent weakness of the web updater (or any web-based updater to any web application) is that it must always rely upon web requests to the server, and it thus must perform extremely critical operations for the update all within the scope of a web request. Web requests, it goes without saying, are not always reliable. For example, one's internet connection might get cut off due being on a wireless network and encountering interference. Furthermore, if any operations take an exceedingly long amount of time, e.g. because of the size of the data set (and thus indexes) on which the update must operate, or the number of database changes that need to be applied, the web request for those operations may take longer than the PHP execution time limit as configured on the webserver. This would potentially cut short the execution of a critical update stage, and thus the update would require manual recovery at best, or completely restoring  X2Engine to a backup at worst.&lt;br /&gt;
&lt;br /&gt;
==== Ways to get around this ====&lt;br /&gt;
In most cases, including on shared hosting, it should be possible to change the time limit on PHP. It is highly recommended that you either disable the timeout or set it to a very generous value (such as 300 seconds) while attempting to update via the web updater, especially if you are many versions behind (i.e. on a version more than five months old) or have a very high record count in any of your modules.&lt;br /&gt;
&lt;br /&gt;
== Using the Command Line Updater ==&lt;br /&gt;
This update method uses all of the same thoroughly-tested underpinnings and processes as the web updater, but does so all within a system command to the Yii console.&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
While in via SSH, or in a terminal emulator, or in a script, run the following command in the &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt; subdirectory of X2Engine:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./yiic update app&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the following optional arguments to the command line updater:&lt;br /&gt;
;&amp;lt;tt&amp;gt;--force&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; for this option (i.e. &amp;lt;tt&amp;gt;--force=1&amp;lt;/tt&amp;gt;) to proceed with the update even if compatibility issues were detected. Default: 0 (halt if compatibility issues were detected).&lt;br /&gt;
;&amp;lt;tt&amp;gt;--backup&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that a database backup should be performed first before updating, &amp;quot;0&amp;quot; to proceed without creating a backup first. Default: 1.&lt;br /&gt;
;&amp;lt;tt&amp;gt;--lock&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that the application should be locked during the final installation of the update package to prevent data entry during the sensitive moments when the database is being altered and the files updated, &amp;quot;0&amp;quot; to permit use even when changes to the app are being applied. Default: 0.&lt;br /&gt;
&lt;br /&gt;
If running the command gives you the following message:&lt;br /&gt;
&amp;lt;pre&amp;gt;The updater is now up-to-date and compliant with the updates server. Re-run the command to proceed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
That indicates that the updater first needed to self-update, and succeeded in doing so. You should then run the same command again to complete the update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Safer to use on large, mission-critical systems. The success of a software update (or upgrade) does not hinge upon a single web request completing successfully.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
More difficult to use; requires shell access to the web server. The console command must be run as the same system user that owns all the files and directories of X2Engine, in order to circumvent permission issues when manipulating file system objects.&lt;br /&gt;
&lt;br /&gt;
== Using the Automatic Updater ==&lt;br /&gt;
[[File:Auto-Update.png|400px|thumb|right|Auto-update scheduler on the Updater Settings page]]&lt;br /&gt;
This method uses the command line updater, but runs the command on your behalf using the local cron daemon. To configure, go to the Updater Settings Page, check the box titled &amp;quot;Update Automatically&amp;quot;, and save. If you have Professional Edition, you can manage the update schedule in addition to other automated tasks from the Cron Table settings page, which can also be found under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Automates the entire process of updating.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
Only available on UNIX-like systems that have a cron daemon, and which permit user access to the cron table.&lt;br /&gt;
&lt;br /&gt;
== Performing &amp;quot;Offline&amp;quot; Updates ==&lt;br /&gt;
In some server environments, the updater may not be able to access the updates server by itself, due to (for example) the PHP or network configuration, or because the &amp;quot;zip&amp;quot; PHP extension (requried) is unavailable or has been disabled. It is in these situations necessary to download the update package manually, unpack on the server (or unpack and upload the files to the server), and then run the updater. When performing updates in this way, the updater will not need to access the updates server, but will use the manually-downloaded and extracted package files present in the local filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Refreshing and Updating the Updater Utility ===&lt;br /&gt;
Before starting, note that the updater itself may have undergone changes; the X2Engine update process is subject to change. To address this technical challenge, the updater utility within X2Engine would normally first attempt to download the latest version of itself, in order to be compatible with the updates server. However, this sometimes will not work properly. If your X2Engine installation is running in a hosting environment that prevents it from making outbound HTTP requests (the [[requirements:|requirements check script]] should tell you if this is the case), you will need to do this manually. Also, in version 3.5.5, there was a bug in the updater utility's self-update methods.&lt;br /&gt;
&lt;br /&gt;
To update the updater utility:&lt;br /&gt;
# '''Determine the version of the updater utility package''' by visiting [https://x2planet.com/installs/updates/updateCheck this link]. You should see a page that's entirely blank except for a version number.&lt;br /&gt;
# '''Download the updater package.''' In versions 3.6 and later, a download link for this should be available through the ''Updater Settings'' page. Otherwise, use the following URL (if in Open Source Edition): &amp;lt;pre&amp;gt;https://x2planet.com/installs/updater.zip&amp;lt;/pre&amp;gt; Otherwise, if using Professional Edition, paste the following url, after replacing {key} with your license key, into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/updater-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# '''Extract the downloaded zip archive and upload''' the &amp;quot;protected&amp;quot; folder to your server. Merge folders and overwrite existing files when prompted.&lt;br /&gt;
# '''Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor''' and find the line that looks like this:&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$updaterVersion = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# '''Change the text inside the pair of single quotes''' on that line so that it is the current updater version obtained in step #1. &lt;br /&gt;
# '''Save the file.'''&lt;br /&gt;
&lt;br /&gt;
=== Obtaining the Update Package ===&lt;br /&gt;
First, use the link in the ''Updater Settings'' page entitled &amp;quot;Latest Update Package for Version...&amp;quot;, to download the update package. If the link in the Updater Settings page does not work, use the following URL to download the update package (if using Open Source Edition), replacing &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; with your X2Engine installation's version:&lt;br /&gt;
&amp;lt;pre&amp;gt;https://x2planet.com/installs/updates/{version}/none&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should receive, from that URL, a zip archive file called &amp;quot;update.zip&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If using Professional Edition, and your download link is not working, contact [mailto:customersupport@x2engine.com customer support].&lt;br /&gt;
&lt;br /&gt;
=== Unpacking the Update ===&lt;br /&gt;
[[File:Update-in-Progress.png|200px|frame|right|System flash message displaying notification of pending update.]]&lt;br /&gt;
The update package will be a zip archive. The ''contents'' of this archive must be extracted ''into'' a folder called &amp;quot;update&amp;quot; in the installation directory. So, in other words, after extracting and uploading to the server, there should be a folder called &amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt; alongside &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;framework&amp;lt;/tt&amp;gt; (etc). Within that folder (&amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt;) should be the following contents:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A file containing important data to be used by the updater&lt;br /&gt;
;&amp;lt;tt&amp;gt;contents.md5&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A message digest file containing MD5 checksums of all files in the archive excluding itself.&lt;br /&gt;
;&amp;lt;tt&amp;gt;source&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A sub-folder/directory, whose internal structure mimics that of X2Engine, containing files that were changed in the update. If there were no files changed (i.e. only database changes were in the update), this directory will not be present.&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A directory containing flat data files. These files are typically not used by the updater, but are useful if performing manual updates or recovery.&lt;br /&gt;
&lt;br /&gt;
=== Applying the Update ===&lt;br /&gt;
[[File:Steps-post-Unpack.png|200px|frame|right|Steps of the update process in the event of manually-extracted package (or resuming a preexisting update)]]&lt;br /&gt;
After unpacking, if you run the command line updater, it should (if no problems arise, i.e. incompatible package version) apply the update straight away. Otherwise, log into X2Engine, and you should see a system flash message notifying you of an update in progress. This message shows up whenever there is an update package folder (&amp;quot;update&amp;quot;) present on the server. If you then go to the updater page, once the page has finished loading, you should see that there only remain the steps &amp;quot;Review and confirm changes&amp;quot;, &amp;quot;Apply changes&amp;quot; and &amp;quot;Reload&amp;quot; remain to be completed. To complete the update, when ready, click &amp;quot;Update&amp;quot;; otherwise, to delete the &amp;quot;update&amp;quot; folder and cancel the update, click &amp;quot;Cancel&amp;quot; either on the updater page or in the system flash message.&lt;br /&gt;
&lt;br /&gt;
== Performing Updates Manually ==&lt;br /&gt;
While it is preferable to avoid manually applying changes to X2Engine, and you may never need to, it may in some circumstance or another actually be necessary to do so. For all such situations, the raw update data files generated by the X2Engine update build process are bundled with every update package to help you perform the operations described in [[#Introduction|&amp;quot;Introduction&amp;quot;]]. This section will describe how to perform each of those operations. &lt;br /&gt;
&lt;br /&gt;
=== Prerequisites ===&lt;br /&gt;
First and foremost, all shell commands listed here, and in general, any operations that create or modify files, ''must be run as the same system user under which the server runs PHP scripts.'' Otherwise, the ownership and/or permissions of files and directories will have to be changed after completing all changes.&lt;br /&gt;
&lt;br /&gt;
To obtain the update package (which will be necessary), refer to &amp;quot;[[#Obtaining_the_Update_Package|Obtaining the Update Package]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Furthermore, it is strongly advised that you first make a back-up copy of all files and of X2Engine's database. Your database backup would best be a full, exact copy, such as one that would be made by [http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html mysqldump], or the built-in export tools of [http://www.phpmyadmin.net/home_page/index.php PHPMyAdmin].&lt;br /&gt;
&lt;br /&gt;
The X2Engine global import/export utility (&amp;quot;Import All Data&amp;quot;/&amp;quot;Export All Data&amp;quot;) in Admin '''is not recommended by itself as the sole backup tool'''. This is especially important to note because of how this tool cannot re-create custom fields; they must exist in the target installation before the import. However, using it to ''supplement'' your standard database backup method will prove useful in the event that you:&lt;br /&gt;
# don't have any custom fields,&lt;br /&gt;
# cannot manage to update X2Engine with success, and&lt;br /&gt;
# wish to start over with a fresh installation.&lt;br /&gt;
In such a case, you will then be able to import your old data into the new installation.&lt;br /&gt;
&lt;br /&gt;
=== Introduction: Data Files ===&lt;br /&gt;
In [[#Unpacking_the_Update|Unpacking the update]], a rough overview of an update package's contents were given. In this section, the anatomy and purpose of each of the data files will be introduced.&lt;br /&gt;
&lt;br /&gt;
Included in every update package is two copies of the following data:&lt;br /&gt;
* Files to copy&lt;br /&gt;
* Files to delete&lt;br /&gt;
* Database changes to enact&lt;br /&gt;
* Migration scripts to run, if any&lt;br /&gt;
&lt;br /&gt;
The first, immediately visible place where all of this data is stored is in the file &amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;, as a JSON-encoded array. See the next section for how to use this file.&lt;br /&gt;
&lt;br /&gt;
The second place, which can be useful to users who do not wish to automate or script any step of a manual update, is in the files contained in the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; directory. Each file is named after the version to which it corresponds, and contains the data required to update ''to'' that version ''from the version immediately preceding it.''' Thus, the versions you should see in each file name are the versions above your X2Engine installation's current version, up to and including the version being updated to.&lt;br /&gt;
&lt;br /&gt;
In an update, the directory will contain the following subdirectories:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of files to copy. The format is each line containing a file path in the list.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of obsolete files for that version which will need to be deleted. The format is the same as in &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts corresponding to each version. Each file is formatted such that it can be directly run on the database as an SQL script, although this is not generally recommended. The list of SQL commands to run is a of the following format: &lt;br /&gt;
  [query 1];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [query 2];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [etc]&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts with similar naming and formatting conventions to the &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; scripts. See [[#The_structure_of_the_.22data.22_array|The structure of the &amp;quot;data&amp;quot; array]] for explanation of the purpose of &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== How to use the manifest for updating manually ===&lt;br /&gt;
If you so choose, you may write a script to perform all of the operations for an update in one fell swoop. If so, and if the preferred scripting language of choice has a JSON-parsing library available, you will be able to save yourself a lot of effort by using the main data file (&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;) to load all of the necessary data for the update.&lt;br /&gt;
&lt;br /&gt;
==== The structure the manifest ====&lt;br /&gt;
In updates, the contents of the plain-text file manifest.json are a JSON-encoded object containing the following properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the data corresponding to each intermediate version. It is, in essence, all the data stored in the files in &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt;. If the update is for an installation of X2Engine that is only one version behind, it will contain only one element. Each element of this array is itself an array containing respective data for the intermediate version.&lt;br /&gt;
;&amp;lt;tt&amp;gt;versions&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array listing the version numbers of each intermediate version in the update. Similar to &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, this array will contain only one element if updating to the next version, and that version ''will be the next version''.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of deletions of obsolete files across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of files added or changed across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''from'', or in other words, the version of X2Engine before the package is applied. '''This must be the same as the version of installation to which the package is being applied.''' If this is not so, the updater utility would exit immediately, as applying the changes in the update would have potentially harmful results to data and/or compatibility with future updates.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''to'', or in other words, the version of X2Engine before the package is applied.&lt;br /&gt;
;&amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the version of the updater utility for which the package was compiled. If the structure of the update package or of the manifest changes, the updater must also change to accommodate and recognize the changes; else it might try to access properties that don't exist, or ignore important ones that do. This is the single property of manifest.json that should never change.&lt;br /&gt;
;&amp;lt;tt&amp;gt;scenario&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, which should be &amp;quot;update&amp;quot; if the package is for an update. In the case of upgrades, which use most of the same functionality of the updater, it should be &amp;quot;upgrade&amp;quot;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation after the package is applied. It should be &amp;quot;opensource&amp;quot; for open source edition, &amp;quot;pro&amp;quot; for professional edition. In the case of an upgrade package, it differs from the &amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation before the package is applied. &lt;br /&gt;
;&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An integer denoting the timestamp of the release date of the version of X2Engine denoted by &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The structure of the &amp;quot;data&amp;quot; array ====&lt;br /&gt;
In updates, each version's data, contained in each element of the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, contains the following nested array properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files added for the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files deleted in the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of SQL commands to bring the database structure and contents into compliance with the version from the previous version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A prerequisite list of SQL commands that should be run before those in &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, and if any fail, the failures should be ignored. These are added to updates whenever it is necessary to normalize the database schema, i.e. in the event that some installations' databases may not be consistent with others, but the commands might fail on every other system. These SQL commands were intended to be &amp;quot;attempted&amp;quot;; they are not guaranteed to succeed on all installations. An example of when this is necessary would be adding a foreign key constraint when in previous versions the storage engine was left to the database's default settings in old versions. MyISAM does not natively support foreign key constraints. Thus, to standardize this across all installations, including installations on servers where the storage engine defaulted to MyISAM, the constraint must first be dropped (in a &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; command) before the relevant tables are changed to the InnoDB storage engine, and the constraint is added back again, in the usual &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;version&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The version number&lt;br /&gt;
;&amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The edition&lt;br /&gt;
;&amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of PHP scripts that would be run in an update for this version. These are a subset of fileList and are typically used for advanced database operations, i.e. restructuring values of JSON-encoded fields, which are extremely impractical (or impossible) to perform using raw SQL commands. The files contained in the list all have paths that look like &amp;quot;&amp;lt;tt&amp;gt;protected/migrations/{version}/{timestamp}-name-of-operations.php&amp;lt;/tt&amp;gt;&amp;quot;. Note, if this array is not empty, you may need to reverse-engineer or otherwise copy and re-purpose/re-write any migration scripts listed in order to perform their necessary operations. This is because they may depend on running within a Yii Framework environment, i.e. will contain references to components of the Yii application singleton such as &amp;quot;db&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Note that in upgrades, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; are replaced by &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; (respectively), and &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; are missing (because they aren't necessary).&lt;br /&gt;
&lt;br /&gt;
==== What you will need for the update, if using the manifest ====&lt;br /&gt;
''Among this data, what you will need:'' &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, which will give you the full list of files that need to be deleted, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, which will give you the full list of files to copy from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory, &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;, which give you values with which to update the main X2Engine configuration file, and, within each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, the sub-array properties &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;, with which you will construct the list of database commands to run. &lt;br /&gt;
&lt;br /&gt;
A procedure for manually updating X2Engine, with data from manifest.json, thus might look like this:&lt;br /&gt;
# For each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; (or in other words, each version's changes):&lt;br /&gt;
## Run all the SQL commands in its corresponding &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; sub-array, ignoring errors&lt;br /&gt;
## Run all SQL commands in its &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; array&lt;br /&gt;
## Perform database operations that would be equivalent to those performed in migration scripts&lt;br /&gt;
# For each file in the cumulative list &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, copy it from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory into its analogous location in the installation directory of X2Engine, creating parent directories as necessary if they don't already exist.&lt;br /&gt;
# Delete all files in the cumulative list &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; from the installation directory&lt;br /&gt;
# Clear the data cache files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt; and the temporary asset folders in &amp;lt;tt&amp;gt;assets/&amp;lt;/tt&amp;gt;&lt;br /&gt;
# Clear all session data (&amp;lt;tt&amp;gt;x2_sessions&amp;lt;/tt&amp;gt;) and auth cache data (&amp;lt;tt&amp;gt;x2_auth_cache&amp;lt;/tt&amp;gt;).&lt;br /&gt;
# Update the main X2Engine configuration (&amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt;) file, setting:&lt;br /&gt;
## &amp;lt;tt&amp;gt;$buildDate&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt; property,&lt;br /&gt;
## &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt; property, and &lt;br /&gt;
## &amp;lt;tt&amp;gt;$updaterVersion&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
&lt;br /&gt;
=== Enacting Database Changes ===&lt;br /&gt;
Inside the &amp;lt;tt&amp;gt;update/sqlList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/sqlForce&amp;lt;/tt&amp;gt; folder is a list of &amp;lt;tt&amp;gt;.sql&amp;lt;/tt&amp;gt; files, named according to their corresponding version's update, that can essentially be run as SQL scripts to perform the update. Note, however, that '''if the update traverses more than one version, the scripts must be run in the order of their corresponding version.''' This is because, in the database changes for any version, it is assumed that the database schema and contents conform to the version that immediately preceded it. The PHP function [[phpfunc:version-compare|version_compare]], and the method &amp;quot;[http://pythonhosted.org/setuptools/pkg_resources.html#parsing-utilities parse_version]&amp;quot;, in the pkg_resources module of setuptools, should correctly identify which version should come first. Note, the [[#The_structure_the_manifest|&amp;quot;versions&amp;quot; property of the manifest]] will contain the list of versions in the correct order.&lt;br /&gt;
&lt;br /&gt;
Furthermore, note, &amp;quot;sqlList&amp;quot; and &amp;quot;sqlForce&amp;quot; may not encompass all necessary database changes; if there are any migration scripts in the update, the operations they perform will similarly need to be performed in the current update (see the description of &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; in [[#The_structure_of_the_.22data.22_array|&amp;quot;the structure of the 'data' array&amp;quot;]]).&lt;br /&gt;
&lt;br /&gt;
In summary, for each version traversed in the update, the following operations should be run in order, with &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version number of the version in question:&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlForce/{version}.sql&amp;lt;/tt&amp;gt; file,''' in the order they appear in the file. Ignore if any commands fail. Note, if passed to the MySQL client as a script, any failures might trigger a halt in execution and thus skip the remainder of the commands.&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlList/{version}.sql&amp;lt;/tt&amp;gt; file.'''&lt;br /&gt;
# '''Perform operations equivalent to those in each of the migration scripts, if any.''' The first part of a script file name is the timestamp it was added to the codebase, and this also dictates the order in which they should be run.&lt;br /&gt;
&lt;br /&gt;
=== Copying new files ===&lt;br /&gt;
Files added/changed in the new version are stored in the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory. In an update, they should be copied into the installation directory, replacing any existing files of the same name/path. If the &amp;lt;tt&amp;gt;rsync&amp;lt;/tt&amp;gt; utility is available on the system, you can use one command to safely copy these files into the installation directory as follows (executed in that same directory):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rsync -avc update/source/ ./&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, you will need to use &amp;lt;tt&amp;gt;cat&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;cp&amp;lt;/tt&amp;gt;, two utilities that should be available on every UNIX-like system, and the lists of files added in each intermediate version. These lists are stored in the folder &amp;lt;tt&amp;gt;update/data/fileList&amp;lt;/tt&amp;gt; and named like &amp;quot;{version}.txt&amp;quot; (&amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version to which it applies), each file on its own line in that file. Run the following command, ignoring errors:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/fileList/*; do for l in $(cat $f); do cp update/source/&amp;quot;$l&amp;quot; ./&amp;quot;$l&amp;quot; ; done ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting old files ===&lt;br /&gt;
Files to be deleted are listed in a similar manner as the files to be added: one file per line. The list of files to delete are located at files with paths that look like this: &amp;lt;tt&amp;gt;update/data/deletionList/{version}.txt&amp;lt;/tt&amp;gt;&lt;br /&gt;
So, for an update from 5.5 to 5.7 with intermediate version 5.6 (for instance), the files would be &amp;lt;tt&amp;gt;update/data/deletionList/5.6&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/data/deletionList/5.7&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is only one version through which to update, the operation is very simple, and can be performed in one UNIX/Linux (bash) shell command as follows (if the version is 3.6.3, for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
xargs -a update/data/deletionList/3.6.3.txt -d&amp;quot;\n&amp;quot; rm -fv&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, if there are multiple versions, this may be a more tricky proposition. The necessity arises to create a cumulative list, because there might be the possibility that a file is deleted in one version and re-established in a new version. Nevertheless, while this is possible, it is highly unlikely, and in most cases it is safe to simply iterate over all the &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; data files and delete all the files listed therein. As a bash command:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/deletionList/*; do xargs -a $f -d&amp;quot;\n&amp;quot; rm -fv ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Editing the Configuration File ===&lt;br /&gt;
This final step in the update process, before refreshing, should not be done manually unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a fresh installation at the same version as the one to which you have updated.&lt;br /&gt;
&lt;br /&gt;
To update the configuration manually:&lt;br /&gt;
# Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor&lt;br /&gt;
# Find the line with the &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; variable in it. It should look like this (but with the version from which you are updating): &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$version = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change the value in quotes to the current version being updated to, and save the file.&lt;br /&gt;
&lt;br /&gt;
=== Clearing caches and refreshing the application ===&lt;br /&gt;
It is generally always a good idea to clear the application's server-side cache and other ephemeral files/data after updating. This is taken care of automatically by the built-in updater, but barring proper use and functioning of that, the operations can be performed manually as follows: &lt;br /&gt;
;clear the data cache&lt;br /&gt;
: delete all &amp;quot;&amp;lt;tt&amp;gt;.bin&amp;lt;/tt&amp;gt;&amp;quot; files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -fv protected/runtime/cache/*.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear web assets&lt;br /&gt;
: delete all sub-folders of &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt; by running this command (in the installation directory): &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -r assets/*/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear the &amp;quot;auth cache&amp;quot;&lt;br /&gt;
: Flush the auth cache table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;&lt;br /&gt;
DELETE FROM `x2_auth_cache` WHERE 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;log all users out&lt;br /&gt;
: Flush the sessions table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;DELETE FROM `x2_sessions` WHERE 1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Upgrading =&lt;br /&gt;
&lt;br /&gt;
== Using the Web Interface ==&lt;br /&gt;
[[File:Software-Upgrade-Registration.png|400px|thumb|right|Software upgrade page, in the first step (registration)]]&lt;br /&gt;
To upgrade to Professional Edition&lt;br /&gt;
# Purchase X2Engine Professional Edition Download. Note the registration info in the activation email.&lt;br /&gt;
# Go to the Admin page in X2Engine and find &amp;quot;Upgrade X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot;.&lt;br /&gt;
# In the ''Upgrade'' page, enter your registration info into the form and click &amp;quot;Register&amp;quot; to continue. Once complete, you will see a list of changes similar to those displayed when performing an update.&lt;br /&gt;
# Review the changes to be applied and then proceed by clicking &amp;quot;Upgrade&amp;quot;, similar to performing an update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Offline&amp;quot; Upgrading ==&lt;br /&gt;
This works in exactly the same way as offline updating. However, to obtain your upgrade package, which you will extract into a folder called &amp;quot;update&amp;quot; on the X2Engine web server, contact [mailto:customersupport@x2engine.com customer support]. Furthermore, you will need to run the following database command on your X2Engine database when done with the upgrade, replacing &amp;lt;tt&amp;gt;{product key}&amp;lt;/tt&amp;gt; with your product key, to allow your installation to receive Professional Edition software updates:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{product key}' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Manually Upgrading ==&lt;br /&gt;
The process of upgrading is very similar to that of updating, and uses all of the same underpinnings as the updater. Thus, almost all of the above information and procedures will apply to upgrades. However, note the following crucial differences:&lt;br /&gt;
&lt;br /&gt;
* '''X2Engine must be at the latest available version in order to upgrade.'''&lt;br /&gt;
* '''To obtain the upgrade package, you must be an owner of an X2Engine Professional Edition Download-type license.''' Contact [mailto:customersupport@x2engine.com customer support] for further details.&lt;br /&gt;
* '''There will be only one &amp;quot;intermediate&amp;quot; version.''' The changes to apply will move X2Engine from a version to that same version, applying instead the necessary changes to switch its ''edition'' from Open Source to Professional.&lt;br /&gt;
* '''There are no file deletions.'''&lt;br /&gt;
* '''The properties of the package manifest differ slightly.''' In upgrades, the properties of the data object are &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, etc. Note also, the update data is in aptly-named sub-folders of &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt; as well, and there are no &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; lists to handle.&lt;br /&gt;
* '''The configuration is not changed.''' Instead, the value stored in the field &amp;lt;tt&amp;gt;unique_id&amp;lt;/tt&amp;gt; of the admin record in the database table &amp;lt;tt&amp;gt;x2_admin&amp;lt;/tt&amp;gt; must be changed to the license key, and the &amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt; field must be changed to &amp;quot;pro&amp;quot;. To perform these both, run the following command on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''It is not necessary to clear caches or user sessions when done.''' These operations are performed in updates to eliminate ephemeral (session-ingrained) data that may no longer be applicable and thus cause problems. Upgrades will rarely, if ever, invalidate this data.&lt;br /&gt;
&lt;br /&gt;
= Updating to Beta Versions = &lt;br /&gt;
Beginning at version 4.1.2, after much advocacy to adopt this practice, X2Engine began a two-phase release process in which each version would be released first as a beta, then to the broader public after initial bug finds. The purpose behind this is to avoid updating production installations of X2Engine to new versions that have had zero public exposure, and thus avoid introducing bugs that were not found in initial QA testing (due to a incompatibility with a particular server environment or configuration of X2Engine) into sensitive production environments.&lt;br /&gt;
&lt;br /&gt;
By default, the updater will not update to beta versions. The updater must be configured to use the proper URL on the update server for beta release data, by the following procedure:&lt;br /&gt;
# Create a file &amp;quot;constants-custom.php&amp;quot; in the root level of X2Engine. You can easily do this by renaming &amp;quot;constants-custom.example.php&amp;quot; to that name (remove &amp;quot;.custom&amp;quot;).&lt;br /&gt;
# Look for the following line, and in it, change &amp;quot;false&amp;quot; to &amp;quot;true&amp;quot;: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('X2_UPDATE_BETA') or define('X2_UPDATE_BETA',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The advantage of enabling beta releases is that new features will be available much sooner. Additionally, fixes to bugs will also be available sooner. The main disadvantage of enabling beta releases is that they are never guaranteed to be as stable as non-beta releases. We encourage users to clone their installations of X2Engine, if able, and use beta versions on them, and report any issues they encounter for their particular server environment/special configuration/custom implementation of X2Engine. This way, any issues that arise can be fixed in the stable release. The more users participate in beta testing, the more stable the non-beta releases will become.&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
== User interface (UI) &amp;quot;bugs&amp;quot; after the update ==&lt;br /&gt;
It is extremely common (and easily remedied) that after an important update, certain features of the user interface of X2Engine may not work properly. This is caused, in nearly all cases, by the web browser's cache, which contains out-of-date copies of X2Engine's essential user interface files. To solve these problems, the first step to try is clearing the cache. A step-by-step guide to doing so is given in [[wikihow:Clear-Your-Browser%27s-Cache]].&lt;br /&gt;
&lt;br /&gt;
== Errors in the updater itself == &lt;br /&gt;
When all else fails, the updater may have to be patched due to an irreconcilable backwards compatibility issue. Fortunately, if one has read/write access to the source files on the webserver, this is straightforward to fix. See [[#Refreshing_and_Updating_the_Updater_Utility|Refreshing and Updating the Updater Utility]].&lt;br /&gt;
&lt;br /&gt;
== Professional Edition not in effect after upgrading ==&lt;br /&gt;
This may be caused by Professional Edition not getting &amp;quot;switched on&amp;quot; properly, despite all the necessary filesystem and database changes already being in effect. First, to see if this scenario applies to you, look for a folder &amp;quot;&amp;lt;tt&amp;gt;reports&amp;lt;/tt&amp;gt;&amp;quot; inside &amp;lt;tt&amp;gt;protected/modules&amp;lt;/tt&amp;gt;, and a table &amp;lt;tt&amp;gt;x2_reports&amp;lt;/tt&amp;gt; in the X2Engine database. If you find these things, you can try the following to enable Professional Edition:&lt;br /&gt;
# Find the file &amp;lt;tt&amp;gt;constants.php&amp;lt;/tt&amp;gt; in the installation directory. Open it in a text editor, and look for the following line: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('PRO_VERSION') or define('PRO_VERSION',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; on that line, and save the file.&lt;br /&gt;
# Run the following SQL command (replacing &amp;lt;tt&amp;gt;{license key}&amp;lt;/tt&amp;gt; with your X2Engine license key) on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General Procedures to Try ==&lt;br /&gt;
It may sometimes be possible that the update did not finish cleanly. In such cases, it will be necessary to manually re-perform some of the final operations in the update, described in [[#Introduction|Introduction]]. &lt;br /&gt;
&lt;br /&gt;
=== Refresh the Source Code ===&lt;br /&gt;
This step is not really the best step to try first, as it is usually more time-consuming. It entails re-uploading the entire application, in order to reset all non-ephemeral (source code) files to what they should be in a fresh installation of X2Engine. To do this:&lt;br /&gt;
# Download the refresh package. There should be a download link for this in the ''Updater Settings'' page. Otherwise, if that doesn't work and you're using Open Source edition, use [https://x2planet.com/installs/refresh.zip this link], or if using Professional Edition, paste the following url (after replacing {key} with your license key) into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/refresh-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Upload the ''contents'' of the downloaded zip archive to the installation directory, overwriting existing files.&lt;br /&gt;
&lt;br /&gt;
=== Clear Caches ===&lt;br /&gt;
See &amp;quot;[[#Clearing_caches_and_refreshing_the_application|Clearing Caches...]]&amp;quot; for details.&lt;br /&gt;
&lt;br /&gt;
=== Regenerate the Configuration ===&lt;br /&gt;
Again, this should not be done unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a new installation at the same version as the one you updated to. Refer to [[#Editing_the_Configuration_File|Editing the Configuration File]].&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1842</id>
		<title>Software Updates and Upgrades</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1842"/>
				<updated>2014-06-24T21:11:31Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Disadvantages */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
[[File:Update_Process.svg|200px|thumb|right|Top-level overview of the update process]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
The following things happen in typical a software update (or upgrade):&lt;br /&gt;
* Files that are new in the new version are added&lt;br /&gt;
* Files that have been changed in the new version are replaced with the new versions of those files &lt;br /&gt;
* Files that are no longer used in the new version are deleted&lt;br /&gt;
* Changes to the database are made to make the database compatible with the new version of X2Engine&lt;br /&gt;
* The main configuration file, &amp;lt;tt&amp;gt;X2Config.php&amp;lt;/tt&amp;gt;, is edited to reflect the new state of the application.&lt;br /&gt;
* X2Engine data caches are cleared&lt;br /&gt;
&lt;br /&gt;
These operations are typically handled by the updater utility component [[x2doc:UpdaterBehavior|UpdaterBehavior]] and controlled via the in-app web-based updater or the command line updater. This article will cover both how to use said interfaces to the updater, and how to perform updates manually or with the &amp;quot;offline&amp;quot; method if the need arises, in addition to troubleshooting and recovering from an update that did not complete cleanly.&lt;br /&gt;
&lt;br /&gt;
= Disclaimer =&lt;br /&gt;
This article covers the standard and recommended update/upgrade methods, in addition to alternate methods and supplemental procedures. As general rules and precautions for using this article:&lt;br /&gt;
# ''If you do not fully and completely understand any step of any instructions given in this article, '''do not''' follow them''; ask for help instead on [http://x2community.com the forums] or through contacting X2Engine Technical Support if you are an existing customer.&lt;br /&gt;
# You should always make a backup of all of your files ''and'' a full backup copy of your database, preferably in a format (i.e. SQL script) that can be used to create a verbatim/carbon copy of the original database, with all of the features of the original schema, '''before''' attempting any steps of the update process manually.&lt;br /&gt;
# Some of the procedures outlined in this article involve manually creating or modifying files. Note that the same filesystem prerequisite for installation, namely that the file ownership must match the user under which PHP executes on the web server, applies to these steps.&lt;br /&gt;
&lt;br /&gt;
Furthermore, '''in regard to uploading files:''' note that any instructions to upload folders into X2Engine, and replace/merge contents, refer to replacing files in X2Engine with files in the archive at analogous paths. For example, if you have a zip file that contains a folder &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, and inside of that is a folder &amp;lt;tt&amp;gt;components&amp;lt;/tt&amp;gt;, and inside of that is a file &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt;, and the instructions are to upload/replace in X2Engine, you should upload &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt; to replace the existing file of the same name in &amp;lt;tt&amp;gt;protected/components&amp;lt;/tt&amp;gt; inside of X2Engine.&lt;br /&gt;
&lt;br /&gt;
X2Engine Inc. cannot be held accountable for damage/errors caused by any missteps or mistakes in manually-performed update operations. If in doubt, make a sandbox/clone of your X2Engine installation by copying the files and database to a different server, and test performing the operations on that before proceeding with your production install of X2Engine.&lt;br /&gt;
&lt;br /&gt;
= Update Notifications =&lt;br /&gt;
[[File:Update Notification.png|200px|frame|right|System flash message displaying software update notification.]]&lt;br /&gt;
To get notified of software updates, unless already using X2Engine Professional Edition, one must first &amp;quot;subscribe&amp;quot; to them. This can be done via checking the box &amp;quot;Notify me of software updates&amp;quot; in the installation form. If X2Engine is already installed, this can be done by going to &amp;quot;Updater Settings&amp;quot; from the Admin page and enabling them. If using Professional Edition, these steps are not not necessary.&lt;br /&gt;
&lt;br /&gt;
Once that has been completed, set the update notification interval as desired in &amp;quot;Updater Settings&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Update Methods =&lt;br /&gt;
There are, in essence, just three basic methods of updating, and variations on each. The three main methods are: &lt;br /&gt;
# web updater,&lt;br /&gt;
# command line updater, and&lt;br /&gt;
# manually.&lt;br /&gt;
In each case, an update package from the update data server is used as the authoritative list of changes to apply, but in the non-manual methods, X2Engine's own updater class does all the work applying changes from said package.&lt;br /&gt;
&lt;br /&gt;
In web and command line updates, there is also the option to first download and unpack the package manually (see [[#Performing_.22Offline.22_Updates|performing &amp;quot;offline&amp;quot; updates]]). This circumvents the need for the local server (on which X2Engine is hosted) to be connected to the internet. Similarly, in the case of manual updates, it is not necessary for the web server to actually have an internet connection.&lt;br /&gt;
&lt;br /&gt;
== Using the Web Updater ==&lt;br /&gt;
[[File:Updater-1.png|300px|thumb|right|The updater utility page.]]&lt;br /&gt;
The web updater can be accessed by any of the following methods:&lt;br /&gt;
* Using the link in the notification message at the top of the window in versions later than 3.5.5&lt;br /&gt;
* By going to the Admin page and clicking the &amp;quot;Update&amp;quot; button at the top, in earlier versions, with update notifications enabled&lt;br /&gt;
* Through &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
First, navigate to the web updater page, and '''do not''' cancel the request (stop or hit your browser's &amp;quot;back&amp;quot; button) until the page has loaded. If the updater utility has undergone changes and needs to self-update to be compatible with the software updates server, this is when it will do so. Thus, it is critical that you do not interrupt this process. If the page does not load, or displays the error message that dependencies could not be retrieved, you may have to [[#Performing_.22Offline.22_Updates|perform an &amp;quot;offline&amp;quot; update]].&lt;br /&gt;
&lt;br /&gt;
There are three ways of getting to the updater page. One is to simply click the link given in the update notification flash message, if available. The other is to click on the &amp;quot;Update&amp;quot; button at the top of the admin page. If software update notifications are not enabled, one can manually check for and install an update by going to &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
Next, make a backup of the database and files (recommended), follow the instructions on the page, and click the &amp;quot;Update&amp;quot; button when ready.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
It is generally quick and is the easiest method to use, and it does not require shell/SSH access to the web server.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
It is not recommended for use on large installations of X2Engine, i.e. containing over a million records, or for updating from very old versions.&lt;br /&gt;
&lt;br /&gt;
==== Explanation ====&lt;br /&gt;
The inherent weakness of the web updater (or any web-based updater to any web application) is that it must always rely upon web requests to the server, and it thus must perform extremely critical operations for the update all within the scope of a web request. Web requests, it goes without saying, are not always reliable. For example, one's internet connection might get cut off due being on a wireless network and encountering interference. Furthermore, if any operations take an exceedingly long amount of time, e.g. because of the size of the data set (and thus indexes) on which the update must operate, or the number of database changes that need to be applied, the web request for those operations may take longer than the PHP execution time limit as configured on the webserver. This would potentially cut short the execution of a critical update stage, and thus the update would require manual recovery at best, or completely restoring  X2Engine to a backup at worst.&lt;br /&gt;
&lt;br /&gt;
==== Ways to get around this ====&lt;br /&gt;
In most cases, including on shared hosting, it should be possible to change the time limit on PHP. It is highly recommended that you either disable the timeout or set it to a very generous value (such as 300 seconds) while attempting to update via the web updater.&lt;br /&gt;
&lt;br /&gt;
== Using the Command Line Updater ==&lt;br /&gt;
This update method uses all of the same thoroughly-tested underpinnings and processes as the web updater, but does so all within a system command to the Yii console.&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
While in via SSH, or in a terminal emulator, or in a script, run the following command in the &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt; subdirectory of X2Engine:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./yiic update app&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the following optional arguments to the command line updater:&lt;br /&gt;
;&amp;lt;tt&amp;gt;--force&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; for this option (i.e. &amp;lt;tt&amp;gt;--force=1&amp;lt;/tt&amp;gt;) to proceed with the update even if compatibility issues were detected. Default: 0 (halt if compatibility issues were detected).&lt;br /&gt;
;&amp;lt;tt&amp;gt;--backup&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that a database backup should be performed first before updating, &amp;quot;0&amp;quot; to proceed without creating a backup first. Default: 1.&lt;br /&gt;
;&amp;lt;tt&amp;gt;--lock&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that the application should be locked during the final installation of the update package to prevent data entry during the sensitive moments when the database is being altered and the files updated, &amp;quot;0&amp;quot; to permit use even when changes to the app are being applied. Default: 0.&lt;br /&gt;
&lt;br /&gt;
If running the command gives you the following message:&lt;br /&gt;
&amp;lt;pre&amp;gt;The updater is now up-to-date and compliant with the updates server. Re-run the command to proceed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
That indicates that the updater first needed to self-update, and succeeded in doing so. You should then run the same command again to complete the update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Safer to use on large, mission-critical systems. The success of a software update (or upgrade) does not hinge upon a single web request completing successfully.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
More difficult to use; requires shell access to the web server. The console command must be run as the same system user that owns all the files and directories of X2Engine, in order to circumvent permission issues when manipulating file system objects.&lt;br /&gt;
&lt;br /&gt;
== Using the Automatic Updater ==&lt;br /&gt;
[[File:Auto-Update.png|400px|thumb|right|Auto-update scheduler on the Updater Settings page]]&lt;br /&gt;
This method uses the command line updater, but runs the command on your behalf using the local cron daemon. To configure, go to the Updater Settings Page, check the box titled &amp;quot;Update Automatically&amp;quot;, and save. If you have Professional Edition, you can manage the update schedule in addition to other automated tasks from the Cron Table settings page, which can also be found under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Automates the entire process of updating.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
Only available on UNIX-like systems that have a cron daemon, and which permit user access to the cron table.&lt;br /&gt;
&lt;br /&gt;
== Performing &amp;quot;Offline&amp;quot; Updates ==&lt;br /&gt;
In some server environments, the updater may not be able to access the updates server by itself, due to (for example) the PHP or network configuration, or because the &amp;quot;zip&amp;quot; PHP extension (requried) is unavailable or has been disabled. It is in these situations necessary to download the update package manually, unpack on the server (or unpack and upload the files to the server), and then run the updater. When performing updates in this way, the updater will not need to access the updates server, but will use the manually-downloaded and extracted package files present in the local filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Refreshing and Updating the Updater Utility ===&lt;br /&gt;
Before starting, note that the updater itself may have undergone changes; the X2Engine update process is subject to change. To address this technical challenge, the updater utility within X2Engine would normally first attempt to download the latest version of itself, in order to be compatible with the updates server. However, this sometimes will not work properly. If your X2Engine installation is running in a hosting environment that prevents it from making outbound HTTP requests (the [[requirements:|requirements check script]] should tell you if this is the case), you will need to do this manually. Also, in version 3.5.5, there was a bug in the updater utility's self-update methods.&lt;br /&gt;
&lt;br /&gt;
To update the updater utility:&lt;br /&gt;
# '''Determine the version of the updater utility package''' by visiting [https://x2planet.com/installs/updates/updateCheck this link]. You should see a page that's entirely blank except for a version number.&lt;br /&gt;
# '''Download the updater package.''' In versions 3.6 and later, a download link for this should be available through the ''Updater Settings'' page. Otherwise, use the following URL (if in Open Source Edition): &amp;lt;pre&amp;gt;https://x2planet.com/installs/updater.zip&amp;lt;/pre&amp;gt; Otherwise, if using Professional Edition, paste the following url, after replacing {key} with your license key, into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/updater-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# '''Extract the downloaded zip archive and upload''' the &amp;quot;protected&amp;quot; folder to your server. Merge folders and overwrite existing files when prompted.&lt;br /&gt;
# '''Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor''' and find the line that looks like this:&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$updaterVersion = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# '''Change the text inside the pair of single quotes''' on that line so that it is the current updater version obtained in step #1. &lt;br /&gt;
# '''Save the file.'''&lt;br /&gt;
&lt;br /&gt;
=== Obtaining the Update Package ===&lt;br /&gt;
First, use the link in the ''Updater Settings'' page entitled &amp;quot;Latest Update Package for Version...&amp;quot;, to download the update package. If the link in the Updater Settings page does not work, use the following URL to download the update package (if using Open Source Edition), replacing &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; with your X2Engine installation's version:&lt;br /&gt;
&amp;lt;pre&amp;gt;https://x2planet.com/installs/updates/{version}/none&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should receive, from that URL, a zip archive file called &amp;quot;update.zip&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If using Professional Edition, and your download link is not working, contact [mailto:customersupport@x2engine.com customer support].&lt;br /&gt;
&lt;br /&gt;
=== Unpacking the Update ===&lt;br /&gt;
[[File:Update-in-Progress.png|200px|frame|right|System flash message displaying notification of pending update.]]&lt;br /&gt;
The update package will be a zip archive. The ''contents'' of this archive must be extracted ''into'' a folder called &amp;quot;update&amp;quot; in the installation directory. So, in other words, after extracting and uploading to the server, there should be a folder called &amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt; alongside &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;framework&amp;lt;/tt&amp;gt; (etc). Within that folder (&amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt;) should be the following contents:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A file containing important data to be used by the updater&lt;br /&gt;
;&amp;lt;tt&amp;gt;contents.md5&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A message digest file containing MD5 checksums of all files in the archive excluding itself.&lt;br /&gt;
;&amp;lt;tt&amp;gt;source&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A sub-folder/directory, whose internal structure mimics that of X2Engine, containing files that were changed in the update. If there were no files changed (i.e. only database changes were in the update), this directory will not be present.&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A directory containing flat data files. These files are typically not used by the updater, but are useful if performing manual updates or recovery.&lt;br /&gt;
&lt;br /&gt;
=== Applying the Update ===&lt;br /&gt;
[[File:Steps-post-Unpack.png|200px|frame|right|Steps of the update process in the event of manually-extracted package (or resuming a preexisting update)]]&lt;br /&gt;
After unpacking, if you run the command line updater, it should (if no problems arise, i.e. incompatible package version) apply the update straight away. Otherwise, log into X2Engine, and you should see a system flash message notifying you of an update in progress. This message shows up whenever there is an update package folder (&amp;quot;update&amp;quot;) present on the server. If you then go to the updater page, once the page has finished loading, you should see that there only remain the steps &amp;quot;Review and confirm changes&amp;quot;, &amp;quot;Apply changes&amp;quot; and &amp;quot;Reload&amp;quot; remain to be completed. To complete the update, when ready, click &amp;quot;Update&amp;quot;; otherwise, to delete the &amp;quot;update&amp;quot; folder and cancel the update, click &amp;quot;Cancel&amp;quot; either on the updater page or in the system flash message.&lt;br /&gt;
&lt;br /&gt;
== Performing Updates Manually ==&lt;br /&gt;
While it is preferable to avoid manually applying changes to X2Engine, and you may never need to, it may in some circumstance or another actually be necessary to do so. For all such situations, the raw update data files generated by the X2Engine update build process are bundled with every update package to help you perform the operations described in [[#Introduction|&amp;quot;Introduction&amp;quot;]]. This section will describe how to perform each of those operations. &lt;br /&gt;
&lt;br /&gt;
=== Prerequisites ===&lt;br /&gt;
First and foremost, all shell commands listed here, and in general, any operations that create or modify files, ''must be run as the same system user under which the server runs PHP scripts.'' Otherwise, the ownership and/or permissions of files and directories will have to be changed after completing all changes.&lt;br /&gt;
&lt;br /&gt;
To obtain the update package (which will be necessary), refer to &amp;quot;[[#Obtaining_the_Update_Package|Obtaining the Update Package]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Furthermore, it is strongly advised that you first make a back-up copy of all files and of X2Engine's database. Your database backup would best be a full, exact copy, such as one that would be made by [http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html mysqldump], or the built-in export tools of [http://www.phpmyadmin.net/home_page/index.php PHPMyAdmin].&lt;br /&gt;
&lt;br /&gt;
The X2Engine global import/export utility (&amp;quot;Import All Data&amp;quot;/&amp;quot;Export All Data&amp;quot;) in Admin '''is not recommended by itself as the sole backup tool'''. This is especially important to note because of how this tool cannot re-create custom fields; they must exist in the target installation before the import. However, using it to ''supplement'' your standard database backup method will prove useful in the event that you:&lt;br /&gt;
# don't have any custom fields,&lt;br /&gt;
# cannot manage to update X2Engine with success, and&lt;br /&gt;
# wish to start over with a fresh installation.&lt;br /&gt;
In such a case, you will then be able to import your old data into the new installation.&lt;br /&gt;
&lt;br /&gt;
=== Introduction: Data Files ===&lt;br /&gt;
In [[#Unpacking_the_Update|Unpacking the update]], a rough overview of an update package's contents were given. In this section, the anatomy and purpose of each of the data files will be introduced.&lt;br /&gt;
&lt;br /&gt;
Included in every update package is two copies of the following data:&lt;br /&gt;
* Files to copy&lt;br /&gt;
* Files to delete&lt;br /&gt;
* Database changes to enact&lt;br /&gt;
* Migration scripts to run, if any&lt;br /&gt;
&lt;br /&gt;
The first, immediately visible place where all of this data is stored is in the file &amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;, as a JSON-encoded array. See the next section for how to use this file.&lt;br /&gt;
&lt;br /&gt;
The second place, which can be useful to users who do not wish to automate or script any step of a manual update, is in the files contained in the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; directory. Each file is named after the version to which it corresponds, and contains the data required to update ''to'' that version ''from the version immediately preceding it.''' Thus, the versions you should see in each file name are the versions above your X2Engine installation's current version, up to and including the version being updated to.&lt;br /&gt;
&lt;br /&gt;
In an update, the directory will contain the following subdirectories:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of files to copy. The format is each line containing a file path in the list.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of obsolete files for that version which will need to be deleted. The format is the same as in &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts corresponding to each version. Each file is formatted such that it can be directly run on the database as an SQL script, although this is not generally recommended. The list of SQL commands to run is a of the following format: &lt;br /&gt;
  [query 1];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [query 2];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [etc]&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts with similar naming and formatting conventions to the &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; scripts. See [[#The_structure_of_the_.22data.22_array|The structure of the &amp;quot;data&amp;quot; array]] for explanation of the purpose of &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== How to use the manifest for updating manually ===&lt;br /&gt;
If you so choose, you may write a script to perform all of the operations for an update in one fell swoop. If so, and if the preferred scripting language of choice has a JSON-parsing library available, you will be able to save yourself a lot of effort by using the main data file (&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;) to load all of the necessary data for the update.&lt;br /&gt;
&lt;br /&gt;
==== The structure the manifest ====&lt;br /&gt;
In updates, the contents of the plain-text file manifest.json are a JSON-encoded object containing the following properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the data corresponding to each intermediate version. It is, in essence, all the data stored in the files in &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt;. If the update is for an installation of X2Engine that is only one version behind, it will contain only one element. Each element of this array is itself an array containing respective data for the intermediate version.&lt;br /&gt;
;&amp;lt;tt&amp;gt;versions&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array listing the version numbers of each intermediate version in the update. Similar to &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, this array will contain only one element if updating to the next version, and that version ''will be the next version''.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of deletions of obsolete files across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of files added or changed across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''from'', or in other words, the version of X2Engine before the package is applied. '''This must be the same as the version of installation to which the package is being applied.''' If this is not so, the updater utility would exit immediately, as applying the changes in the update would have potentially harmful results to data and/or compatibility with future updates.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''to'', or in other words, the version of X2Engine before the package is applied.&lt;br /&gt;
;&amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the version of the updater utility for which the package was compiled. If the structure of the update package or of the manifest changes, the updater must also change to accommodate and recognize the changes; else it might try to access properties that don't exist, or ignore important ones that do. This is the single property of manifest.json that should never change.&lt;br /&gt;
;&amp;lt;tt&amp;gt;scenario&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, which should be &amp;quot;update&amp;quot; if the package is for an update. In the case of upgrades, which use most of the same functionality of the updater, it should be &amp;quot;upgrade&amp;quot;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation after the package is applied. It should be &amp;quot;opensource&amp;quot; for open source edition, &amp;quot;pro&amp;quot; for professional edition. In the case of an upgrade package, it differs from the &amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation before the package is applied. &lt;br /&gt;
;&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An integer denoting the timestamp of the release date of the version of X2Engine denoted by &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The structure of the &amp;quot;data&amp;quot; array ====&lt;br /&gt;
In updates, each version's data, contained in each element of the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, contains the following nested array properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files added for the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files deleted in the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of SQL commands to bring the database structure and contents into compliance with the version from the previous version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A prerequisite list of SQL commands that should be run before those in &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, and if any fail, the failures should be ignored. These are added to updates whenever it is necessary to normalize the database schema, i.e. in the event that some installations' databases may not be consistent with others, but the commands might fail on every other system. These SQL commands were intended to be &amp;quot;attempted&amp;quot;; they are not guaranteed to succeed on all installations. An example of when this is necessary would be adding a foreign key constraint when in previous versions the storage engine was left to the database's default settings in old versions. MyISAM does not natively support foreign key constraints. Thus, to standardize this across all installations, including installations on servers where the storage engine defaulted to MyISAM, the constraint must first be dropped (in a &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; command) before the relevant tables are changed to the InnoDB storage engine, and the constraint is added back again, in the usual &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;version&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The version number&lt;br /&gt;
;&amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The edition&lt;br /&gt;
;&amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of PHP scripts that would be run in an update for this version. These are a subset of fileList and are typically used for advanced database operations, i.e. restructuring values of JSON-encoded fields, which are extremely impractical (or impossible) to perform using raw SQL commands. The files contained in the list all have paths that look like &amp;quot;&amp;lt;tt&amp;gt;protected/migrations/{version}/{timestamp}-name-of-operations.php&amp;lt;/tt&amp;gt;&amp;quot;. Note, if this array is not empty, you may need to reverse-engineer or otherwise copy and re-purpose/re-write any migration scripts listed in order to perform their necessary operations. This is because they may depend on running within a Yii Framework environment, i.e. will contain references to components of the Yii application singleton such as &amp;quot;db&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Note that in upgrades, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; are replaced by &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; (respectively), and &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; are missing (because they aren't necessary).&lt;br /&gt;
&lt;br /&gt;
==== What you will need for the update, if using the manifest ====&lt;br /&gt;
''Among this data, what you will need:'' &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, which will give you the full list of files that need to be deleted, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, which will give you the full list of files to copy from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory, &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;, which give you values with which to update the main X2Engine configuration file, and, within each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, the sub-array properties &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;, with which you will construct the list of database commands to run. &lt;br /&gt;
&lt;br /&gt;
A procedure for manually updating X2Engine, with data from manifest.json, thus might look like this:&lt;br /&gt;
# For each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; (or in other words, each version's changes):&lt;br /&gt;
## Run all the SQL commands in its corresponding &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; sub-array, ignoring errors&lt;br /&gt;
## Run all SQL commands in its &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; array&lt;br /&gt;
## Perform database operations that would be equivalent to those performed in migration scripts&lt;br /&gt;
# For each file in the cumulative list &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, copy it from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory into its analogous location in the installation directory of X2Engine, creating parent directories as necessary if they don't already exist.&lt;br /&gt;
# Delete all files in the cumulative list &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; from the installation directory&lt;br /&gt;
# Clear the data cache files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt; and the temporary asset folders in &amp;lt;tt&amp;gt;assets/&amp;lt;/tt&amp;gt;&lt;br /&gt;
# Clear all session data (&amp;lt;tt&amp;gt;x2_sessions&amp;lt;/tt&amp;gt;) and auth cache data (&amp;lt;tt&amp;gt;x2_auth_cache&amp;lt;/tt&amp;gt;).&lt;br /&gt;
# Update the main X2Engine configuration (&amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt;) file, setting:&lt;br /&gt;
## &amp;lt;tt&amp;gt;$buildDate&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt; property,&lt;br /&gt;
## &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt; property, and &lt;br /&gt;
## &amp;lt;tt&amp;gt;$updaterVersion&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
&lt;br /&gt;
=== Enacting Database Changes ===&lt;br /&gt;
Inside the &amp;lt;tt&amp;gt;update/sqlList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/sqlForce&amp;lt;/tt&amp;gt; folder is a list of &amp;lt;tt&amp;gt;.sql&amp;lt;/tt&amp;gt; files, named according to their corresponding version's update, that can essentially be run as SQL scripts to perform the update. Note, however, that '''if the update traverses more than one version, the scripts must be run in the order of their corresponding version.''' This is because, in the database changes for any version, it is assumed that the database schema and contents conform to the version that immediately preceded it. The PHP function [[phpfunc:version-compare|version_compare]], and the method &amp;quot;[http://pythonhosted.org/setuptools/pkg_resources.html#parsing-utilities parse_version]&amp;quot;, in the pkg_resources module of setuptools, should correctly identify which version should come first. Note, the [[#The_structure_the_manifest|&amp;quot;versions&amp;quot; property of the manifest]] will contain the list of versions in the correct order.&lt;br /&gt;
&lt;br /&gt;
Furthermore, note, &amp;quot;sqlList&amp;quot; and &amp;quot;sqlForce&amp;quot; may not encompass all necessary database changes; if there are any migration scripts in the update, the operations they perform will similarly need to be performed in the current update (see the description of &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; in [[#The_structure_of_the_.22data.22_array|&amp;quot;the structure of the 'data' array&amp;quot;]]).&lt;br /&gt;
&lt;br /&gt;
In summary, for each version traversed in the update, the following operations should be run in order, with &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version number of the version in question:&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlForce/{version}.sql&amp;lt;/tt&amp;gt; file,''' in the order they appear in the file. Ignore if any commands fail. Note, if passed to the MySQL client as a script, any failures might trigger a halt in execution and thus skip the remainder of the commands.&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlList/{version}.sql&amp;lt;/tt&amp;gt; file.'''&lt;br /&gt;
# '''Perform operations equivalent to those in each of the migration scripts, if any.''' The first part of a script file name is the timestamp it was added to the codebase, and this also dictates the order in which they should be run.&lt;br /&gt;
&lt;br /&gt;
=== Copying new files ===&lt;br /&gt;
Files added/changed in the new version are stored in the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory. In an update, they should be copied into the installation directory, replacing any existing files of the same name/path. If the &amp;lt;tt&amp;gt;rsync&amp;lt;/tt&amp;gt; utility is available on the system, you can use one command to safely copy these files into the installation directory as follows (executed in that same directory):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rsync -avc update/source/ ./&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, you will need to use &amp;lt;tt&amp;gt;cat&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;cp&amp;lt;/tt&amp;gt;, two utilities that should be available on every UNIX-like system, and the lists of files added in each intermediate version. These lists are stored in the folder &amp;lt;tt&amp;gt;update/data/fileList&amp;lt;/tt&amp;gt; and named like &amp;quot;{version}.txt&amp;quot; (&amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version to which it applies), each file on its own line in that file. Run the following command, ignoring errors:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/fileList/*; do for l in $(cat $f); do cp update/source/&amp;quot;$l&amp;quot; ./&amp;quot;$l&amp;quot; ; done ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting old files ===&lt;br /&gt;
Files to be deleted are listed in a similar manner as the files to be added: one file per line. The list of files to delete are located at files with paths that look like this: &amp;lt;tt&amp;gt;update/data/deletionList/{version}.txt&amp;lt;/tt&amp;gt;&lt;br /&gt;
So, for an update from 5.5 to 5.7 with intermediate version 5.6 (for instance), the files would be &amp;lt;tt&amp;gt;update/data/deletionList/5.6&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/data/deletionList/5.7&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is only one version through which to update, the operation is very simple, and can be performed in one UNIX/Linux (bash) shell command as follows (if the version is 3.6.3, for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
xargs -a update/data/deletionList/3.6.3.txt -d&amp;quot;\n&amp;quot; rm -fv&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, if there are multiple versions, this may be a more tricky proposition. The necessity arises to create a cumulative list, because there might be the possibility that a file is deleted in one version and re-established in a new version. Nevertheless, while this is possible, it is highly unlikely, and in most cases it is safe to simply iterate over all the &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; data files and delete all the files listed therein. As a bash command:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/deletionList/*; do xargs -a $f -d&amp;quot;\n&amp;quot; rm -fv ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Editing the Configuration File ===&lt;br /&gt;
This final step in the update process, before refreshing, should not be done manually unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a fresh installation at the same version as the one to which you have updated.&lt;br /&gt;
&lt;br /&gt;
To update the configuration manually:&lt;br /&gt;
# Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor&lt;br /&gt;
# Find the line with the &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; variable in it. It should look like this (but with the version from which you are updating): &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$version = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change the value in quotes to the current version being updated to, and save the file.&lt;br /&gt;
&lt;br /&gt;
=== Clearing caches and refreshing the application ===&lt;br /&gt;
It is generally always a good idea to clear the application's server-side cache and other ephemeral files/data after updating. This is taken care of automatically by the built-in updater, but barring proper use and functioning of that, the operations can be performed manually as follows: &lt;br /&gt;
;clear the data cache&lt;br /&gt;
: delete all &amp;quot;&amp;lt;tt&amp;gt;.bin&amp;lt;/tt&amp;gt;&amp;quot; files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -fv protected/runtime/cache/*.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear web assets&lt;br /&gt;
: delete all sub-folders of &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt; by running this command (in the installation directory): &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -r assets/*/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear the &amp;quot;auth cache&amp;quot;&lt;br /&gt;
: Flush the auth cache table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;&lt;br /&gt;
DELETE FROM `x2_auth_cache` WHERE 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;log all users out&lt;br /&gt;
: Flush the sessions table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;DELETE FROM `x2_sessions` WHERE 1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Upgrading =&lt;br /&gt;
&lt;br /&gt;
== Using the Web Interface ==&lt;br /&gt;
[[File:Software-Upgrade-Registration.png|400px|thumb|right|Software upgrade page, in the first step (registration)]]&lt;br /&gt;
To upgrade to Professional Edition&lt;br /&gt;
# Purchase X2Engine Professional Edition Download. Note the registration info in the activation email.&lt;br /&gt;
# Go to the Admin page in X2Engine and find &amp;quot;Upgrade X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot;.&lt;br /&gt;
# In the ''Upgrade'' page, enter your registration info into the form and click &amp;quot;Register&amp;quot; to continue. Once complete, you will see a list of changes similar to those displayed when performing an update.&lt;br /&gt;
# Review the changes to be applied and then proceed by clicking &amp;quot;Upgrade&amp;quot;, similar to performing an update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Offline&amp;quot; Upgrading ==&lt;br /&gt;
This works in exactly the same way as offline updating. However, to obtain your upgrade package, which you will extract into a folder called &amp;quot;update&amp;quot; on the X2Engine web server, contact [mailto:customersupport@x2engine.com customer support]. Furthermore, you will need to run the following database command on your X2Engine database when done with the upgrade, replacing &amp;lt;tt&amp;gt;{product key}&amp;lt;/tt&amp;gt; with your product key, to allow your installation to receive Professional Edition software updates:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{product key}' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Manually Upgrading ==&lt;br /&gt;
The process of upgrading is very similar to that of updating, and uses all of the same underpinnings as the updater. Thus, almost all of the above information and procedures will apply to upgrades. However, note the following crucial differences:&lt;br /&gt;
&lt;br /&gt;
* '''X2Engine must be at the latest available version in order to upgrade.'''&lt;br /&gt;
* '''To obtain the upgrade package, you must be an owner of an X2Engine Professional Edition Download-type license.''' Contact [mailto:customersupport@x2engine.com customer support] for further details.&lt;br /&gt;
* '''There will be only one &amp;quot;intermediate&amp;quot; version.''' The changes to apply will move X2Engine from a version to that same version, applying instead the necessary changes to switch its ''edition'' from Open Source to Professional.&lt;br /&gt;
* '''There are no file deletions.'''&lt;br /&gt;
* '''The properties of the package manifest differ slightly.''' In upgrades, the properties of the data object are &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, etc. Note also, the update data is in aptly-named sub-folders of &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt; as well, and there are no &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; lists to handle.&lt;br /&gt;
* '''The configuration is not changed.''' Instead, the value stored in the field &amp;lt;tt&amp;gt;unique_id&amp;lt;/tt&amp;gt; of the admin record in the database table &amp;lt;tt&amp;gt;x2_admin&amp;lt;/tt&amp;gt; must be changed to the license key, and the &amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt; field must be changed to &amp;quot;pro&amp;quot;. To perform these both, run the following command on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''It is not necessary to clear caches or user sessions when done.''' These operations are performed in updates to eliminate ephemeral (session-ingrained) data that may no longer be applicable and thus cause problems. Upgrades will rarely, if ever, invalidate this data.&lt;br /&gt;
&lt;br /&gt;
= Updating to Beta Versions = &lt;br /&gt;
Beginning at version 4.1.2, after much advocacy to adopt this practice, X2Engine began a two-phase release process in which each version would be released first as a beta, then to the broader public after initial bug finds. The purpose behind this is to avoid updating production installations of X2Engine to new versions that have had zero public exposure, and thus avoid introducing bugs that were not found in initial QA testing (due to a incompatibility with a particular server environment or configuration of X2Engine) into sensitive production environments.&lt;br /&gt;
&lt;br /&gt;
By default, the updater will not update to beta versions. The updater must be configured to use the proper URL on the update server for beta release data, by the following procedure:&lt;br /&gt;
# Create a file &amp;quot;constants-custom.php&amp;quot; in the root level of X2Engine. You can easily do this by renaming &amp;quot;constants-custom.example.php&amp;quot; to that name (remove &amp;quot;.custom&amp;quot;).&lt;br /&gt;
# Look for the following line, and in it, change &amp;quot;false&amp;quot; to &amp;quot;true&amp;quot;: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('X2_UPDATE_BETA') or define('X2_UPDATE_BETA',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The advantage of enabling beta releases is that new features will be available much sooner. Additionally, fixes to bugs will also be available sooner. The main disadvantage of enabling beta releases is that they are never guaranteed to be as stable as non-beta releases. We encourage users to clone their installations of X2Engine, if able, and use beta versions on them, and report any issues they encounter for their particular server environment/special configuration/custom implementation of X2Engine. This way, any issues that arise can be fixed in the stable release. The more users participate in beta testing, the more stable the non-beta releases will become.&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
== User interface (UI) &amp;quot;bugs&amp;quot; after the update ==&lt;br /&gt;
It is extremely common (and easily remedied) that after an important update, certain features of the user interface of X2Engine may not work properly. This is caused, in nearly all cases, by the web browser's cache, which contains out-of-date copies of X2Engine's essential user interface files. To solve these problems, the first step to try is clearing the cache. A step-by-step guide to doing so is given in [[wikihow:Clear-Your-Browser%27s-Cache]].&lt;br /&gt;
&lt;br /&gt;
== Errors in the updater itself == &lt;br /&gt;
When all else fails, the updater may have to be patched due to an irreconcilable backwards compatibility issue. Fortunately, if one has read/write access to the source files on the webserver, this is straightforward to fix. See [[#Refreshing_and_Updating_the_Updater_Utility|Refreshing and Updating the Updater Utility]].&lt;br /&gt;
&lt;br /&gt;
== Professional Edition not in effect after upgrading ==&lt;br /&gt;
This may be caused by Professional Edition not getting &amp;quot;switched on&amp;quot; properly, despite all the necessary filesystem and database changes already being in effect. First, to see if this scenario applies to you, look for a folder &amp;quot;&amp;lt;tt&amp;gt;reports&amp;lt;/tt&amp;gt;&amp;quot; inside &amp;lt;tt&amp;gt;protected/modules&amp;lt;/tt&amp;gt;, and a table &amp;lt;tt&amp;gt;x2_reports&amp;lt;/tt&amp;gt; in the X2Engine database. If you find these things, you can try the following to enable Professional Edition:&lt;br /&gt;
# Find the file &amp;lt;tt&amp;gt;constants.php&amp;lt;/tt&amp;gt; in the installation directory. Open it in a text editor, and look for the following line: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('PRO_VERSION') or define('PRO_VERSION',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; on that line, and save the file.&lt;br /&gt;
# Run the following SQL command (replacing &amp;lt;tt&amp;gt;{license key}&amp;lt;/tt&amp;gt; with your X2Engine license key) on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General Procedures to Try ==&lt;br /&gt;
It may sometimes be possible that the update did not finish cleanly. In such cases, it will be necessary to manually re-perform some of the final operations in the update, described in [[#Introduction|Introduction]]. &lt;br /&gt;
&lt;br /&gt;
=== Refresh the Source Code ===&lt;br /&gt;
This step is not really the best step to try first, as it is usually more time-consuming. It entails re-uploading the entire application, in order to reset all non-ephemeral (source code) files to what they should be in a fresh installation of X2Engine. To do this:&lt;br /&gt;
# Download the refresh package. There should be a download link for this in the ''Updater Settings'' page. Otherwise, if that doesn't work and you're using Open Source edition, use [https://x2planet.com/installs/refresh.zip this link], or if using Professional Edition, paste the following url (after replacing {key} with your license key) into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/refresh-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Upload the ''contents'' of the downloaded zip archive to the installation directory, overwriting existing files.&lt;br /&gt;
&lt;br /&gt;
=== Clear Caches ===&lt;br /&gt;
See &amp;quot;[[#Clearing_caches_and_refreshing_the_application|Clearing Caches...]]&amp;quot; for details.&lt;br /&gt;
&lt;br /&gt;
=== Regenerate the Configuration ===&lt;br /&gt;
Again, this should not be done unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a new installation at the same version as the one you updated to. Refer to [[#Editing_the_Configuration_File|Editing the Configuration File]].&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1841</id>
		<title>Software Updates and Upgrades</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1841"/>
				<updated>2014-06-18T23:38:39Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Updating to Beta Versions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
[[File:Update_Process.svg|200px|thumb|right|Top-level overview of the update process]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
The following things happen in typical a software update (or upgrade):&lt;br /&gt;
* Files that are new in the new version are added&lt;br /&gt;
* Files that have been changed in the new version are replaced with the new versions of those files &lt;br /&gt;
* Files that are no longer used in the new version are deleted&lt;br /&gt;
* Changes to the database are made to make the database compatible with the new version of X2Engine&lt;br /&gt;
* The main configuration file, &amp;lt;tt&amp;gt;X2Config.php&amp;lt;/tt&amp;gt;, is edited to reflect the new state of the application.&lt;br /&gt;
* X2Engine data caches are cleared&lt;br /&gt;
&lt;br /&gt;
These operations are typically handled by the updater utility component [[x2doc:UpdaterBehavior|UpdaterBehavior]] and controlled via the in-app web-based updater or the command line updater. This article will cover both how to use said interfaces to the updater, and how to perform updates manually or with the &amp;quot;offline&amp;quot; method if the need arises, in addition to troubleshooting and recovering from an update that did not complete cleanly.&lt;br /&gt;
&lt;br /&gt;
= Disclaimer =&lt;br /&gt;
This article covers the standard and recommended update/upgrade methods, in addition to alternate methods and supplemental procedures. As general rules and precautions for using this article:&lt;br /&gt;
# ''If you do not fully and completely understand any step of any instructions given in this article, '''do not''' follow them''; ask for help instead on [http://x2community.com the forums] or through contacting X2Engine Technical Support if you are an existing customer.&lt;br /&gt;
# You should always make a backup of all of your files ''and'' a full backup copy of your database, preferably in a format (i.e. SQL script) that can be used to create a verbatim/carbon copy of the original database, with all of the features of the original schema, '''before''' attempting any steps of the update process manually.&lt;br /&gt;
# Some of the procedures outlined in this article involve manually creating or modifying files. Note that the same filesystem prerequisite for installation, namely that the file ownership must match the user under which PHP executes on the web server, applies to these steps.&lt;br /&gt;
&lt;br /&gt;
Furthermore, '''in regard to uploading files:''' note that any instructions to upload folders into X2Engine, and replace/merge contents, refer to replacing files in X2Engine with files in the archive at analogous paths. For example, if you have a zip file that contains a folder &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, and inside of that is a folder &amp;lt;tt&amp;gt;components&amp;lt;/tt&amp;gt;, and inside of that is a file &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt;, and the instructions are to upload/replace in X2Engine, you should upload &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt; to replace the existing file of the same name in &amp;lt;tt&amp;gt;protected/components&amp;lt;/tt&amp;gt; inside of X2Engine.&lt;br /&gt;
&lt;br /&gt;
X2Engine Inc. cannot be held accountable for damage/errors caused by any missteps or mistakes in manually-performed update operations. If in doubt, make a sandbox/clone of your X2Engine installation by copying the files and database to a different server, and test performing the operations on that before proceeding with your production install of X2Engine.&lt;br /&gt;
&lt;br /&gt;
= Update Notifications =&lt;br /&gt;
[[File:Update Notification.png|200px|frame|right|System flash message displaying software update notification.]]&lt;br /&gt;
To get notified of software updates, unless already using X2Engine Professional Edition, one must first &amp;quot;subscribe&amp;quot; to them. This can be done via checking the box &amp;quot;Notify me of software updates&amp;quot; in the installation form. If X2Engine is already installed, this can be done by going to &amp;quot;Updater Settings&amp;quot; from the Admin page and enabling them. If using Professional Edition, these steps are not not necessary.&lt;br /&gt;
&lt;br /&gt;
Once that has been completed, set the update notification interval as desired in &amp;quot;Updater Settings&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Update Methods =&lt;br /&gt;
There are, in essence, just three basic methods of updating, and variations on each. The three main methods are: &lt;br /&gt;
# web updater,&lt;br /&gt;
# command line updater, and&lt;br /&gt;
# manually.&lt;br /&gt;
In each case, an update package from the update data server is used as the authoritative list of changes to apply, but in the non-manual methods, X2Engine's own updater class does all the work applying changes from said package.&lt;br /&gt;
&lt;br /&gt;
In web and command line updates, there is also the option to first download and unpack the package manually (see [[#Performing_.22Offline.22_Updates|performing &amp;quot;offline&amp;quot; updates]]). This circumvents the need for the local server (on which X2Engine is hosted) to be connected to the internet. Similarly, in the case of manual updates, it is not necessary for the web server to actually have an internet connection.&lt;br /&gt;
&lt;br /&gt;
== Using the Web Updater ==&lt;br /&gt;
[[File:Updater-1.png|300px|thumb|right|The updater utility page.]]&lt;br /&gt;
The web updater can be accessed by any of the following methods:&lt;br /&gt;
* Using the link in the notification message at the top of the window in versions later than 3.5.5&lt;br /&gt;
* By going to the Admin page and clicking the &amp;quot;Update&amp;quot; button at the top, in earlier versions, with update notifications enabled&lt;br /&gt;
* Through &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
First, navigate to the web updater page, and '''do not''' cancel the request (stop or hit your browser's &amp;quot;back&amp;quot; button) until the page has loaded. If the updater utility has undergone changes and needs to self-update to be compatible with the software updates server, this is when it will do so. Thus, it is critical that you do not interrupt this process. If the page does not load, or displays the error message that dependencies could not be retrieved, you may have to [[#Performing_.22Offline.22_Updates|perform an &amp;quot;offline&amp;quot; update]].&lt;br /&gt;
&lt;br /&gt;
There are three ways of getting to the updater page. One is to simply click the link given in the update notification flash message, if available. The other is to click on the &amp;quot;Update&amp;quot; button at the top of the admin page. If software update notifications are not enabled, one can manually check for and install an update by going to &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
Next, make a backup of the database and files (recommended), follow the instructions on the page, and click the &amp;quot;Update&amp;quot; button when ready.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
It is generally quick and is the easiest method to use, and it does not require shell/SSH access to the web server.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
It is not recommended for use on large installations of X2Engine, i.e. containing over a million records.&lt;br /&gt;
&lt;br /&gt;
==== Explanation ====&lt;br /&gt;
The inherent weakness of the web updater (or any web-based updater to any web application) is that it must always rely upon web requests to the server, and it thus must perform extremely critical operations for the update all within the scope of a web request. Web requests, it goes without saying, are not always reliable. For example, one's internet connection might get cut off due being on a wireless network and encountering interference. Furthermore, if any operations take an exceedingly long amount of time, e.g. because of the size of the data set on which the update must operate, the web request for those operations may take longer than the PHP execution time limit as configured on the webserver. This would potentially cut short the execution of a critical update stage, and thus the update would require manual recovery at best, or completely restoring  X2Engine to a backup at worst.&lt;br /&gt;
&lt;br /&gt;
==== Ways to get around this ====&lt;br /&gt;
In most cases, including on shared hosting, it should be possible to change the time limit on PHP. It is highly recommended that you either disable the timeout or set it to a very generous value (such as 300 seconds) while attempting to update via the web updater.&lt;br /&gt;
&lt;br /&gt;
== Using the Command Line Updater ==&lt;br /&gt;
This update method uses all of the same thoroughly-tested underpinnings and processes as the web updater, but does so all within a system command to the Yii console.&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
While in via SSH, or in a terminal emulator, or in a script, run the following command in the &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt; subdirectory of X2Engine:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./yiic update app&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the following optional arguments to the command line updater:&lt;br /&gt;
;&amp;lt;tt&amp;gt;--force&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; for this option (i.e. &amp;lt;tt&amp;gt;--force=1&amp;lt;/tt&amp;gt;) to proceed with the update even if compatibility issues were detected. Default: 0 (halt if compatibility issues were detected).&lt;br /&gt;
;&amp;lt;tt&amp;gt;--backup&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that a database backup should be performed first before updating, &amp;quot;0&amp;quot; to proceed without creating a backup first. Default: 1.&lt;br /&gt;
;&amp;lt;tt&amp;gt;--lock&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that the application should be locked during the final installation of the update package to prevent data entry during the sensitive moments when the database is being altered and the files updated, &amp;quot;0&amp;quot; to permit use even when changes to the app are being applied. Default: 0.&lt;br /&gt;
&lt;br /&gt;
If running the command gives you the following message:&lt;br /&gt;
&amp;lt;pre&amp;gt;The updater is now up-to-date and compliant with the updates server. Re-run the command to proceed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
That indicates that the updater first needed to self-update, and succeeded in doing so. You should then run the same command again to complete the update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Safer to use on large, mission-critical systems. The success of a software update (or upgrade) does not hinge upon a single web request completing successfully.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
More difficult to use; requires shell access to the web server. The console command must be run as the same system user that owns all the files and directories of X2Engine, in order to circumvent permission issues when manipulating file system objects.&lt;br /&gt;
&lt;br /&gt;
== Using the Automatic Updater ==&lt;br /&gt;
[[File:Auto-Update.png|400px|thumb|right|Auto-update scheduler on the Updater Settings page]]&lt;br /&gt;
This method uses the command line updater, but runs the command on your behalf using the local cron daemon. To configure, go to the Updater Settings Page, check the box titled &amp;quot;Update Automatically&amp;quot;, and save. If you have Professional Edition, you can manage the update schedule in addition to other automated tasks from the Cron Table settings page, which can also be found under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Automates the entire process of updating.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
Only available on UNIX-like systems that have a cron daemon, and which permit user access to the cron table.&lt;br /&gt;
&lt;br /&gt;
== Performing &amp;quot;Offline&amp;quot; Updates ==&lt;br /&gt;
In some server environments, the updater may not be able to access the updates server by itself, due to (for example) the PHP or network configuration, or because the &amp;quot;zip&amp;quot; PHP extension (requried) is unavailable or has been disabled. It is in these situations necessary to download the update package manually, unpack on the server (or unpack and upload the files to the server), and then run the updater. When performing updates in this way, the updater will not need to access the updates server, but will use the manually-downloaded and extracted package files present in the local filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Refreshing and Updating the Updater Utility ===&lt;br /&gt;
Before starting, note that the updater itself may have undergone changes; the X2Engine update process is subject to change. To address this technical challenge, the updater utility within X2Engine would normally first attempt to download the latest version of itself, in order to be compatible with the updates server. However, this sometimes will not work properly. If your X2Engine installation is running in a hosting environment that prevents it from making outbound HTTP requests (the [[requirements:|requirements check script]] should tell you if this is the case), you will need to do this manually. Also, in version 3.5.5, there was a bug in the updater utility's self-update methods.&lt;br /&gt;
&lt;br /&gt;
To update the updater utility:&lt;br /&gt;
# '''Determine the version of the updater utility package''' by visiting [https://x2planet.com/installs/updates/updateCheck this link]. You should see a page that's entirely blank except for a version number.&lt;br /&gt;
# '''Download the updater package.''' In versions 3.6 and later, a download link for this should be available through the ''Updater Settings'' page. Otherwise, use the following URL (if in Open Source Edition): &amp;lt;pre&amp;gt;https://x2planet.com/installs/updater.zip&amp;lt;/pre&amp;gt; Otherwise, if using Professional Edition, paste the following url, after replacing {key} with your license key, into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/updater-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# '''Extract the downloaded zip archive and upload''' the &amp;quot;protected&amp;quot; folder to your server. Merge folders and overwrite existing files when prompted.&lt;br /&gt;
# '''Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor''' and find the line that looks like this:&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$updaterVersion = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# '''Change the text inside the pair of single quotes''' on that line so that it is the current updater version obtained in step #1. &lt;br /&gt;
# '''Save the file.'''&lt;br /&gt;
&lt;br /&gt;
=== Obtaining the Update Package ===&lt;br /&gt;
First, use the link in the ''Updater Settings'' page entitled &amp;quot;Latest Update Package for Version...&amp;quot;, to download the update package. If the link in the Updater Settings page does not work, use the following URL to download the update package (if using Open Source Edition), replacing &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; with your X2Engine installation's version:&lt;br /&gt;
&amp;lt;pre&amp;gt;https://x2planet.com/installs/updates/{version}/none&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should receive, from that URL, a zip archive file called &amp;quot;update.zip&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If using Professional Edition, and your download link is not working, contact [mailto:customersupport@x2engine.com customer support].&lt;br /&gt;
&lt;br /&gt;
=== Unpacking the Update ===&lt;br /&gt;
[[File:Update-in-Progress.png|200px|frame|right|System flash message displaying notification of pending update.]]&lt;br /&gt;
The update package will be a zip archive. The ''contents'' of this archive must be extracted ''into'' a folder called &amp;quot;update&amp;quot; in the installation directory. So, in other words, after extracting and uploading to the server, there should be a folder called &amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt; alongside &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;framework&amp;lt;/tt&amp;gt; (etc). Within that folder (&amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt;) should be the following contents:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A file containing important data to be used by the updater&lt;br /&gt;
;&amp;lt;tt&amp;gt;contents.md5&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A message digest file containing MD5 checksums of all files in the archive excluding itself.&lt;br /&gt;
;&amp;lt;tt&amp;gt;source&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A sub-folder/directory, whose internal structure mimics that of X2Engine, containing files that were changed in the update. If there were no files changed (i.e. only database changes were in the update), this directory will not be present.&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A directory containing flat data files. These files are typically not used by the updater, but are useful if performing manual updates or recovery.&lt;br /&gt;
&lt;br /&gt;
=== Applying the Update ===&lt;br /&gt;
[[File:Steps-post-Unpack.png|200px|frame|right|Steps of the update process in the event of manually-extracted package (or resuming a preexisting update)]]&lt;br /&gt;
After unpacking, if you run the command line updater, it should (if no problems arise, i.e. incompatible package version) apply the update straight away. Otherwise, log into X2Engine, and you should see a system flash message notifying you of an update in progress. This message shows up whenever there is an update package folder (&amp;quot;update&amp;quot;) present on the server. If you then go to the updater page, once the page has finished loading, you should see that there only remain the steps &amp;quot;Review and confirm changes&amp;quot;, &amp;quot;Apply changes&amp;quot; and &amp;quot;Reload&amp;quot; remain to be completed. To complete the update, when ready, click &amp;quot;Update&amp;quot;; otherwise, to delete the &amp;quot;update&amp;quot; folder and cancel the update, click &amp;quot;Cancel&amp;quot; either on the updater page or in the system flash message.&lt;br /&gt;
&lt;br /&gt;
== Performing Updates Manually ==&lt;br /&gt;
While it is preferable to avoid manually applying changes to X2Engine, and you may never need to, it may in some circumstance or another actually be necessary to do so. For all such situations, the raw update data files generated by the X2Engine update build process are bundled with every update package to help you perform the operations described in [[#Introduction|&amp;quot;Introduction&amp;quot;]]. This section will describe how to perform each of those operations. &lt;br /&gt;
&lt;br /&gt;
=== Prerequisites ===&lt;br /&gt;
First and foremost, all shell commands listed here, and in general, any operations that create or modify files, ''must be run as the same system user under which the server runs PHP scripts.'' Otherwise, the ownership and/or permissions of files and directories will have to be changed after completing all changes.&lt;br /&gt;
&lt;br /&gt;
To obtain the update package (which will be necessary), refer to &amp;quot;[[#Obtaining_the_Update_Package|Obtaining the Update Package]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Furthermore, it is strongly advised that you first make a back-up copy of all files and of X2Engine's database. Your database backup would best be a full, exact copy, such as one that would be made by [http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html mysqldump], or the built-in export tools of [http://www.phpmyadmin.net/home_page/index.php PHPMyAdmin].&lt;br /&gt;
&lt;br /&gt;
The X2Engine global import/export utility (&amp;quot;Import All Data&amp;quot;/&amp;quot;Export All Data&amp;quot;) in Admin '''is not recommended by itself as the sole backup tool'''. This is especially important to note because of how this tool cannot re-create custom fields; they must exist in the target installation before the import. However, using it to ''supplement'' your standard database backup method will prove useful in the event that you:&lt;br /&gt;
# don't have any custom fields,&lt;br /&gt;
# cannot manage to update X2Engine with success, and&lt;br /&gt;
# wish to start over with a fresh installation.&lt;br /&gt;
In such a case, you will then be able to import your old data into the new installation.&lt;br /&gt;
&lt;br /&gt;
=== Introduction: Data Files ===&lt;br /&gt;
In [[#Unpacking_the_Update|Unpacking the update]], a rough overview of an update package's contents were given. In this section, the anatomy and purpose of each of the data files will be introduced.&lt;br /&gt;
&lt;br /&gt;
Included in every update package is two copies of the following data:&lt;br /&gt;
* Files to copy&lt;br /&gt;
* Files to delete&lt;br /&gt;
* Database changes to enact&lt;br /&gt;
* Migration scripts to run, if any&lt;br /&gt;
&lt;br /&gt;
The first, immediately visible place where all of this data is stored is in the file &amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;, as a JSON-encoded array. See the next section for how to use this file.&lt;br /&gt;
&lt;br /&gt;
The second place, which can be useful to users who do not wish to automate or script any step of a manual update, is in the files contained in the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; directory. Each file is named after the version to which it corresponds, and contains the data required to update ''to'' that version ''from the version immediately preceding it.''' Thus, the versions you should see in each file name are the versions above your X2Engine installation's current version, up to and including the version being updated to.&lt;br /&gt;
&lt;br /&gt;
In an update, the directory will contain the following subdirectories:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of files to copy. The format is each line containing a file path in the list.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of obsolete files for that version which will need to be deleted. The format is the same as in &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts corresponding to each version. Each file is formatted such that it can be directly run on the database as an SQL script, although this is not generally recommended. The list of SQL commands to run is a of the following format: &lt;br /&gt;
  [query 1];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [query 2];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [etc]&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts with similar naming and formatting conventions to the &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; scripts. See [[#The_structure_of_the_.22data.22_array|The structure of the &amp;quot;data&amp;quot; array]] for explanation of the purpose of &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== How to use the manifest for updating manually ===&lt;br /&gt;
If you so choose, you may write a script to perform all of the operations for an update in one fell swoop. If so, and if the preferred scripting language of choice has a JSON-parsing library available, you will be able to save yourself a lot of effort by using the main data file (&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;) to load all of the necessary data for the update.&lt;br /&gt;
&lt;br /&gt;
==== The structure the manifest ====&lt;br /&gt;
In updates, the contents of the plain-text file manifest.json are a JSON-encoded object containing the following properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the data corresponding to each intermediate version. It is, in essence, all the data stored in the files in &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt;. If the update is for an installation of X2Engine that is only one version behind, it will contain only one element. Each element of this array is itself an array containing respective data for the intermediate version.&lt;br /&gt;
;&amp;lt;tt&amp;gt;versions&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array listing the version numbers of each intermediate version in the update. Similar to &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, this array will contain only one element if updating to the next version, and that version ''will be the next version''.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of deletions of obsolete files across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of files added or changed across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''from'', or in other words, the version of X2Engine before the package is applied. '''This must be the same as the version of installation to which the package is being applied.''' If this is not so, the updater utility would exit immediately, as applying the changes in the update would have potentially harmful results to data and/or compatibility with future updates.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''to'', or in other words, the version of X2Engine before the package is applied.&lt;br /&gt;
;&amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the version of the updater utility for which the package was compiled. If the structure of the update package or of the manifest changes, the updater must also change to accommodate and recognize the changes; else it might try to access properties that don't exist, or ignore important ones that do. This is the single property of manifest.json that should never change.&lt;br /&gt;
;&amp;lt;tt&amp;gt;scenario&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, which should be &amp;quot;update&amp;quot; if the package is for an update. In the case of upgrades, which use most of the same functionality of the updater, it should be &amp;quot;upgrade&amp;quot;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation after the package is applied. It should be &amp;quot;opensource&amp;quot; for open source edition, &amp;quot;pro&amp;quot; for professional edition. In the case of an upgrade package, it differs from the &amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation before the package is applied. &lt;br /&gt;
;&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An integer denoting the timestamp of the release date of the version of X2Engine denoted by &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The structure of the &amp;quot;data&amp;quot; array ====&lt;br /&gt;
In updates, each version's data, contained in each element of the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, contains the following nested array properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files added for the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files deleted in the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of SQL commands to bring the database structure and contents into compliance with the version from the previous version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A prerequisite list of SQL commands that should be run before those in &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, and if any fail, the failures should be ignored. These are added to updates whenever it is necessary to normalize the database schema, i.e. in the event that some installations' databases may not be consistent with others, but the commands might fail on every other system. These SQL commands were intended to be &amp;quot;attempted&amp;quot;; they are not guaranteed to succeed on all installations. An example of when this is necessary would be adding a foreign key constraint when in previous versions the storage engine was left to the database's default settings in old versions. MyISAM does not natively support foreign key constraints. Thus, to standardize this across all installations, including installations on servers where the storage engine defaulted to MyISAM, the constraint must first be dropped (in a &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; command) before the relevant tables are changed to the InnoDB storage engine, and the constraint is added back again, in the usual &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;version&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The version number&lt;br /&gt;
;&amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The edition&lt;br /&gt;
;&amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of PHP scripts that would be run in an update for this version. These are a subset of fileList and are typically used for advanced database operations, i.e. restructuring values of JSON-encoded fields, which are extremely impractical (or impossible) to perform using raw SQL commands. The files contained in the list all have paths that look like &amp;quot;&amp;lt;tt&amp;gt;protected/migrations/{version}/{timestamp}-name-of-operations.php&amp;lt;/tt&amp;gt;&amp;quot;. Note, if this array is not empty, you may need to reverse-engineer or otherwise copy and re-purpose/re-write any migration scripts listed in order to perform their necessary operations. This is because they may depend on running within a Yii Framework environment, i.e. will contain references to components of the Yii application singleton such as &amp;quot;db&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Note that in upgrades, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; are replaced by &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; (respectively), and &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; are missing (because they aren't necessary).&lt;br /&gt;
&lt;br /&gt;
==== What you will need for the update, if using the manifest ====&lt;br /&gt;
''Among this data, what you will need:'' &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, which will give you the full list of files that need to be deleted, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, which will give you the full list of files to copy from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory, &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;, which give you values with which to update the main X2Engine configuration file, and, within each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, the sub-array properties &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;, with which you will construct the list of database commands to run. &lt;br /&gt;
&lt;br /&gt;
A procedure for manually updating X2Engine, with data from manifest.json, thus might look like this:&lt;br /&gt;
# For each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; (or in other words, each version's changes):&lt;br /&gt;
## Run all the SQL commands in its corresponding &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; sub-array, ignoring errors&lt;br /&gt;
## Run all SQL commands in its &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; array&lt;br /&gt;
## Perform database operations that would be equivalent to those performed in migration scripts&lt;br /&gt;
# For each file in the cumulative list &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, copy it from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory into its analogous location in the installation directory of X2Engine, creating parent directories as necessary if they don't already exist.&lt;br /&gt;
# Delete all files in the cumulative list &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; from the installation directory&lt;br /&gt;
# Clear the data cache files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt; and the temporary asset folders in &amp;lt;tt&amp;gt;assets/&amp;lt;/tt&amp;gt;&lt;br /&gt;
# Clear all session data (&amp;lt;tt&amp;gt;x2_sessions&amp;lt;/tt&amp;gt;) and auth cache data (&amp;lt;tt&amp;gt;x2_auth_cache&amp;lt;/tt&amp;gt;).&lt;br /&gt;
# Update the main X2Engine configuration (&amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt;) file, setting:&lt;br /&gt;
## &amp;lt;tt&amp;gt;$buildDate&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt; property,&lt;br /&gt;
## &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt; property, and &lt;br /&gt;
## &amp;lt;tt&amp;gt;$updaterVersion&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
&lt;br /&gt;
=== Enacting Database Changes ===&lt;br /&gt;
Inside the &amp;lt;tt&amp;gt;update/sqlList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/sqlForce&amp;lt;/tt&amp;gt; folder is a list of &amp;lt;tt&amp;gt;.sql&amp;lt;/tt&amp;gt; files, named according to their corresponding version's update, that can essentially be run as SQL scripts to perform the update. Note, however, that '''if the update traverses more than one version, the scripts must be run in the order of their corresponding version.''' This is because, in the database changes for any version, it is assumed that the database schema and contents conform to the version that immediately preceded it. The PHP function [[phpfunc:version-compare|version_compare]], and the method &amp;quot;[http://pythonhosted.org/setuptools/pkg_resources.html#parsing-utilities parse_version]&amp;quot;, in the pkg_resources module of setuptools, should correctly identify which version should come first. Note, the [[#The_structure_the_manifest|&amp;quot;versions&amp;quot; property of the manifest]] will contain the list of versions in the correct order.&lt;br /&gt;
&lt;br /&gt;
Furthermore, note, &amp;quot;sqlList&amp;quot; and &amp;quot;sqlForce&amp;quot; may not encompass all necessary database changes; if there are any migration scripts in the update, the operations they perform will similarly need to be performed in the current update (see the description of &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; in [[#The_structure_of_the_.22data.22_array|&amp;quot;the structure of the 'data' array&amp;quot;]]).&lt;br /&gt;
&lt;br /&gt;
In summary, for each version traversed in the update, the following operations should be run in order, with &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version number of the version in question:&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlForce/{version}.sql&amp;lt;/tt&amp;gt; file,''' in the order they appear in the file. Ignore if any commands fail. Note, if passed to the MySQL client as a script, any failures might trigger a halt in execution and thus skip the remainder of the commands.&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlList/{version}.sql&amp;lt;/tt&amp;gt; file.'''&lt;br /&gt;
# '''Perform operations equivalent to those in each of the migration scripts, if any.''' The first part of a script file name is the timestamp it was added to the codebase, and this also dictates the order in which they should be run.&lt;br /&gt;
&lt;br /&gt;
=== Copying new files ===&lt;br /&gt;
Files added/changed in the new version are stored in the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory. In an update, they should be copied into the installation directory, replacing any existing files of the same name/path. If the &amp;lt;tt&amp;gt;rsync&amp;lt;/tt&amp;gt; utility is available on the system, you can use one command to safely copy these files into the installation directory as follows (executed in that same directory):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rsync -avc update/source/ ./&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, you will need to use &amp;lt;tt&amp;gt;cat&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;cp&amp;lt;/tt&amp;gt;, two utilities that should be available on every UNIX-like system, and the lists of files added in each intermediate version. These lists are stored in the folder &amp;lt;tt&amp;gt;update/data/fileList&amp;lt;/tt&amp;gt; and named like &amp;quot;{version}.txt&amp;quot; (&amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version to which it applies), each file on its own line in that file. Run the following command, ignoring errors:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/fileList/*; do for l in $(cat $f); do cp update/source/&amp;quot;$l&amp;quot; ./&amp;quot;$l&amp;quot; ; done ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting old files ===&lt;br /&gt;
Files to be deleted are listed in a similar manner as the files to be added: one file per line. The list of files to delete are located at files with paths that look like this: &amp;lt;tt&amp;gt;update/data/deletionList/{version}.txt&amp;lt;/tt&amp;gt;&lt;br /&gt;
So, for an update from 5.5 to 5.7 with intermediate version 5.6 (for instance), the files would be &amp;lt;tt&amp;gt;update/data/deletionList/5.6&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/data/deletionList/5.7&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is only one version through which to update, the operation is very simple, and can be performed in one UNIX/Linux (bash) shell command as follows (if the version is 3.6.3, for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
xargs -a update/data/deletionList/3.6.3.txt -d&amp;quot;\n&amp;quot; rm -fv&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, if there are multiple versions, this may be a more tricky proposition. The necessity arises to create a cumulative list, because there might be the possibility that a file is deleted in one version and re-established in a new version. Nevertheless, while this is possible, it is highly unlikely, and in most cases it is safe to simply iterate over all the &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; data files and delete all the files listed therein. As a bash command:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/deletionList/*; do xargs -a $f -d&amp;quot;\n&amp;quot; rm -fv ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Editing the Configuration File ===&lt;br /&gt;
This final step in the update process, before refreshing, should not be done manually unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a fresh installation at the same version as the one to which you have updated.&lt;br /&gt;
&lt;br /&gt;
To update the configuration manually:&lt;br /&gt;
# Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor&lt;br /&gt;
# Find the line with the &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; variable in it. It should look like this (but with the version from which you are updating): &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$version = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change the value in quotes to the current version being updated to, and save the file.&lt;br /&gt;
&lt;br /&gt;
=== Clearing caches and refreshing the application ===&lt;br /&gt;
It is generally always a good idea to clear the application's server-side cache and other ephemeral files/data after updating. This is taken care of automatically by the built-in updater, but barring proper use and functioning of that, the operations can be performed manually as follows: &lt;br /&gt;
;clear the data cache&lt;br /&gt;
: delete all &amp;quot;&amp;lt;tt&amp;gt;.bin&amp;lt;/tt&amp;gt;&amp;quot; files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -fv protected/runtime/cache/*.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear web assets&lt;br /&gt;
: delete all sub-folders of &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt; by running this command (in the installation directory): &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -r assets/*/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear the &amp;quot;auth cache&amp;quot;&lt;br /&gt;
: Flush the auth cache table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;&lt;br /&gt;
DELETE FROM `x2_auth_cache` WHERE 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;log all users out&lt;br /&gt;
: Flush the sessions table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;DELETE FROM `x2_sessions` WHERE 1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Upgrading =&lt;br /&gt;
&lt;br /&gt;
== Using the Web Interface ==&lt;br /&gt;
[[File:Software-Upgrade-Registration.png|400px|thumb|right|Software upgrade page, in the first step (registration)]]&lt;br /&gt;
To upgrade to Professional Edition&lt;br /&gt;
# Purchase X2Engine Professional Edition Download. Note the registration info in the activation email.&lt;br /&gt;
# Go to the Admin page in X2Engine and find &amp;quot;Upgrade X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot;.&lt;br /&gt;
# In the ''Upgrade'' page, enter your registration info into the form and click &amp;quot;Register&amp;quot; to continue. Once complete, you will see a list of changes similar to those displayed when performing an update.&lt;br /&gt;
# Review the changes to be applied and then proceed by clicking &amp;quot;Upgrade&amp;quot;, similar to performing an update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Offline&amp;quot; Upgrading ==&lt;br /&gt;
This works in exactly the same way as offline updating. However, to obtain your upgrade package, which you will extract into a folder called &amp;quot;update&amp;quot; on the X2Engine web server, contact [mailto:customersupport@x2engine.com customer support]. Furthermore, you will need to run the following database command on your X2Engine database when done with the upgrade, replacing &amp;lt;tt&amp;gt;{product key}&amp;lt;/tt&amp;gt; with your product key, to allow your installation to receive Professional Edition software updates:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{product key}' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Manually Upgrading ==&lt;br /&gt;
The process of upgrading is very similar to that of updating, and uses all of the same underpinnings as the updater. Thus, almost all of the above information and procedures will apply to upgrades. However, note the following crucial differences:&lt;br /&gt;
&lt;br /&gt;
* '''X2Engine must be at the latest available version in order to upgrade.'''&lt;br /&gt;
* '''To obtain the upgrade package, you must be an owner of an X2Engine Professional Edition Download-type license.''' Contact [mailto:customersupport@x2engine.com customer support] for further details.&lt;br /&gt;
* '''There will be only one &amp;quot;intermediate&amp;quot; version.''' The changes to apply will move X2Engine from a version to that same version, applying instead the necessary changes to switch its ''edition'' from Open Source to Professional.&lt;br /&gt;
* '''There are no file deletions.'''&lt;br /&gt;
* '''The properties of the package manifest differ slightly.''' In upgrades, the properties of the data object are &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, etc. Note also, the update data is in aptly-named sub-folders of &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt; as well, and there are no &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; lists to handle.&lt;br /&gt;
* '''The configuration is not changed.''' Instead, the value stored in the field &amp;lt;tt&amp;gt;unique_id&amp;lt;/tt&amp;gt; of the admin record in the database table &amp;lt;tt&amp;gt;x2_admin&amp;lt;/tt&amp;gt; must be changed to the license key, and the &amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt; field must be changed to &amp;quot;pro&amp;quot;. To perform these both, run the following command on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''It is not necessary to clear caches or user sessions when done.''' These operations are performed in updates to eliminate ephemeral (session-ingrained) data that may no longer be applicable and thus cause problems. Upgrades will rarely, if ever, invalidate this data.&lt;br /&gt;
&lt;br /&gt;
= Updating to Beta Versions = &lt;br /&gt;
Beginning at version 4.1.2, after much advocacy to adopt this practice, X2Engine began a two-phase release process in which each version would be released first as a beta, then to the broader public after initial bug finds. The purpose behind this is to avoid updating production installations of X2Engine to new versions that have had zero public exposure, and thus avoid introducing bugs that were not found in initial QA testing (due to a incompatibility with a particular server environment or configuration of X2Engine) into sensitive production environments.&lt;br /&gt;
&lt;br /&gt;
By default, the updater will not update to beta versions. The updater must be configured to use the proper URL on the update server for beta release data, by the following procedure:&lt;br /&gt;
# Create a file &amp;quot;constants-custom.php&amp;quot; in the root level of X2Engine. You can easily do this by renaming &amp;quot;constants-custom.example.php&amp;quot; to that name (remove &amp;quot;.custom&amp;quot;).&lt;br /&gt;
# Look for the following line, and in it, change &amp;quot;false&amp;quot; to &amp;quot;true&amp;quot;: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('X2_UPDATE_BETA') or define('X2_UPDATE_BETA',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The advantage of enabling beta releases is that new features will be available much sooner. Additionally, fixes to bugs will also be available sooner. The main disadvantage of enabling beta releases is that they are never guaranteed to be as stable as non-beta releases. We encourage users to clone their installations of X2Engine, if able, and use beta versions on them, and report any issues they encounter for their particular server environment/special configuration/custom implementation of X2Engine. This way, any issues that arise can be fixed in the stable release. The more users participate in beta testing, the more stable the non-beta releases will become.&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
== User interface (UI) &amp;quot;bugs&amp;quot; after the update ==&lt;br /&gt;
It is extremely common (and easily remedied) that after an important update, certain features of the user interface of X2Engine may not work properly. This is caused, in nearly all cases, by the web browser's cache, which contains out-of-date copies of X2Engine's essential user interface files. To solve these problems, the first step to try is clearing the cache. A step-by-step guide to doing so is given in [[wikihow:Clear-Your-Browser%27s-Cache]].&lt;br /&gt;
&lt;br /&gt;
== Errors in the updater itself == &lt;br /&gt;
When all else fails, the updater may have to be patched due to an irreconcilable backwards compatibility issue. Fortunately, if one has read/write access to the source files on the webserver, this is straightforward to fix. See [[#Refreshing_and_Updating_the_Updater_Utility|Refreshing and Updating the Updater Utility]].&lt;br /&gt;
&lt;br /&gt;
== Professional Edition not in effect after upgrading ==&lt;br /&gt;
This may be caused by Professional Edition not getting &amp;quot;switched on&amp;quot; properly, despite all the necessary filesystem and database changes already being in effect. First, to see if this scenario applies to you, look for a folder &amp;quot;&amp;lt;tt&amp;gt;reports&amp;lt;/tt&amp;gt;&amp;quot; inside &amp;lt;tt&amp;gt;protected/modules&amp;lt;/tt&amp;gt;, and a table &amp;lt;tt&amp;gt;x2_reports&amp;lt;/tt&amp;gt; in the X2Engine database. If you find these things, you can try the following to enable Professional Edition:&lt;br /&gt;
# Find the file &amp;lt;tt&amp;gt;constants.php&amp;lt;/tt&amp;gt; in the installation directory. Open it in a text editor, and look for the following line: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('PRO_VERSION') or define('PRO_VERSION',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; on that line, and save the file.&lt;br /&gt;
# Run the following SQL command (replacing &amp;lt;tt&amp;gt;{license key}&amp;lt;/tt&amp;gt; with your X2Engine license key) on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General Procedures to Try ==&lt;br /&gt;
It may sometimes be possible that the update did not finish cleanly. In such cases, it will be necessary to manually re-perform some of the final operations in the update, described in [[#Introduction|Introduction]]. &lt;br /&gt;
&lt;br /&gt;
=== Refresh the Source Code ===&lt;br /&gt;
This step is not really the best step to try first, as it is usually more time-consuming. It entails re-uploading the entire application, in order to reset all non-ephemeral (source code) files to what they should be in a fresh installation of X2Engine. To do this:&lt;br /&gt;
# Download the refresh package. There should be a download link for this in the ''Updater Settings'' page. Otherwise, if that doesn't work and you're using Open Source edition, use [https://x2planet.com/installs/refresh.zip this link], or if using Professional Edition, paste the following url (after replacing {key} with your license key) into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/refresh-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Upload the ''contents'' of the downloaded zip archive to the installation directory, overwriting existing files.&lt;br /&gt;
&lt;br /&gt;
=== Clear Caches ===&lt;br /&gt;
See &amp;quot;[[#Clearing_caches_and_refreshing_the_application|Clearing Caches...]]&amp;quot; for details.&lt;br /&gt;
&lt;br /&gt;
=== Regenerate the Configuration ===&lt;br /&gt;
Again, this should not be done unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a new installation at the same version as the one you updated to. Refer to [[#Editing_the_Configuration_File|Editing the Configuration File]].&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1840</id>
		<title>Software Updates and Upgrades</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1840"/>
				<updated>2014-06-18T23:36:43Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Enabling Beta Releases */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
[[File:Update_Process.svg|200px|thumb|right|Top-level overview of the update process]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
The following things happen in typical a software update (or upgrade):&lt;br /&gt;
* Files that are new in the new version are added&lt;br /&gt;
* Files that have been changed in the new version are replaced with the new versions of those files &lt;br /&gt;
* Files that are no longer used in the new version are deleted&lt;br /&gt;
* Changes to the database are made to make the database compatible with the new version of X2Engine&lt;br /&gt;
* The main configuration file, &amp;lt;tt&amp;gt;X2Config.php&amp;lt;/tt&amp;gt;, is edited to reflect the new state of the application.&lt;br /&gt;
* X2Engine data caches are cleared&lt;br /&gt;
&lt;br /&gt;
These operations are typically handled by the updater utility component [[x2doc:UpdaterBehavior|UpdaterBehavior]] and controlled via the in-app web-based updater or the command line updater. This article will cover both how to use said interfaces to the updater, and how to perform updates manually or with the &amp;quot;offline&amp;quot; method if the need arises, in addition to troubleshooting and recovering from an update that did not complete cleanly.&lt;br /&gt;
&lt;br /&gt;
= Disclaimer =&lt;br /&gt;
This article covers the standard and recommended update/upgrade methods, in addition to alternate methods and supplemental procedures. As general rules and precautions for using this article:&lt;br /&gt;
# ''If you do not fully and completely understand any step of any instructions given in this article, '''do not''' follow them''; ask for help instead on [http://x2community.com the forums] or through contacting X2Engine Technical Support if you are an existing customer.&lt;br /&gt;
# You should always make a backup of all of your files ''and'' a full backup copy of your database, preferably in a format (i.e. SQL script) that can be used to create a verbatim/carbon copy of the original database, with all of the features of the original schema, '''before''' attempting any steps of the update process manually.&lt;br /&gt;
# Some of the procedures outlined in this article involve manually creating or modifying files. Note that the same filesystem prerequisite for installation, namely that the file ownership must match the user under which PHP executes on the web server, applies to these steps.&lt;br /&gt;
&lt;br /&gt;
Furthermore, '''in regard to uploading files:''' note that any instructions to upload folders into X2Engine, and replace/merge contents, refer to replacing files in X2Engine with files in the archive at analogous paths. For example, if you have a zip file that contains a folder &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, and inside of that is a folder &amp;lt;tt&amp;gt;components&amp;lt;/tt&amp;gt;, and inside of that is a file &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt;, and the instructions are to upload/replace in X2Engine, you should upload &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt; to replace the existing file of the same name in &amp;lt;tt&amp;gt;protected/components&amp;lt;/tt&amp;gt; inside of X2Engine.&lt;br /&gt;
&lt;br /&gt;
X2Engine Inc. cannot be held accountable for damage/errors caused by any missteps or mistakes in manually-performed update operations. If in doubt, make a sandbox/clone of your X2Engine installation by copying the files and database to a different server, and test performing the operations on that before proceeding with your production install of X2Engine.&lt;br /&gt;
&lt;br /&gt;
= Update Notifications =&lt;br /&gt;
[[File:Update Notification.png|200px|frame|right|System flash message displaying software update notification.]]&lt;br /&gt;
To get notified of software updates, unless already using X2Engine Professional Edition, one must first &amp;quot;subscribe&amp;quot; to them. This can be done via checking the box &amp;quot;Notify me of software updates&amp;quot; in the installation form. If X2Engine is already installed, this can be done by going to &amp;quot;Updater Settings&amp;quot; from the Admin page and enabling them. If using Professional Edition, these steps are not not necessary.&lt;br /&gt;
&lt;br /&gt;
Once that has been completed, set the update notification interval as desired in &amp;quot;Updater Settings&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Update Methods =&lt;br /&gt;
There are, in essence, just three basic methods of updating, and variations on each. The three main methods are: &lt;br /&gt;
# web updater,&lt;br /&gt;
# command line updater, and&lt;br /&gt;
# manually.&lt;br /&gt;
In each case, an update package from the update data server is used as the authoritative list of changes to apply, but in the non-manual methods, X2Engine's own updater class does all the work applying changes from said package.&lt;br /&gt;
&lt;br /&gt;
In web and command line updates, there is also the option to first download and unpack the package manually (see [[#Performing_.22Offline.22_Updates|performing &amp;quot;offline&amp;quot; updates]]). This circumvents the need for the local server (on which X2Engine is hosted) to be connected to the internet. Similarly, in the case of manual updates, it is not necessary for the web server to actually have an internet connection.&lt;br /&gt;
&lt;br /&gt;
== Using the Web Updater ==&lt;br /&gt;
[[File:Updater-1.png|300px|thumb|right|The updater utility page.]]&lt;br /&gt;
The web updater can be accessed by any of the following methods:&lt;br /&gt;
* Using the link in the notification message at the top of the window in versions later than 3.5.5&lt;br /&gt;
* By going to the Admin page and clicking the &amp;quot;Update&amp;quot; button at the top, in earlier versions, with update notifications enabled&lt;br /&gt;
* Through &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
First, navigate to the web updater page, and '''do not''' cancel the request (stop or hit your browser's &amp;quot;back&amp;quot; button) until the page has loaded. If the updater utility has undergone changes and needs to self-update to be compatible with the software updates server, this is when it will do so. Thus, it is critical that you do not interrupt this process. If the page does not load, or displays the error message that dependencies could not be retrieved, you may have to [[#Performing_.22Offline.22_Updates|perform an &amp;quot;offline&amp;quot; update]].&lt;br /&gt;
&lt;br /&gt;
There are three ways of getting to the updater page. One is to simply click the link given in the update notification flash message, if available. The other is to click on the &amp;quot;Update&amp;quot; button at the top of the admin page. If software update notifications are not enabled, one can manually check for and install an update by going to &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
Next, make a backup of the database and files (recommended), follow the instructions on the page, and click the &amp;quot;Update&amp;quot; button when ready.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
It is generally quick and is the easiest method to use, and it does not require shell/SSH access to the web server.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
It is not recommended for use on large installations of X2Engine, i.e. containing over a million records.&lt;br /&gt;
&lt;br /&gt;
==== Explanation ====&lt;br /&gt;
The inherent weakness of the web updater (or any web-based updater to any web application) is that it must always rely upon web requests to the server, and it thus must perform extremely critical operations for the update all within the scope of a web request. Web requests, it goes without saying, are not always reliable. For example, one's internet connection might get cut off due being on a wireless network and encountering interference. Furthermore, if any operations take an exceedingly long amount of time, e.g. because of the size of the data set on which the update must operate, the web request for those operations may take longer than the PHP execution time limit as configured on the webserver. This would potentially cut short the execution of a critical update stage, and thus the update would require manual recovery at best, or completely restoring  X2Engine to a backup at worst.&lt;br /&gt;
&lt;br /&gt;
==== Ways to get around this ====&lt;br /&gt;
In most cases, including on shared hosting, it should be possible to change the time limit on PHP. It is highly recommended that you either disable the timeout or set it to a very generous value (such as 300 seconds) while attempting to update via the web updater.&lt;br /&gt;
&lt;br /&gt;
== Using the Command Line Updater ==&lt;br /&gt;
This update method uses all of the same thoroughly-tested underpinnings and processes as the web updater, but does so all within a system command to the Yii console.&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
While in via SSH, or in a terminal emulator, or in a script, run the following command in the &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt; subdirectory of X2Engine:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./yiic update app&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the following optional arguments to the command line updater:&lt;br /&gt;
;&amp;lt;tt&amp;gt;--force&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; for this option (i.e. &amp;lt;tt&amp;gt;--force=1&amp;lt;/tt&amp;gt;) to proceed with the update even if compatibility issues were detected. Default: 0 (halt if compatibility issues were detected).&lt;br /&gt;
;&amp;lt;tt&amp;gt;--backup&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that a database backup should be performed first before updating, &amp;quot;0&amp;quot; to proceed without creating a backup first. Default: 1.&lt;br /&gt;
;&amp;lt;tt&amp;gt;--lock&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that the application should be locked during the final installation of the update package to prevent data entry during the sensitive moments when the database is being altered and the files updated, &amp;quot;0&amp;quot; to permit use even when changes to the app are being applied. Default: 0.&lt;br /&gt;
&lt;br /&gt;
If running the command gives you the following message:&lt;br /&gt;
&amp;lt;pre&amp;gt;The updater is now up-to-date and compliant with the updates server. Re-run the command to proceed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
That indicates that the updater first needed to self-update, and succeeded in doing so. You should then run the same command again to complete the update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Safer to use on large, mission-critical systems. The success of a software update (or upgrade) does not hinge upon a single web request completing successfully.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
More difficult to use; requires shell access to the web server. The console command must be run as the same system user that owns all the files and directories of X2Engine, in order to circumvent permission issues when manipulating file system objects.&lt;br /&gt;
&lt;br /&gt;
== Using the Automatic Updater ==&lt;br /&gt;
[[File:Auto-Update.png|400px|thumb|right|Auto-update scheduler on the Updater Settings page]]&lt;br /&gt;
This method uses the command line updater, but runs the command on your behalf using the local cron daemon. To configure, go to the Updater Settings Page, check the box titled &amp;quot;Update Automatically&amp;quot;, and save. If you have Professional Edition, you can manage the update schedule in addition to other automated tasks from the Cron Table settings page, which can also be found under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Automates the entire process of updating.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
Only available on UNIX-like systems that have a cron daemon, and which permit user access to the cron table.&lt;br /&gt;
&lt;br /&gt;
== Performing &amp;quot;Offline&amp;quot; Updates ==&lt;br /&gt;
In some server environments, the updater may not be able to access the updates server by itself, due to (for example) the PHP or network configuration, or because the &amp;quot;zip&amp;quot; PHP extension (requried) is unavailable or has been disabled. It is in these situations necessary to download the update package manually, unpack on the server (or unpack and upload the files to the server), and then run the updater. When performing updates in this way, the updater will not need to access the updates server, but will use the manually-downloaded and extracted package files present in the local filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Refreshing and Updating the Updater Utility ===&lt;br /&gt;
Before starting, note that the updater itself may have undergone changes; the X2Engine update process is subject to change. To address this technical challenge, the updater utility within X2Engine would normally first attempt to download the latest version of itself, in order to be compatible with the updates server. However, this sometimes will not work properly. If your X2Engine installation is running in a hosting environment that prevents it from making outbound HTTP requests (the [[requirements:|requirements check script]] should tell you if this is the case), you will need to do this manually. Also, in version 3.5.5, there was a bug in the updater utility's self-update methods.&lt;br /&gt;
&lt;br /&gt;
To update the updater utility:&lt;br /&gt;
# '''Determine the version of the updater utility package''' by visiting [https://x2planet.com/installs/updates/updateCheck this link]. You should see a page that's entirely blank except for a version number.&lt;br /&gt;
# '''Download the updater package.''' In versions 3.6 and later, a download link for this should be available through the ''Updater Settings'' page. Otherwise, use the following URL (if in Open Source Edition): &amp;lt;pre&amp;gt;https://x2planet.com/installs/updater.zip&amp;lt;/pre&amp;gt; Otherwise, if using Professional Edition, paste the following url, after replacing {key} with your license key, into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/updater-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# '''Extract the downloaded zip archive and upload''' the &amp;quot;protected&amp;quot; folder to your server. Merge folders and overwrite existing files when prompted.&lt;br /&gt;
# '''Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor''' and find the line that looks like this:&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$updaterVersion = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# '''Change the text inside the pair of single quotes''' on that line so that it is the current updater version obtained in step #1. &lt;br /&gt;
# '''Save the file.'''&lt;br /&gt;
&lt;br /&gt;
=== Obtaining the Update Package ===&lt;br /&gt;
First, use the link in the ''Updater Settings'' page entitled &amp;quot;Latest Update Package for Version...&amp;quot;, to download the update package. If the link in the Updater Settings page does not work, use the following URL to download the update package (if using Open Source Edition), replacing &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; with your X2Engine installation's version:&lt;br /&gt;
&amp;lt;pre&amp;gt;https://x2planet.com/installs/updates/{version}/none&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should receive, from that URL, a zip archive file called &amp;quot;update.zip&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If using Professional Edition, and your download link is not working, contact [mailto:customersupport@x2engine.com customer support].&lt;br /&gt;
&lt;br /&gt;
=== Unpacking the Update ===&lt;br /&gt;
[[File:Update-in-Progress.png|200px|frame|right|System flash message displaying notification of pending update.]]&lt;br /&gt;
The update package will be a zip archive. The ''contents'' of this archive must be extracted ''into'' a folder called &amp;quot;update&amp;quot; in the installation directory. So, in other words, after extracting and uploading to the server, there should be a folder called &amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt; alongside &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;framework&amp;lt;/tt&amp;gt; (etc). Within that folder (&amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt;) should be the following contents:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A file containing important data to be used by the updater&lt;br /&gt;
;&amp;lt;tt&amp;gt;contents.md5&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A message digest file containing MD5 checksums of all files in the archive excluding itself.&lt;br /&gt;
;&amp;lt;tt&amp;gt;source&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A sub-folder/directory, whose internal structure mimics that of X2Engine, containing files that were changed in the update. If there were no files changed (i.e. only database changes were in the update), this directory will not be present.&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A directory containing flat data files. These files are typically not used by the updater, but are useful if performing manual updates or recovery.&lt;br /&gt;
&lt;br /&gt;
=== Applying the Update ===&lt;br /&gt;
[[File:Steps-post-Unpack.png|200px|frame|right|Steps of the update process in the event of manually-extracted package (or resuming a preexisting update)]]&lt;br /&gt;
After unpacking, if you run the command line updater, it should (if no problems arise, i.e. incompatible package version) apply the update straight away. Otherwise, log into X2Engine, and you should see a system flash message notifying you of an update in progress. This message shows up whenever there is an update package folder (&amp;quot;update&amp;quot;) present on the server. If you then go to the updater page, once the page has finished loading, you should see that there only remain the steps &amp;quot;Review and confirm changes&amp;quot;, &amp;quot;Apply changes&amp;quot; and &amp;quot;Reload&amp;quot; remain to be completed. To complete the update, when ready, click &amp;quot;Update&amp;quot;; otherwise, to delete the &amp;quot;update&amp;quot; folder and cancel the update, click &amp;quot;Cancel&amp;quot; either on the updater page or in the system flash message.&lt;br /&gt;
&lt;br /&gt;
== Performing Updates Manually ==&lt;br /&gt;
While it is preferable to avoid manually applying changes to X2Engine, and you may never need to, it may in some circumstance or another actually be necessary to do so. For all such situations, the raw update data files generated by the X2Engine update build process are bundled with every update package to help you perform the operations described in [[#Introduction|&amp;quot;Introduction&amp;quot;]]. This section will describe how to perform each of those operations. &lt;br /&gt;
&lt;br /&gt;
=== Prerequisites ===&lt;br /&gt;
First and foremost, all shell commands listed here, and in general, any operations that create or modify files, ''must be run as the same system user under which the server runs PHP scripts.'' Otherwise, the ownership and/or permissions of files and directories will have to be changed after completing all changes.&lt;br /&gt;
&lt;br /&gt;
To obtain the update package (which will be necessary), refer to &amp;quot;[[#Obtaining_the_Update_Package|Obtaining the Update Package]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Furthermore, it is strongly advised that you first make a back-up copy of all files and of X2Engine's database. Your database backup would best be a full, exact copy, such as one that would be made by [http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html mysqldump], or the built-in export tools of [http://www.phpmyadmin.net/home_page/index.php PHPMyAdmin].&lt;br /&gt;
&lt;br /&gt;
The X2Engine global import/export utility (&amp;quot;Import All Data&amp;quot;/&amp;quot;Export All Data&amp;quot;) in Admin '''is not recommended by itself as the sole backup tool'''. This is especially important to note because of how this tool cannot re-create custom fields; they must exist in the target installation before the import. However, using it to ''supplement'' your standard database backup method will prove useful in the event that you:&lt;br /&gt;
# don't have any custom fields,&lt;br /&gt;
# cannot manage to update X2Engine with success, and&lt;br /&gt;
# wish to start over with a fresh installation.&lt;br /&gt;
In such a case, you will then be able to import your old data into the new installation.&lt;br /&gt;
&lt;br /&gt;
=== Introduction: Data Files ===&lt;br /&gt;
In [[#Unpacking_the_Update|Unpacking the update]], a rough overview of an update package's contents were given. In this section, the anatomy and purpose of each of the data files will be introduced.&lt;br /&gt;
&lt;br /&gt;
Included in every update package is two copies of the following data:&lt;br /&gt;
* Files to copy&lt;br /&gt;
* Files to delete&lt;br /&gt;
* Database changes to enact&lt;br /&gt;
* Migration scripts to run, if any&lt;br /&gt;
&lt;br /&gt;
The first, immediately visible place where all of this data is stored is in the file &amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;, as a JSON-encoded array. See the next section for how to use this file.&lt;br /&gt;
&lt;br /&gt;
The second place, which can be useful to users who do not wish to automate or script any step of a manual update, is in the files contained in the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; directory. Each file is named after the version to which it corresponds, and contains the data required to update ''to'' that version ''from the version immediately preceding it.''' Thus, the versions you should see in each file name are the versions above your X2Engine installation's current version, up to and including the version being updated to.&lt;br /&gt;
&lt;br /&gt;
In an update, the directory will contain the following subdirectories:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of files to copy. The format is each line containing a file path in the list.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of obsolete files for that version which will need to be deleted. The format is the same as in &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts corresponding to each version. Each file is formatted such that it can be directly run on the database as an SQL script, although this is not generally recommended. The list of SQL commands to run is a of the following format: &lt;br /&gt;
  [query 1];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [query 2];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [etc]&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts with similar naming and formatting conventions to the &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; scripts. See [[#The_structure_of_the_.22data.22_array|The structure of the &amp;quot;data&amp;quot; array]] for explanation of the purpose of &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== How to use the manifest for updating manually ===&lt;br /&gt;
If you so choose, you may write a script to perform all of the operations for an update in one fell swoop. If so, and if the preferred scripting language of choice has a JSON-parsing library available, you will be able to save yourself a lot of effort by using the main data file (&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;) to load all of the necessary data for the update.&lt;br /&gt;
&lt;br /&gt;
==== The structure the manifest ====&lt;br /&gt;
In updates, the contents of the plain-text file manifest.json are a JSON-encoded object containing the following properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the data corresponding to each intermediate version. It is, in essence, all the data stored in the files in &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt;. If the update is for an installation of X2Engine that is only one version behind, it will contain only one element. Each element of this array is itself an array containing respective data for the intermediate version.&lt;br /&gt;
;&amp;lt;tt&amp;gt;versions&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array listing the version numbers of each intermediate version in the update. Similar to &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, this array will contain only one element if updating to the next version, and that version ''will be the next version''.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of deletions of obsolete files across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of files added or changed across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''from'', or in other words, the version of X2Engine before the package is applied. '''This must be the same as the version of installation to which the package is being applied.''' If this is not so, the updater utility would exit immediately, as applying the changes in the update would have potentially harmful results to data and/or compatibility with future updates.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''to'', or in other words, the version of X2Engine before the package is applied.&lt;br /&gt;
;&amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the version of the updater utility for which the package was compiled. If the structure of the update package or of the manifest changes, the updater must also change to accommodate and recognize the changes; else it might try to access properties that don't exist, or ignore important ones that do. This is the single property of manifest.json that should never change.&lt;br /&gt;
;&amp;lt;tt&amp;gt;scenario&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, which should be &amp;quot;update&amp;quot; if the package is for an update. In the case of upgrades, which use most of the same functionality of the updater, it should be &amp;quot;upgrade&amp;quot;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation after the package is applied. It should be &amp;quot;opensource&amp;quot; for open source edition, &amp;quot;pro&amp;quot; for professional edition. In the case of an upgrade package, it differs from the &amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation before the package is applied. &lt;br /&gt;
;&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An integer denoting the timestamp of the release date of the version of X2Engine denoted by &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The structure of the &amp;quot;data&amp;quot; array ====&lt;br /&gt;
In updates, each version's data, contained in each element of the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, contains the following nested array properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files added for the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files deleted in the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of SQL commands to bring the database structure and contents into compliance with the version from the previous version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A prerequisite list of SQL commands that should be run before those in &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, and if any fail, the failures should be ignored. These are added to updates whenever it is necessary to normalize the database schema, i.e. in the event that some installations' databases may not be consistent with others, but the commands might fail on every other system. These SQL commands were intended to be &amp;quot;attempted&amp;quot;; they are not guaranteed to succeed on all installations. An example of when this is necessary would be adding a foreign key constraint when in previous versions the storage engine was left to the database's default settings in old versions. MyISAM does not natively support foreign key constraints. Thus, to standardize this across all installations, including installations on servers where the storage engine defaulted to MyISAM, the constraint must first be dropped (in a &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; command) before the relevant tables are changed to the InnoDB storage engine, and the constraint is added back again, in the usual &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;version&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The version number&lt;br /&gt;
;&amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The edition&lt;br /&gt;
;&amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of PHP scripts that would be run in an update for this version. These are a subset of fileList and are typically used for advanced database operations, i.e. restructuring values of JSON-encoded fields, which are extremely impractical (or impossible) to perform using raw SQL commands. The files contained in the list all have paths that look like &amp;quot;&amp;lt;tt&amp;gt;protected/migrations/{version}/{timestamp}-name-of-operations.php&amp;lt;/tt&amp;gt;&amp;quot;. Note, if this array is not empty, you may need to reverse-engineer or otherwise copy and re-purpose/re-write any migration scripts listed in order to perform their necessary operations. This is because they may depend on running within a Yii Framework environment, i.e. will contain references to components of the Yii application singleton such as &amp;quot;db&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Note that in upgrades, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; are replaced by &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; (respectively), and &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; are missing (because they aren't necessary).&lt;br /&gt;
&lt;br /&gt;
==== What you will need for the update, if using the manifest ====&lt;br /&gt;
''Among this data, what you will need:'' &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, which will give you the full list of files that need to be deleted, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, which will give you the full list of files to copy from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory, &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;, which give you values with which to update the main X2Engine configuration file, and, within each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, the sub-array properties &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;, with which you will construct the list of database commands to run. &lt;br /&gt;
&lt;br /&gt;
A procedure for manually updating X2Engine, with data from manifest.json, thus might look like this:&lt;br /&gt;
# For each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; (or in other words, each version's changes):&lt;br /&gt;
## Run all the SQL commands in its corresponding &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; sub-array, ignoring errors&lt;br /&gt;
## Run all SQL commands in its &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; array&lt;br /&gt;
## Perform database operations that would be equivalent to those performed in migration scripts&lt;br /&gt;
# For each file in the cumulative list &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, copy it from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory into its analogous location in the installation directory of X2Engine, creating parent directories as necessary if they don't already exist.&lt;br /&gt;
# Delete all files in the cumulative list &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; from the installation directory&lt;br /&gt;
# Clear the data cache files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt; and the temporary asset folders in &amp;lt;tt&amp;gt;assets/&amp;lt;/tt&amp;gt;&lt;br /&gt;
# Clear all session data (&amp;lt;tt&amp;gt;x2_sessions&amp;lt;/tt&amp;gt;) and auth cache data (&amp;lt;tt&amp;gt;x2_auth_cache&amp;lt;/tt&amp;gt;).&lt;br /&gt;
# Update the main X2Engine configuration (&amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt;) file, setting:&lt;br /&gt;
## &amp;lt;tt&amp;gt;$buildDate&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt; property,&lt;br /&gt;
## &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt; property, and &lt;br /&gt;
## &amp;lt;tt&amp;gt;$updaterVersion&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
&lt;br /&gt;
=== Enacting Database Changes ===&lt;br /&gt;
Inside the &amp;lt;tt&amp;gt;update/sqlList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/sqlForce&amp;lt;/tt&amp;gt; folder is a list of &amp;lt;tt&amp;gt;.sql&amp;lt;/tt&amp;gt; files, named according to their corresponding version's update, that can essentially be run as SQL scripts to perform the update. Note, however, that '''if the update traverses more than one version, the scripts must be run in the order of their corresponding version.''' This is because, in the database changes for any version, it is assumed that the database schema and contents conform to the version that immediately preceded it. The PHP function [[phpfunc:version-compare|version_compare]], and the method &amp;quot;[http://pythonhosted.org/setuptools/pkg_resources.html#parsing-utilities parse_version]&amp;quot;, in the pkg_resources module of setuptools, should correctly identify which version should come first. Note, the [[#The_structure_the_manifest|&amp;quot;versions&amp;quot; property of the manifest]] will contain the list of versions in the correct order.&lt;br /&gt;
&lt;br /&gt;
Furthermore, note, &amp;quot;sqlList&amp;quot; and &amp;quot;sqlForce&amp;quot; may not encompass all necessary database changes; if there are any migration scripts in the update, the operations they perform will similarly need to be performed in the current update (see the description of &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; in [[#The_structure_of_the_.22data.22_array|&amp;quot;the structure of the 'data' array&amp;quot;]]).&lt;br /&gt;
&lt;br /&gt;
In summary, for each version traversed in the update, the following operations should be run in order, with &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version number of the version in question:&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlForce/{version}.sql&amp;lt;/tt&amp;gt; file,''' in the order they appear in the file. Ignore if any commands fail. Note, if passed to the MySQL client as a script, any failures might trigger a halt in execution and thus skip the remainder of the commands.&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlList/{version}.sql&amp;lt;/tt&amp;gt; file.'''&lt;br /&gt;
# '''Perform operations equivalent to those in each of the migration scripts, if any.''' The first part of a script file name is the timestamp it was added to the codebase, and this also dictates the order in which they should be run.&lt;br /&gt;
&lt;br /&gt;
=== Copying new files ===&lt;br /&gt;
Files added/changed in the new version are stored in the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory. In an update, they should be copied into the installation directory, replacing any existing files of the same name/path. If the &amp;lt;tt&amp;gt;rsync&amp;lt;/tt&amp;gt; utility is available on the system, you can use one command to safely copy these files into the installation directory as follows (executed in that same directory):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rsync -avc update/source/ ./&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, you will need to use &amp;lt;tt&amp;gt;cat&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;cp&amp;lt;/tt&amp;gt;, two utilities that should be available on every UNIX-like system, and the lists of files added in each intermediate version. These lists are stored in the folder &amp;lt;tt&amp;gt;update/data/fileList&amp;lt;/tt&amp;gt; and named like &amp;quot;{version}.txt&amp;quot; (&amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version to which it applies), each file on its own line in that file. Run the following command, ignoring errors:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/fileList/*; do for l in $(cat $f); do cp update/source/&amp;quot;$l&amp;quot; ./&amp;quot;$l&amp;quot; ; done ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting old files ===&lt;br /&gt;
Files to be deleted are listed in a similar manner as the files to be added: one file per line. The list of files to delete are located at files with paths that look like this: &amp;lt;tt&amp;gt;update/data/deletionList/{version}.txt&amp;lt;/tt&amp;gt;&lt;br /&gt;
So, for an update from 5.5 to 5.7 with intermediate version 5.6 (for instance), the files would be &amp;lt;tt&amp;gt;update/data/deletionList/5.6&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/data/deletionList/5.7&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is only one version through which to update, the operation is very simple, and can be performed in one UNIX/Linux (bash) shell command as follows (if the version is 3.6.3, for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
xargs -a update/data/deletionList/3.6.3.txt -d&amp;quot;\n&amp;quot; rm -fv&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, if there are multiple versions, this may be a more tricky proposition. The necessity arises to create a cumulative list, because there might be the possibility that a file is deleted in one version and re-established in a new version. Nevertheless, while this is possible, it is highly unlikely, and in most cases it is safe to simply iterate over all the &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; data files and delete all the files listed therein. As a bash command:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/deletionList/*; do xargs -a $f -d&amp;quot;\n&amp;quot; rm -fv ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Editing the Configuration File ===&lt;br /&gt;
This final step in the update process, before refreshing, should not be done manually unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a fresh installation at the same version as the one to which you have updated.&lt;br /&gt;
&lt;br /&gt;
To update the configuration manually:&lt;br /&gt;
# Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor&lt;br /&gt;
# Find the line with the &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; variable in it. It should look like this (but with the version from which you are updating): &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$version = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change the value in quotes to the current version being updated to, and save the file.&lt;br /&gt;
&lt;br /&gt;
=== Clearing caches and refreshing the application ===&lt;br /&gt;
It is generally always a good idea to clear the application's server-side cache and other ephemeral files/data after updating. This is taken care of automatically by the built-in updater, but barring proper use and functioning of that, the operations can be performed manually as follows: &lt;br /&gt;
;clear the data cache&lt;br /&gt;
: delete all &amp;quot;&amp;lt;tt&amp;gt;.bin&amp;lt;/tt&amp;gt;&amp;quot; files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -fv protected/runtime/cache/*.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear web assets&lt;br /&gt;
: delete all sub-folders of &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt; by running this command (in the installation directory): &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -r assets/*/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear the &amp;quot;auth cache&amp;quot;&lt;br /&gt;
: Flush the auth cache table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;&lt;br /&gt;
DELETE FROM `x2_auth_cache` WHERE 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;log all users out&lt;br /&gt;
: Flush the sessions table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;DELETE FROM `x2_sessions` WHERE 1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Upgrading =&lt;br /&gt;
&lt;br /&gt;
== Using the Web Interface ==&lt;br /&gt;
[[File:Software-Upgrade-Registration.png|400px|thumb|right|Software upgrade page, in the first step (registration)]]&lt;br /&gt;
To upgrade to Professional Edition&lt;br /&gt;
# Purchase X2Engine Professional Edition Download. Note the registration info in the activation email.&lt;br /&gt;
# Go to the Admin page in X2Engine and find &amp;quot;Upgrade X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot;.&lt;br /&gt;
# In the ''Upgrade'' page, enter your registration info into the form and click &amp;quot;Register&amp;quot; to continue. Once complete, you will see a list of changes similar to those displayed when performing an update.&lt;br /&gt;
# Review the changes to be applied and then proceed by clicking &amp;quot;Upgrade&amp;quot;, similar to performing an update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Offline&amp;quot; Upgrading ==&lt;br /&gt;
This works in exactly the same way as offline updating. However, to obtain your upgrade package, which you will extract into a folder called &amp;quot;update&amp;quot; on the X2Engine web server, contact [mailto:customersupport@x2engine.com customer support]. Furthermore, you will need to run the following database command on your X2Engine database when done with the upgrade, replacing &amp;lt;tt&amp;gt;{product key}&amp;lt;/tt&amp;gt; with your product key, to allow your installation to receive Professional Edition software updates:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{product key}' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Manually Upgrading ==&lt;br /&gt;
The process of upgrading is very similar to that of updating, and uses all of the same underpinnings as the updater. Thus, almost all of the above information and procedures will apply to upgrades. However, note the following crucial differences:&lt;br /&gt;
&lt;br /&gt;
* '''X2Engine must be at the latest available version in order to upgrade.'''&lt;br /&gt;
* '''To obtain the upgrade package, you must be an owner of an X2Engine Professional Edition Download-type license.''' Contact [mailto:customersupport@x2engine.com customer support] for further details.&lt;br /&gt;
* '''There will be only one &amp;quot;intermediate&amp;quot; version.''' The changes to apply will move X2Engine from a version to that same version, applying instead the necessary changes to switch its ''edition'' from Open Source to Professional.&lt;br /&gt;
* '''There are no file deletions.'''&lt;br /&gt;
* '''The properties of the package manifest differ slightly.''' In upgrades, the properties of the data object are &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, etc. Note also, the update data is in aptly-named sub-folders of &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt; as well, and there are no &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; lists to handle.&lt;br /&gt;
* '''The configuration is not changed.''' Instead, the value stored in the field &amp;lt;tt&amp;gt;unique_id&amp;lt;/tt&amp;gt; of the admin record in the database table &amp;lt;tt&amp;gt;x2_admin&amp;lt;/tt&amp;gt; must be changed to the license key, and the &amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt; field must be changed to &amp;quot;pro&amp;quot;. To perform these both, run the following command on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''It is not necessary to clear caches or user sessions when done.''' These operations are performed in updates to eliminate ephemeral (session-ingrained) data that may no longer be applicable and thus cause problems. Upgrades will rarely, if ever, invalidate this data.&lt;br /&gt;
&lt;br /&gt;
= Updating to Beta Versions = &lt;br /&gt;
Beginning at version 4.1.2, after much advocacy to adopt this practice, X2Engine began a two-phase release process in which each version would be released first as a beta, then to the broader public after initial bug finds. The purpose behind this is to avoid updating production installations of X2Engine to new versions that have had zero public exposure, and thus avoid bugs that were not found in initial QA testing (due to a incompatibility with a particular server environment or configuration of X2Engine).&lt;br /&gt;
&lt;br /&gt;
By default, the updater will not update to beta versions. The updater must be configured to use the proper URL on the update server for beta release data, by the following procedure:&lt;br /&gt;
# Create a file &amp;quot;constants-custom.php&amp;quot; in the root level of X2Engine. You can easily do this by renaming &amp;quot;constants-custom.example.php&amp;quot; to that name (remove &amp;quot;.custom&amp;quot;).&lt;br /&gt;
# Look for the following line, and in it, change &amp;quot;false&amp;quot; to &amp;quot;true&amp;quot;: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('X2_UPDATE_BETA') or define('X2_UPDATE_BETA',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The advantage of enabling beta releases is that new features will be available much sooner. Additionally, fixes to bugs will also be available sooner. The main disadvantage of enabling beta releases is that they are never guaranteed to be as stable as non-beta releases. We encourage users to clone their installations of X2Engine, if able, and use beta versions on them, and report any issues they encounter for their particular server environment/special configuration/custom implementation of X2Engine. This way, any issues that arise can be fixed in the stable release. The more users participate in beta testing, the more stable the non-beta releases will become.&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
== User interface (UI) &amp;quot;bugs&amp;quot; after the update ==&lt;br /&gt;
It is extremely common (and easily remedied) that after an important update, certain features of the user interface of X2Engine may not work properly. This is caused, in nearly all cases, by the web browser's cache, which contains out-of-date copies of X2Engine's essential user interface files. To solve these problems, the first step to try is clearing the cache. A step-by-step guide to doing so is given in [[wikihow:Clear-Your-Browser%27s-Cache]].&lt;br /&gt;
&lt;br /&gt;
== Errors in the updater itself == &lt;br /&gt;
When all else fails, the updater may have to be patched due to an irreconcilable backwards compatibility issue. Fortunately, if one has read/write access to the source files on the webserver, this is straightforward to fix. See [[#Refreshing_and_Updating_the_Updater_Utility|Refreshing and Updating the Updater Utility]].&lt;br /&gt;
&lt;br /&gt;
== Professional Edition not in effect after upgrading ==&lt;br /&gt;
This may be caused by Professional Edition not getting &amp;quot;switched on&amp;quot; properly, despite all the necessary filesystem and database changes already being in effect. First, to see if this scenario applies to you, look for a folder &amp;quot;&amp;lt;tt&amp;gt;reports&amp;lt;/tt&amp;gt;&amp;quot; inside &amp;lt;tt&amp;gt;protected/modules&amp;lt;/tt&amp;gt;, and a table &amp;lt;tt&amp;gt;x2_reports&amp;lt;/tt&amp;gt; in the X2Engine database. If you find these things, you can try the following to enable Professional Edition:&lt;br /&gt;
# Find the file &amp;lt;tt&amp;gt;constants.php&amp;lt;/tt&amp;gt; in the installation directory. Open it in a text editor, and look for the following line: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('PRO_VERSION') or define('PRO_VERSION',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; on that line, and save the file.&lt;br /&gt;
# Run the following SQL command (replacing &amp;lt;tt&amp;gt;{license key}&amp;lt;/tt&amp;gt; with your X2Engine license key) on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General Procedures to Try ==&lt;br /&gt;
It may sometimes be possible that the update did not finish cleanly. In such cases, it will be necessary to manually re-perform some of the final operations in the update, described in [[#Introduction|Introduction]]. &lt;br /&gt;
&lt;br /&gt;
=== Refresh the Source Code ===&lt;br /&gt;
This step is not really the best step to try first, as it is usually more time-consuming. It entails re-uploading the entire application, in order to reset all non-ephemeral (source code) files to what they should be in a fresh installation of X2Engine. To do this:&lt;br /&gt;
# Download the refresh package. There should be a download link for this in the ''Updater Settings'' page. Otherwise, if that doesn't work and you're using Open Source edition, use [https://x2planet.com/installs/refresh.zip this link], or if using Professional Edition, paste the following url (after replacing {key} with your license key) into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/refresh-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Upload the ''contents'' of the downloaded zip archive to the installation directory, overwriting existing files.&lt;br /&gt;
&lt;br /&gt;
=== Clear Caches ===&lt;br /&gt;
See &amp;quot;[[#Clearing_caches_and_refreshing_the_application|Clearing Caches...]]&amp;quot; for details.&lt;br /&gt;
&lt;br /&gt;
=== Regenerate the Configuration ===&lt;br /&gt;
Again, this should not be done unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a new installation at the same version as the one you updated to. Refer to [[#Editing_the_Configuration_File|Editing the Configuration File]].&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1839</id>
		<title>Software Updates and Upgrades</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1839"/>
				<updated>2014-06-18T23:36:20Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Enabling Beta Releases */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
[[File:Update_Process.svg|200px|thumb|right|Top-level overview of the update process]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
The following things happen in typical a software update (or upgrade):&lt;br /&gt;
* Files that are new in the new version are added&lt;br /&gt;
* Files that have been changed in the new version are replaced with the new versions of those files &lt;br /&gt;
* Files that are no longer used in the new version are deleted&lt;br /&gt;
* Changes to the database are made to make the database compatible with the new version of X2Engine&lt;br /&gt;
* The main configuration file, &amp;lt;tt&amp;gt;X2Config.php&amp;lt;/tt&amp;gt;, is edited to reflect the new state of the application.&lt;br /&gt;
* X2Engine data caches are cleared&lt;br /&gt;
&lt;br /&gt;
These operations are typically handled by the updater utility component [[x2doc:UpdaterBehavior|UpdaterBehavior]] and controlled via the in-app web-based updater or the command line updater. This article will cover both how to use said interfaces to the updater, and how to perform updates manually or with the &amp;quot;offline&amp;quot; method if the need arises, in addition to troubleshooting and recovering from an update that did not complete cleanly.&lt;br /&gt;
&lt;br /&gt;
= Disclaimer =&lt;br /&gt;
This article covers the standard and recommended update/upgrade methods, in addition to alternate methods and supplemental procedures. As general rules and precautions for using this article:&lt;br /&gt;
# ''If you do not fully and completely understand any step of any instructions given in this article, '''do not''' follow them''; ask for help instead on [http://x2community.com the forums] or through contacting X2Engine Technical Support if you are an existing customer.&lt;br /&gt;
# You should always make a backup of all of your files ''and'' a full backup copy of your database, preferably in a format (i.e. SQL script) that can be used to create a verbatim/carbon copy of the original database, with all of the features of the original schema, '''before''' attempting any steps of the update process manually.&lt;br /&gt;
# Some of the procedures outlined in this article involve manually creating or modifying files. Note that the same filesystem prerequisite for installation, namely that the file ownership must match the user under which PHP executes on the web server, applies to these steps.&lt;br /&gt;
&lt;br /&gt;
Furthermore, '''in regard to uploading files:''' note that any instructions to upload folders into X2Engine, and replace/merge contents, refer to replacing files in X2Engine with files in the archive at analogous paths. For example, if you have a zip file that contains a folder &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, and inside of that is a folder &amp;lt;tt&amp;gt;components&amp;lt;/tt&amp;gt;, and inside of that is a file &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt;, and the instructions are to upload/replace in X2Engine, you should upload &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt; to replace the existing file of the same name in &amp;lt;tt&amp;gt;protected/components&amp;lt;/tt&amp;gt; inside of X2Engine.&lt;br /&gt;
&lt;br /&gt;
X2Engine Inc. cannot be held accountable for damage/errors caused by any missteps or mistakes in manually-performed update operations. If in doubt, make a sandbox/clone of your X2Engine installation by copying the files and database to a different server, and test performing the operations on that before proceeding with your production install of X2Engine.&lt;br /&gt;
&lt;br /&gt;
= Update Notifications =&lt;br /&gt;
[[File:Update Notification.png|200px|frame|right|System flash message displaying software update notification.]]&lt;br /&gt;
To get notified of software updates, unless already using X2Engine Professional Edition, one must first &amp;quot;subscribe&amp;quot; to them. This can be done via checking the box &amp;quot;Notify me of software updates&amp;quot; in the installation form. If X2Engine is already installed, this can be done by going to &amp;quot;Updater Settings&amp;quot; from the Admin page and enabling them. If using Professional Edition, these steps are not not necessary.&lt;br /&gt;
&lt;br /&gt;
Once that has been completed, set the update notification interval as desired in &amp;quot;Updater Settings&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Update Methods =&lt;br /&gt;
There are, in essence, just three basic methods of updating, and variations on each. The three main methods are: &lt;br /&gt;
# web updater,&lt;br /&gt;
# command line updater, and&lt;br /&gt;
# manually.&lt;br /&gt;
In each case, an update package from the update data server is used as the authoritative list of changes to apply, but in the non-manual methods, X2Engine's own updater class does all the work applying changes from said package.&lt;br /&gt;
&lt;br /&gt;
In web and command line updates, there is also the option to first download and unpack the package manually (see [[#Performing_.22Offline.22_Updates|performing &amp;quot;offline&amp;quot; updates]]). This circumvents the need for the local server (on which X2Engine is hosted) to be connected to the internet. Similarly, in the case of manual updates, it is not necessary for the web server to actually have an internet connection.&lt;br /&gt;
&lt;br /&gt;
== Using the Web Updater ==&lt;br /&gt;
[[File:Updater-1.png|300px|thumb|right|The updater utility page.]]&lt;br /&gt;
The web updater can be accessed by any of the following methods:&lt;br /&gt;
* Using the link in the notification message at the top of the window in versions later than 3.5.5&lt;br /&gt;
* By going to the Admin page and clicking the &amp;quot;Update&amp;quot; button at the top, in earlier versions, with update notifications enabled&lt;br /&gt;
* Through &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
First, navigate to the web updater page, and '''do not''' cancel the request (stop or hit your browser's &amp;quot;back&amp;quot; button) until the page has loaded. If the updater utility has undergone changes and needs to self-update to be compatible with the software updates server, this is when it will do so. Thus, it is critical that you do not interrupt this process. If the page does not load, or displays the error message that dependencies could not be retrieved, you may have to [[#Performing_.22Offline.22_Updates|perform an &amp;quot;offline&amp;quot; update]].&lt;br /&gt;
&lt;br /&gt;
There are three ways of getting to the updater page. One is to simply click the link given in the update notification flash message, if available. The other is to click on the &amp;quot;Update&amp;quot; button at the top of the admin page. If software update notifications are not enabled, one can manually check for and install an update by going to &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
Next, make a backup of the database and files (recommended), follow the instructions on the page, and click the &amp;quot;Update&amp;quot; button when ready.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
It is generally quick and is the easiest method to use, and it does not require shell/SSH access to the web server.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
It is not recommended for use on large installations of X2Engine, i.e. containing over a million records.&lt;br /&gt;
&lt;br /&gt;
==== Explanation ====&lt;br /&gt;
The inherent weakness of the web updater (or any web-based updater to any web application) is that it must always rely upon web requests to the server, and it thus must perform extremely critical operations for the update all within the scope of a web request. Web requests, it goes without saying, are not always reliable. For example, one's internet connection might get cut off due being on a wireless network and encountering interference. Furthermore, if any operations take an exceedingly long amount of time, e.g. because of the size of the data set on which the update must operate, the web request for those operations may take longer than the PHP execution time limit as configured on the webserver. This would potentially cut short the execution of a critical update stage, and thus the update would require manual recovery at best, or completely restoring  X2Engine to a backup at worst.&lt;br /&gt;
&lt;br /&gt;
==== Ways to get around this ====&lt;br /&gt;
In most cases, including on shared hosting, it should be possible to change the time limit on PHP. It is highly recommended that you either disable the timeout or set it to a very generous value (such as 300 seconds) while attempting to update via the web updater.&lt;br /&gt;
&lt;br /&gt;
== Using the Command Line Updater ==&lt;br /&gt;
This update method uses all of the same thoroughly-tested underpinnings and processes as the web updater, but does so all within a system command to the Yii console.&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
While in via SSH, or in a terminal emulator, or in a script, run the following command in the &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt; subdirectory of X2Engine:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./yiic update app&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the following optional arguments to the command line updater:&lt;br /&gt;
;&amp;lt;tt&amp;gt;--force&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; for this option (i.e. &amp;lt;tt&amp;gt;--force=1&amp;lt;/tt&amp;gt;) to proceed with the update even if compatibility issues were detected. Default: 0 (halt if compatibility issues were detected).&lt;br /&gt;
;&amp;lt;tt&amp;gt;--backup&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that a database backup should be performed first before updating, &amp;quot;0&amp;quot; to proceed without creating a backup first. Default: 1.&lt;br /&gt;
;&amp;lt;tt&amp;gt;--lock&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that the application should be locked during the final installation of the update package to prevent data entry during the sensitive moments when the database is being altered and the files updated, &amp;quot;0&amp;quot; to permit use even when changes to the app are being applied. Default: 0.&lt;br /&gt;
&lt;br /&gt;
If running the command gives you the following message:&lt;br /&gt;
&amp;lt;pre&amp;gt;The updater is now up-to-date and compliant with the updates server. Re-run the command to proceed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
That indicates that the updater first needed to self-update, and succeeded in doing so. You should then run the same command again to complete the update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Safer to use on large, mission-critical systems. The success of a software update (or upgrade) does not hinge upon a single web request completing successfully.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
More difficult to use; requires shell access to the web server. The console command must be run as the same system user that owns all the files and directories of X2Engine, in order to circumvent permission issues when manipulating file system objects.&lt;br /&gt;
&lt;br /&gt;
== Using the Automatic Updater ==&lt;br /&gt;
[[File:Auto-Update.png|400px|thumb|right|Auto-update scheduler on the Updater Settings page]]&lt;br /&gt;
This method uses the command line updater, but runs the command on your behalf using the local cron daemon. To configure, go to the Updater Settings Page, check the box titled &amp;quot;Update Automatically&amp;quot;, and save. If you have Professional Edition, you can manage the update schedule in addition to other automated tasks from the Cron Table settings page, which can also be found under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Automates the entire process of updating.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
Only available on UNIX-like systems that have a cron daemon, and which permit user access to the cron table.&lt;br /&gt;
&lt;br /&gt;
== Performing &amp;quot;Offline&amp;quot; Updates ==&lt;br /&gt;
In some server environments, the updater may not be able to access the updates server by itself, due to (for example) the PHP or network configuration, or because the &amp;quot;zip&amp;quot; PHP extension (requried) is unavailable or has been disabled. It is in these situations necessary to download the update package manually, unpack on the server (or unpack and upload the files to the server), and then run the updater. When performing updates in this way, the updater will not need to access the updates server, but will use the manually-downloaded and extracted package files present in the local filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Refreshing and Updating the Updater Utility ===&lt;br /&gt;
Before starting, note that the updater itself may have undergone changes; the X2Engine update process is subject to change. To address this technical challenge, the updater utility within X2Engine would normally first attempt to download the latest version of itself, in order to be compatible with the updates server. However, this sometimes will not work properly. If your X2Engine installation is running in a hosting environment that prevents it from making outbound HTTP requests (the [[requirements:|requirements check script]] should tell you if this is the case), you will need to do this manually. Also, in version 3.5.5, there was a bug in the updater utility's self-update methods.&lt;br /&gt;
&lt;br /&gt;
To update the updater utility:&lt;br /&gt;
# '''Determine the version of the updater utility package''' by visiting [https://x2planet.com/installs/updates/updateCheck this link]. You should see a page that's entirely blank except for a version number.&lt;br /&gt;
# '''Download the updater package.''' In versions 3.6 and later, a download link for this should be available through the ''Updater Settings'' page. Otherwise, use the following URL (if in Open Source Edition): &amp;lt;pre&amp;gt;https://x2planet.com/installs/updater.zip&amp;lt;/pre&amp;gt; Otherwise, if using Professional Edition, paste the following url, after replacing {key} with your license key, into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/updater-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# '''Extract the downloaded zip archive and upload''' the &amp;quot;protected&amp;quot; folder to your server. Merge folders and overwrite existing files when prompted.&lt;br /&gt;
# '''Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor''' and find the line that looks like this:&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$updaterVersion = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# '''Change the text inside the pair of single quotes''' on that line so that it is the current updater version obtained in step #1. &lt;br /&gt;
# '''Save the file.'''&lt;br /&gt;
&lt;br /&gt;
=== Obtaining the Update Package ===&lt;br /&gt;
First, use the link in the ''Updater Settings'' page entitled &amp;quot;Latest Update Package for Version...&amp;quot;, to download the update package. If the link in the Updater Settings page does not work, use the following URL to download the update package (if using Open Source Edition), replacing &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; with your X2Engine installation's version:&lt;br /&gt;
&amp;lt;pre&amp;gt;https://x2planet.com/installs/updates/{version}/none&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should receive, from that URL, a zip archive file called &amp;quot;update.zip&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If using Professional Edition, and your download link is not working, contact [mailto:customersupport@x2engine.com customer support].&lt;br /&gt;
&lt;br /&gt;
=== Unpacking the Update ===&lt;br /&gt;
[[File:Update-in-Progress.png|200px|frame|right|System flash message displaying notification of pending update.]]&lt;br /&gt;
The update package will be a zip archive. The ''contents'' of this archive must be extracted ''into'' a folder called &amp;quot;update&amp;quot; in the installation directory. So, in other words, after extracting and uploading to the server, there should be a folder called &amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt; alongside &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;framework&amp;lt;/tt&amp;gt; (etc). Within that folder (&amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt;) should be the following contents:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A file containing important data to be used by the updater&lt;br /&gt;
;&amp;lt;tt&amp;gt;contents.md5&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A message digest file containing MD5 checksums of all files in the archive excluding itself.&lt;br /&gt;
;&amp;lt;tt&amp;gt;source&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A sub-folder/directory, whose internal structure mimics that of X2Engine, containing files that were changed in the update. If there were no files changed (i.e. only database changes were in the update), this directory will not be present.&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A directory containing flat data files. These files are typically not used by the updater, but are useful if performing manual updates or recovery.&lt;br /&gt;
&lt;br /&gt;
=== Applying the Update ===&lt;br /&gt;
[[File:Steps-post-Unpack.png|200px|frame|right|Steps of the update process in the event of manually-extracted package (or resuming a preexisting update)]]&lt;br /&gt;
After unpacking, if you run the command line updater, it should (if no problems arise, i.e. incompatible package version) apply the update straight away. Otherwise, log into X2Engine, and you should see a system flash message notifying you of an update in progress. This message shows up whenever there is an update package folder (&amp;quot;update&amp;quot;) present on the server. If you then go to the updater page, once the page has finished loading, you should see that there only remain the steps &amp;quot;Review and confirm changes&amp;quot;, &amp;quot;Apply changes&amp;quot; and &amp;quot;Reload&amp;quot; remain to be completed. To complete the update, when ready, click &amp;quot;Update&amp;quot;; otherwise, to delete the &amp;quot;update&amp;quot; folder and cancel the update, click &amp;quot;Cancel&amp;quot; either on the updater page or in the system flash message.&lt;br /&gt;
&lt;br /&gt;
== Performing Updates Manually ==&lt;br /&gt;
While it is preferable to avoid manually applying changes to X2Engine, and you may never need to, it may in some circumstance or another actually be necessary to do so. For all such situations, the raw update data files generated by the X2Engine update build process are bundled with every update package to help you perform the operations described in [[#Introduction|&amp;quot;Introduction&amp;quot;]]. This section will describe how to perform each of those operations. &lt;br /&gt;
&lt;br /&gt;
=== Prerequisites ===&lt;br /&gt;
First and foremost, all shell commands listed here, and in general, any operations that create or modify files, ''must be run as the same system user under which the server runs PHP scripts.'' Otherwise, the ownership and/or permissions of files and directories will have to be changed after completing all changes.&lt;br /&gt;
&lt;br /&gt;
To obtain the update package (which will be necessary), refer to &amp;quot;[[#Obtaining_the_Update_Package|Obtaining the Update Package]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Furthermore, it is strongly advised that you first make a back-up copy of all files and of X2Engine's database. Your database backup would best be a full, exact copy, such as one that would be made by [http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html mysqldump], or the built-in export tools of [http://www.phpmyadmin.net/home_page/index.php PHPMyAdmin].&lt;br /&gt;
&lt;br /&gt;
The X2Engine global import/export utility (&amp;quot;Import All Data&amp;quot;/&amp;quot;Export All Data&amp;quot;) in Admin '''is not recommended by itself as the sole backup tool'''. This is especially important to note because of how this tool cannot re-create custom fields; they must exist in the target installation before the import. However, using it to ''supplement'' your standard database backup method will prove useful in the event that you:&lt;br /&gt;
# don't have any custom fields,&lt;br /&gt;
# cannot manage to update X2Engine with success, and&lt;br /&gt;
# wish to start over with a fresh installation.&lt;br /&gt;
In such a case, you will then be able to import your old data into the new installation.&lt;br /&gt;
&lt;br /&gt;
=== Introduction: Data Files ===&lt;br /&gt;
In [[#Unpacking_the_Update|Unpacking the update]], a rough overview of an update package's contents were given. In this section, the anatomy and purpose of each of the data files will be introduced.&lt;br /&gt;
&lt;br /&gt;
Included in every update package is two copies of the following data:&lt;br /&gt;
* Files to copy&lt;br /&gt;
* Files to delete&lt;br /&gt;
* Database changes to enact&lt;br /&gt;
* Migration scripts to run, if any&lt;br /&gt;
&lt;br /&gt;
The first, immediately visible place where all of this data is stored is in the file &amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;, as a JSON-encoded array. See the next section for how to use this file.&lt;br /&gt;
&lt;br /&gt;
The second place, which can be useful to users who do not wish to automate or script any step of a manual update, is in the files contained in the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; directory. Each file is named after the version to which it corresponds, and contains the data required to update ''to'' that version ''from the version immediately preceding it.''' Thus, the versions you should see in each file name are the versions above your X2Engine installation's current version, up to and including the version being updated to.&lt;br /&gt;
&lt;br /&gt;
In an update, the directory will contain the following subdirectories:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of files to copy. The format is each line containing a file path in the list.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of obsolete files for that version which will need to be deleted. The format is the same as in &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts corresponding to each version. Each file is formatted such that it can be directly run on the database as an SQL script, although this is not generally recommended. The list of SQL commands to run is a of the following format: &lt;br /&gt;
  [query 1];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [query 2];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [etc]&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts with similar naming and formatting conventions to the &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; scripts. See [[#The_structure_of_the_.22data.22_array|The structure of the &amp;quot;data&amp;quot; array]] for explanation of the purpose of &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== How to use the manifest for updating manually ===&lt;br /&gt;
If you so choose, you may write a script to perform all of the operations for an update in one fell swoop. If so, and if the preferred scripting language of choice has a JSON-parsing library available, you will be able to save yourself a lot of effort by using the main data file (&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;) to load all of the necessary data for the update.&lt;br /&gt;
&lt;br /&gt;
==== The structure the manifest ====&lt;br /&gt;
In updates, the contents of the plain-text file manifest.json are a JSON-encoded object containing the following properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the data corresponding to each intermediate version. It is, in essence, all the data stored in the files in &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt;. If the update is for an installation of X2Engine that is only one version behind, it will contain only one element. Each element of this array is itself an array containing respective data for the intermediate version.&lt;br /&gt;
;&amp;lt;tt&amp;gt;versions&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array listing the version numbers of each intermediate version in the update. Similar to &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, this array will contain only one element if updating to the next version, and that version ''will be the next version''.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of deletions of obsolete files across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of files added or changed across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''from'', or in other words, the version of X2Engine before the package is applied. '''This must be the same as the version of installation to which the package is being applied.''' If this is not so, the updater utility would exit immediately, as applying the changes in the update would have potentially harmful results to data and/or compatibility with future updates.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''to'', or in other words, the version of X2Engine before the package is applied.&lt;br /&gt;
;&amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the version of the updater utility for which the package was compiled. If the structure of the update package or of the manifest changes, the updater must also change to accommodate and recognize the changes; else it might try to access properties that don't exist, or ignore important ones that do. This is the single property of manifest.json that should never change.&lt;br /&gt;
;&amp;lt;tt&amp;gt;scenario&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, which should be &amp;quot;update&amp;quot; if the package is for an update. In the case of upgrades, which use most of the same functionality of the updater, it should be &amp;quot;upgrade&amp;quot;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation after the package is applied. It should be &amp;quot;opensource&amp;quot; for open source edition, &amp;quot;pro&amp;quot; for professional edition. In the case of an upgrade package, it differs from the &amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation before the package is applied. &lt;br /&gt;
;&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An integer denoting the timestamp of the release date of the version of X2Engine denoted by &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The structure of the &amp;quot;data&amp;quot; array ====&lt;br /&gt;
In updates, each version's data, contained in each element of the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, contains the following nested array properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files added for the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files deleted in the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of SQL commands to bring the database structure and contents into compliance with the version from the previous version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A prerequisite list of SQL commands that should be run before those in &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, and if any fail, the failures should be ignored. These are added to updates whenever it is necessary to normalize the database schema, i.e. in the event that some installations' databases may not be consistent with others, but the commands might fail on every other system. These SQL commands were intended to be &amp;quot;attempted&amp;quot;; they are not guaranteed to succeed on all installations. An example of when this is necessary would be adding a foreign key constraint when in previous versions the storage engine was left to the database's default settings in old versions. MyISAM does not natively support foreign key constraints. Thus, to standardize this across all installations, including installations on servers where the storage engine defaulted to MyISAM, the constraint must first be dropped (in a &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; command) before the relevant tables are changed to the InnoDB storage engine, and the constraint is added back again, in the usual &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;version&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The version number&lt;br /&gt;
;&amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The edition&lt;br /&gt;
;&amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of PHP scripts that would be run in an update for this version. These are a subset of fileList and are typically used for advanced database operations, i.e. restructuring values of JSON-encoded fields, which are extremely impractical (or impossible) to perform using raw SQL commands. The files contained in the list all have paths that look like &amp;quot;&amp;lt;tt&amp;gt;protected/migrations/{version}/{timestamp}-name-of-operations.php&amp;lt;/tt&amp;gt;&amp;quot;. Note, if this array is not empty, you may need to reverse-engineer or otherwise copy and re-purpose/re-write any migration scripts listed in order to perform their necessary operations. This is because they may depend on running within a Yii Framework environment, i.e. will contain references to components of the Yii application singleton such as &amp;quot;db&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Note that in upgrades, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; are replaced by &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; (respectively), and &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; are missing (because they aren't necessary).&lt;br /&gt;
&lt;br /&gt;
==== What you will need for the update, if using the manifest ====&lt;br /&gt;
''Among this data, what you will need:'' &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, which will give you the full list of files that need to be deleted, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, which will give you the full list of files to copy from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory, &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;, which give you values with which to update the main X2Engine configuration file, and, within each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, the sub-array properties &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;, with which you will construct the list of database commands to run. &lt;br /&gt;
&lt;br /&gt;
A procedure for manually updating X2Engine, with data from manifest.json, thus might look like this:&lt;br /&gt;
# For each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; (or in other words, each version's changes):&lt;br /&gt;
## Run all the SQL commands in its corresponding &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; sub-array, ignoring errors&lt;br /&gt;
## Run all SQL commands in its &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; array&lt;br /&gt;
## Perform database operations that would be equivalent to those performed in migration scripts&lt;br /&gt;
# For each file in the cumulative list &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, copy it from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory into its analogous location in the installation directory of X2Engine, creating parent directories as necessary if they don't already exist.&lt;br /&gt;
# Delete all files in the cumulative list &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; from the installation directory&lt;br /&gt;
# Clear the data cache files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt; and the temporary asset folders in &amp;lt;tt&amp;gt;assets/&amp;lt;/tt&amp;gt;&lt;br /&gt;
# Clear all session data (&amp;lt;tt&amp;gt;x2_sessions&amp;lt;/tt&amp;gt;) and auth cache data (&amp;lt;tt&amp;gt;x2_auth_cache&amp;lt;/tt&amp;gt;).&lt;br /&gt;
# Update the main X2Engine configuration (&amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt;) file, setting:&lt;br /&gt;
## &amp;lt;tt&amp;gt;$buildDate&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt; property,&lt;br /&gt;
## &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt; property, and &lt;br /&gt;
## &amp;lt;tt&amp;gt;$updaterVersion&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
&lt;br /&gt;
=== Enacting Database Changes ===&lt;br /&gt;
Inside the &amp;lt;tt&amp;gt;update/sqlList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/sqlForce&amp;lt;/tt&amp;gt; folder is a list of &amp;lt;tt&amp;gt;.sql&amp;lt;/tt&amp;gt; files, named according to their corresponding version's update, that can essentially be run as SQL scripts to perform the update. Note, however, that '''if the update traverses more than one version, the scripts must be run in the order of their corresponding version.''' This is because, in the database changes for any version, it is assumed that the database schema and contents conform to the version that immediately preceded it. The PHP function [[phpfunc:version-compare|version_compare]], and the method &amp;quot;[http://pythonhosted.org/setuptools/pkg_resources.html#parsing-utilities parse_version]&amp;quot;, in the pkg_resources module of setuptools, should correctly identify which version should come first. Note, the [[#The_structure_the_manifest|&amp;quot;versions&amp;quot; property of the manifest]] will contain the list of versions in the correct order.&lt;br /&gt;
&lt;br /&gt;
Furthermore, note, &amp;quot;sqlList&amp;quot; and &amp;quot;sqlForce&amp;quot; may not encompass all necessary database changes; if there are any migration scripts in the update, the operations they perform will similarly need to be performed in the current update (see the description of &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; in [[#The_structure_of_the_.22data.22_array|&amp;quot;the structure of the 'data' array&amp;quot;]]).&lt;br /&gt;
&lt;br /&gt;
In summary, for each version traversed in the update, the following operations should be run in order, with &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version number of the version in question:&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlForce/{version}.sql&amp;lt;/tt&amp;gt; file,''' in the order they appear in the file. Ignore if any commands fail. Note, if passed to the MySQL client as a script, any failures might trigger a halt in execution and thus skip the remainder of the commands.&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlList/{version}.sql&amp;lt;/tt&amp;gt; file.'''&lt;br /&gt;
# '''Perform operations equivalent to those in each of the migration scripts, if any.''' The first part of a script file name is the timestamp it was added to the codebase, and this also dictates the order in which they should be run.&lt;br /&gt;
&lt;br /&gt;
=== Copying new files ===&lt;br /&gt;
Files added/changed in the new version are stored in the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory. In an update, they should be copied into the installation directory, replacing any existing files of the same name/path. If the &amp;lt;tt&amp;gt;rsync&amp;lt;/tt&amp;gt; utility is available on the system, you can use one command to safely copy these files into the installation directory as follows (executed in that same directory):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rsync -avc update/source/ ./&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, you will need to use &amp;lt;tt&amp;gt;cat&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;cp&amp;lt;/tt&amp;gt;, two utilities that should be available on every UNIX-like system, and the lists of files added in each intermediate version. These lists are stored in the folder &amp;lt;tt&amp;gt;update/data/fileList&amp;lt;/tt&amp;gt; and named like &amp;quot;{version}.txt&amp;quot; (&amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version to which it applies), each file on its own line in that file. Run the following command, ignoring errors:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/fileList/*; do for l in $(cat $f); do cp update/source/&amp;quot;$l&amp;quot; ./&amp;quot;$l&amp;quot; ; done ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting old files ===&lt;br /&gt;
Files to be deleted are listed in a similar manner as the files to be added: one file per line. The list of files to delete are located at files with paths that look like this: &amp;lt;tt&amp;gt;update/data/deletionList/{version}.txt&amp;lt;/tt&amp;gt;&lt;br /&gt;
So, for an update from 5.5 to 5.7 with intermediate version 5.6 (for instance), the files would be &amp;lt;tt&amp;gt;update/data/deletionList/5.6&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/data/deletionList/5.7&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is only one version through which to update, the operation is very simple, and can be performed in one UNIX/Linux (bash) shell command as follows (if the version is 3.6.3, for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
xargs -a update/data/deletionList/3.6.3.txt -d&amp;quot;\n&amp;quot; rm -fv&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, if there are multiple versions, this may be a more tricky proposition. The necessity arises to create a cumulative list, because there might be the possibility that a file is deleted in one version and re-established in a new version. Nevertheless, while this is possible, it is highly unlikely, and in most cases it is safe to simply iterate over all the &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; data files and delete all the files listed therein. As a bash command:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/deletionList/*; do xargs -a $f -d&amp;quot;\n&amp;quot; rm -fv ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Editing the Configuration File ===&lt;br /&gt;
This final step in the update process, before refreshing, should not be done manually unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a fresh installation at the same version as the one to which you have updated.&lt;br /&gt;
&lt;br /&gt;
To update the configuration manually:&lt;br /&gt;
# Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor&lt;br /&gt;
# Find the line with the &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; variable in it. It should look like this (but with the version from which you are updating): &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$version = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change the value in quotes to the current version being updated to, and save the file.&lt;br /&gt;
&lt;br /&gt;
=== Clearing caches and refreshing the application ===&lt;br /&gt;
It is generally always a good idea to clear the application's server-side cache and other ephemeral files/data after updating. This is taken care of automatically by the built-in updater, but barring proper use and functioning of that, the operations can be performed manually as follows: &lt;br /&gt;
;clear the data cache&lt;br /&gt;
: delete all &amp;quot;&amp;lt;tt&amp;gt;.bin&amp;lt;/tt&amp;gt;&amp;quot; files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -fv protected/runtime/cache/*.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear web assets&lt;br /&gt;
: delete all sub-folders of &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt; by running this command (in the installation directory): &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -r assets/*/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear the &amp;quot;auth cache&amp;quot;&lt;br /&gt;
: Flush the auth cache table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;&lt;br /&gt;
DELETE FROM `x2_auth_cache` WHERE 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;log all users out&lt;br /&gt;
: Flush the sessions table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;DELETE FROM `x2_sessions` WHERE 1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Upgrading =&lt;br /&gt;
&lt;br /&gt;
== Using the Web Interface ==&lt;br /&gt;
[[File:Software-Upgrade-Registration.png|400px|thumb|right|Software upgrade page, in the first step (registration)]]&lt;br /&gt;
To upgrade to Professional Edition&lt;br /&gt;
# Purchase X2Engine Professional Edition Download. Note the registration info in the activation email.&lt;br /&gt;
# Go to the Admin page in X2Engine and find &amp;quot;Upgrade X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot;.&lt;br /&gt;
# In the ''Upgrade'' page, enter your registration info into the form and click &amp;quot;Register&amp;quot; to continue. Once complete, you will see a list of changes similar to those displayed when performing an update.&lt;br /&gt;
# Review the changes to be applied and then proceed by clicking &amp;quot;Upgrade&amp;quot;, similar to performing an update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Offline&amp;quot; Upgrading ==&lt;br /&gt;
This works in exactly the same way as offline updating. However, to obtain your upgrade package, which you will extract into a folder called &amp;quot;update&amp;quot; on the X2Engine web server, contact [mailto:customersupport@x2engine.com customer support]. Furthermore, you will need to run the following database command on your X2Engine database when done with the upgrade, replacing &amp;lt;tt&amp;gt;{product key}&amp;lt;/tt&amp;gt; with your product key, to allow your installation to receive Professional Edition software updates:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{product key}' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Manually Upgrading ==&lt;br /&gt;
The process of upgrading is very similar to that of updating, and uses all of the same underpinnings as the updater. Thus, almost all of the above information and procedures will apply to upgrades. However, note the following crucial differences:&lt;br /&gt;
&lt;br /&gt;
* '''X2Engine must be at the latest available version in order to upgrade.'''&lt;br /&gt;
* '''To obtain the upgrade package, you must be an owner of an X2Engine Professional Edition Download-type license.''' Contact [mailto:customersupport@x2engine.com customer support] for further details.&lt;br /&gt;
* '''There will be only one &amp;quot;intermediate&amp;quot; version.''' The changes to apply will move X2Engine from a version to that same version, applying instead the necessary changes to switch its ''edition'' from Open Source to Professional.&lt;br /&gt;
* '''There are no file deletions.'''&lt;br /&gt;
* '''The properties of the package manifest differ slightly.''' In upgrades, the properties of the data object are &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, etc. Note also, the update data is in aptly-named sub-folders of &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt; as well, and there are no &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; lists to handle.&lt;br /&gt;
* '''The configuration is not changed.''' Instead, the value stored in the field &amp;lt;tt&amp;gt;unique_id&amp;lt;/tt&amp;gt; of the admin record in the database table &amp;lt;tt&amp;gt;x2_admin&amp;lt;/tt&amp;gt; must be changed to the license key, and the &amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt; field must be changed to &amp;quot;pro&amp;quot;. To perform these both, run the following command on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''It is not necessary to clear caches or user sessions when done.''' These operations are performed in updates to eliminate ephemeral (session-ingrained) data that may no longer be applicable and thus cause problems. Upgrades will rarely, if ever, invalidate this data.&lt;br /&gt;
&lt;br /&gt;
= Enabling Beta Releases = &lt;br /&gt;
Beginning at version 4.1.2, after much advocacy to adopt this practice, X2Engine began a two-phase release process in which each version would be released first as a beta, then to the broader public after initial bug finds. The purpose behind this is to avoid updating production installations of X2Engine to new versions that have had zero public exposure, and thus avoid bugs that were not found in initial QA testing (due to a incompatibility with a particular server environment or configuration of X2Engine).&lt;br /&gt;
&lt;br /&gt;
By default, the updater will not update to beta versions. The updater must be configured to use the proper URL on the update server for beta release data, by the following procedure:&lt;br /&gt;
# Create a file &amp;quot;constants-custom.php&amp;quot; in the root level of X2Engine. You can easily do this by renaming &amp;quot;constants-custom.example.php&amp;quot; to that name (remove &amp;quot;.custom&amp;quot;).&lt;br /&gt;
# Look for the following line, and in it, change &amp;quot;false&amp;quot; to &amp;quot;true&amp;quot;: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('X2_UPDATE_BETA') or define('X2_UPDATE_BETA',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The advantage of enabling beta releases is that new features will be available much sooner. Additionally, fixes to bugs will also be available sooner. The main disadvantage of enabling beta releases is that they are never guaranteed to be as stable as non-beta releases. We encourage users to clone their installations of X2Engine, if able, and use beta versions on them, and report any issues they encounter for their particular server environment/special configuration/custom implementation of X2Engine. This way, any issues that arise can be fixed in the stable release. The more users participate in beta testing, the more stable the non-beta releases will become.&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
== User interface (UI) &amp;quot;bugs&amp;quot; after the update ==&lt;br /&gt;
It is extremely common (and easily remedied) that after an important update, certain features of the user interface of X2Engine may not work properly. This is caused, in nearly all cases, by the web browser's cache, which contains out-of-date copies of X2Engine's essential user interface files. To solve these problems, the first step to try is clearing the cache. A step-by-step guide to doing so is given in [[wikihow:Clear-Your-Browser%27s-Cache]].&lt;br /&gt;
&lt;br /&gt;
== Errors in the updater itself == &lt;br /&gt;
When all else fails, the updater may have to be patched due to an irreconcilable backwards compatibility issue. Fortunately, if one has read/write access to the source files on the webserver, this is straightforward to fix. See [[#Refreshing_and_Updating_the_Updater_Utility|Refreshing and Updating the Updater Utility]].&lt;br /&gt;
&lt;br /&gt;
== Professional Edition not in effect after upgrading ==&lt;br /&gt;
This may be caused by Professional Edition not getting &amp;quot;switched on&amp;quot; properly, despite all the necessary filesystem and database changes already being in effect. First, to see if this scenario applies to you, look for a folder &amp;quot;&amp;lt;tt&amp;gt;reports&amp;lt;/tt&amp;gt;&amp;quot; inside &amp;lt;tt&amp;gt;protected/modules&amp;lt;/tt&amp;gt;, and a table &amp;lt;tt&amp;gt;x2_reports&amp;lt;/tt&amp;gt; in the X2Engine database. If you find these things, you can try the following to enable Professional Edition:&lt;br /&gt;
# Find the file &amp;lt;tt&amp;gt;constants.php&amp;lt;/tt&amp;gt; in the installation directory. Open it in a text editor, and look for the following line: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('PRO_VERSION') or define('PRO_VERSION',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; on that line, and save the file.&lt;br /&gt;
# Run the following SQL command (replacing &amp;lt;tt&amp;gt;{license key}&amp;lt;/tt&amp;gt; with your X2Engine license key) on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General Procedures to Try ==&lt;br /&gt;
It may sometimes be possible that the update did not finish cleanly. In such cases, it will be necessary to manually re-perform some of the final operations in the update, described in [[#Introduction|Introduction]]. &lt;br /&gt;
&lt;br /&gt;
=== Refresh the Source Code ===&lt;br /&gt;
This step is not really the best step to try first, as it is usually more time-consuming. It entails re-uploading the entire application, in order to reset all non-ephemeral (source code) files to what they should be in a fresh installation of X2Engine. To do this:&lt;br /&gt;
# Download the refresh package. There should be a download link for this in the ''Updater Settings'' page. Otherwise, if that doesn't work and you're using Open Source edition, use [https://x2planet.com/installs/refresh.zip this link], or if using Professional Edition, paste the following url (after replacing {key} with your license key) into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/refresh-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Upload the ''contents'' of the downloaded zip archive to the installation directory, overwriting existing files.&lt;br /&gt;
&lt;br /&gt;
=== Clear Caches ===&lt;br /&gt;
See &amp;quot;[[#Clearing_caches_and_refreshing_the_application|Clearing Caches...]]&amp;quot; for details.&lt;br /&gt;
&lt;br /&gt;
=== Regenerate the Configuration ===&lt;br /&gt;
Again, this should not be done unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a new installation at the same version as the one you updated to. Refer to [[#Editing_the_Configuration_File|Editing the Configuration File]].&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1838</id>
		<title>Software Updates and Upgrades</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Software_Updates_and_Upgrades&amp;diff=1838"/>
				<updated>2014-06-18T23:30:10Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Troubleshooting */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
[[File:Update_Process.svg|200px|thumb|right|Top-level overview of the update process]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
The following things happen in typical a software update (or upgrade):&lt;br /&gt;
* Files that are new in the new version are added&lt;br /&gt;
* Files that have been changed in the new version are replaced with the new versions of those files &lt;br /&gt;
* Files that are no longer used in the new version are deleted&lt;br /&gt;
* Changes to the database are made to make the database compatible with the new version of X2Engine&lt;br /&gt;
* The main configuration file, &amp;lt;tt&amp;gt;X2Config.php&amp;lt;/tt&amp;gt;, is edited to reflect the new state of the application.&lt;br /&gt;
* X2Engine data caches are cleared&lt;br /&gt;
&lt;br /&gt;
These operations are typically handled by the updater utility component [[x2doc:UpdaterBehavior|UpdaterBehavior]] and controlled via the in-app web-based updater or the command line updater. This article will cover both how to use said interfaces to the updater, and how to perform updates manually or with the &amp;quot;offline&amp;quot; method if the need arises, in addition to troubleshooting and recovering from an update that did not complete cleanly.&lt;br /&gt;
&lt;br /&gt;
= Disclaimer =&lt;br /&gt;
This article covers the standard and recommended update/upgrade methods, in addition to alternate methods and supplemental procedures. As general rules and precautions for using this article:&lt;br /&gt;
# ''If you do not fully and completely understand any step of any instructions given in this article, '''do not''' follow them''; ask for help instead on [http://x2community.com the forums] or through contacting X2Engine Technical Support if you are an existing customer.&lt;br /&gt;
# You should always make a backup of all of your files ''and'' a full backup copy of your database, preferably in a format (i.e. SQL script) that can be used to create a verbatim/carbon copy of the original database, with all of the features of the original schema, '''before''' attempting any steps of the update process manually.&lt;br /&gt;
# Some of the procedures outlined in this article involve manually creating or modifying files. Note that the same filesystem prerequisite for installation, namely that the file ownership must match the user under which PHP executes on the web server, applies to these steps.&lt;br /&gt;
&lt;br /&gt;
Furthermore, '''in regard to uploading files:''' note that any instructions to upload folders into X2Engine, and replace/merge contents, refer to replacing files in X2Engine with files in the archive at analogous paths. For example, if you have a zip file that contains a folder &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, and inside of that is a folder &amp;lt;tt&amp;gt;components&amp;lt;/tt&amp;gt;, and inside of that is a file &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt;, and the instructions are to upload/replace in X2Engine, you should upload &amp;lt;tt&amp;gt;UpdaterBehavior.php&amp;lt;/tt&amp;gt; to replace the existing file of the same name in &amp;lt;tt&amp;gt;protected/components&amp;lt;/tt&amp;gt; inside of X2Engine.&lt;br /&gt;
&lt;br /&gt;
X2Engine Inc. cannot be held accountable for damage/errors caused by any missteps or mistakes in manually-performed update operations. If in doubt, make a sandbox/clone of your X2Engine installation by copying the files and database to a different server, and test performing the operations on that before proceeding with your production install of X2Engine.&lt;br /&gt;
&lt;br /&gt;
= Update Notifications =&lt;br /&gt;
[[File:Update Notification.png|200px|frame|right|System flash message displaying software update notification.]]&lt;br /&gt;
To get notified of software updates, unless already using X2Engine Professional Edition, one must first &amp;quot;subscribe&amp;quot; to them. This can be done via checking the box &amp;quot;Notify me of software updates&amp;quot; in the installation form. If X2Engine is already installed, this can be done by going to &amp;quot;Updater Settings&amp;quot; from the Admin page and enabling them. If using Professional Edition, these steps are not not necessary.&lt;br /&gt;
&lt;br /&gt;
Once that has been completed, set the update notification interval as desired in &amp;quot;Updater Settings&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Update Methods =&lt;br /&gt;
There are, in essence, just three basic methods of updating, and variations on each. The three main methods are: &lt;br /&gt;
# web updater,&lt;br /&gt;
# command line updater, and&lt;br /&gt;
# manually.&lt;br /&gt;
In each case, an update package from the update data server is used as the authoritative list of changes to apply, but in the non-manual methods, X2Engine's own updater class does all the work applying changes from said package.&lt;br /&gt;
&lt;br /&gt;
In web and command line updates, there is also the option to first download and unpack the package manually (see [[#Performing_.22Offline.22_Updates|performing &amp;quot;offline&amp;quot; updates]]). This circumvents the need for the local server (on which X2Engine is hosted) to be connected to the internet. Similarly, in the case of manual updates, it is not necessary for the web server to actually have an internet connection.&lt;br /&gt;
&lt;br /&gt;
== Using the Web Updater ==&lt;br /&gt;
[[File:Updater-1.png|300px|thumb|right|The updater utility page.]]&lt;br /&gt;
The web updater can be accessed by any of the following methods:&lt;br /&gt;
* Using the link in the notification message at the top of the window in versions later than 3.5.5&lt;br /&gt;
* By going to the Admin page and clicking the &amp;quot;Update&amp;quot; button at the top, in earlier versions, with update notifications enabled&lt;br /&gt;
* Through &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
First, navigate to the web updater page, and '''do not''' cancel the request (stop or hit your browser's &amp;quot;back&amp;quot; button) until the page has loaded. If the updater utility has undergone changes and needs to self-update to be compatible with the software updates server, this is when it will do so. Thus, it is critical that you do not interrupt this process. If the page does not load, or displays the error message that dependencies could not be retrieved, you may have to [[#Performing_.22Offline.22_Updates|perform an &amp;quot;offline&amp;quot; update]].&lt;br /&gt;
&lt;br /&gt;
There are three ways of getting to the updater page. One is to simply click the link given in the update notification flash message, if available. The other is to click on the &amp;quot;Update&amp;quot; button at the top of the admin page. If software update notifications are not enabled, one can manually check for and install an update by going to &amp;quot;Update X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
Next, make a backup of the database and files (recommended), follow the instructions on the page, and click the &amp;quot;Update&amp;quot; button when ready.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
It is generally quick and is the easiest method to use, and it does not require shell/SSH access to the web server.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
It is not recommended for use on large installations of X2Engine, i.e. containing over a million records.&lt;br /&gt;
&lt;br /&gt;
==== Explanation ====&lt;br /&gt;
The inherent weakness of the web updater (or any web-based updater to any web application) is that it must always rely upon web requests to the server, and it thus must perform extremely critical operations for the update all within the scope of a web request. Web requests, it goes without saying, are not always reliable. For example, one's internet connection might get cut off due being on a wireless network and encountering interference. Furthermore, if any operations take an exceedingly long amount of time, e.g. because of the size of the data set on which the update must operate, the web request for those operations may take longer than the PHP execution time limit as configured on the webserver. This would potentially cut short the execution of a critical update stage, and thus the update would require manual recovery at best, or completely restoring  X2Engine to a backup at worst.&lt;br /&gt;
&lt;br /&gt;
==== Ways to get around this ====&lt;br /&gt;
In most cases, including on shared hosting, it should be possible to change the time limit on PHP. It is highly recommended that you either disable the timeout or set it to a very generous value (such as 300 seconds) while attempting to update via the web updater.&lt;br /&gt;
&lt;br /&gt;
== Using the Command Line Updater ==&lt;br /&gt;
This update method uses all of the same thoroughly-tested underpinnings and processes as the web updater, but does so all within a system command to the Yii console.&lt;br /&gt;
&lt;br /&gt;
=== How to use ===&lt;br /&gt;
While in via SSH, or in a terminal emulator, or in a script, run the following command in the &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt; subdirectory of X2Engine:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./yiic update app&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the following optional arguments to the command line updater:&lt;br /&gt;
;&amp;lt;tt&amp;gt;--force&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; for this option (i.e. &amp;lt;tt&amp;gt;--force=1&amp;lt;/tt&amp;gt;) to proceed with the update even if compatibility issues were detected. Default: 0 (halt if compatibility issues were detected).&lt;br /&gt;
;&amp;lt;tt&amp;gt;--backup&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that a database backup should be performed first before updating, &amp;quot;0&amp;quot; to proceed without creating a backup first. Default: 1.&lt;br /&gt;
;&amp;lt;tt&amp;gt;--lock&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Use &amp;quot;1&amp;quot; to specify that the application should be locked during the final installation of the update package to prevent data entry during the sensitive moments when the database is being altered and the files updated, &amp;quot;0&amp;quot; to permit use even when changes to the app are being applied. Default: 0.&lt;br /&gt;
&lt;br /&gt;
If running the command gives you the following message:&lt;br /&gt;
&amp;lt;pre&amp;gt;The updater is now up-to-date and compliant with the updates server. Re-run the command to proceed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
That indicates that the updater first needed to self-update, and succeeded in doing so. You should then run the same command again to complete the update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Safer to use on large, mission-critical systems. The success of a software update (or upgrade) does not hinge upon a single web request completing successfully.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
More difficult to use; requires shell access to the web server. The console command must be run as the same system user that owns all the files and directories of X2Engine, in order to circumvent permission issues when manipulating file system objects.&lt;br /&gt;
&lt;br /&gt;
== Using the Automatic Updater ==&lt;br /&gt;
[[File:Auto-Update.png|400px|thumb|right|Auto-update scheduler on the Updater Settings page]]&lt;br /&gt;
This method uses the command line updater, but runs the command on your behalf using the local cron daemon. To configure, go to the Updater Settings Page, check the box titled &amp;quot;Update Automatically&amp;quot;, and save. If you have Professional Edition, you can manage the update schedule in addition to other automated tasks from the Cron Table settings page, which can also be found under &amp;quot;System Settings&amp;quot; in the Admin page.&lt;br /&gt;
&lt;br /&gt;
=== Advantages ===&lt;br /&gt;
Automates the entire process of updating.&lt;br /&gt;
&lt;br /&gt;
=== Disadvantages ===&lt;br /&gt;
Only available on UNIX-like systems that have a cron daemon, and which permit user access to the cron table.&lt;br /&gt;
&lt;br /&gt;
== Performing &amp;quot;Offline&amp;quot; Updates ==&lt;br /&gt;
In some server environments, the updater may not be able to access the updates server by itself, due to (for example) the PHP or network configuration, or because the &amp;quot;zip&amp;quot; PHP extension (requried) is unavailable or has been disabled. It is in these situations necessary to download the update package manually, unpack on the server (or unpack and upload the files to the server), and then run the updater. When performing updates in this way, the updater will not need to access the updates server, but will use the manually-downloaded and extracted package files present in the local filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Refreshing and Updating the Updater Utility ===&lt;br /&gt;
Before starting, note that the updater itself may have undergone changes; the X2Engine update process is subject to change. To address this technical challenge, the updater utility within X2Engine would normally first attempt to download the latest version of itself, in order to be compatible with the updates server. However, this sometimes will not work properly. If your X2Engine installation is running in a hosting environment that prevents it from making outbound HTTP requests (the [[requirements:|requirements check script]] should tell you if this is the case), you will need to do this manually. Also, in version 3.5.5, there was a bug in the updater utility's self-update methods.&lt;br /&gt;
&lt;br /&gt;
To update the updater utility:&lt;br /&gt;
# '''Determine the version of the updater utility package''' by visiting [https://x2planet.com/installs/updates/updateCheck this link]. You should see a page that's entirely blank except for a version number.&lt;br /&gt;
# '''Download the updater package.''' In versions 3.6 and later, a download link for this should be available through the ''Updater Settings'' page. Otherwise, use the following URL (if in Open Source Edition): &amp;lt;pre&amp;gt;https://x2planet.com/installs/updater.zip&amp;lt;/pre&amp;gt; Otherwise, if using Professional Edition, paste the following url, after replacing {key} with your license key, into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/updater-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# '''Extract the downloaded zip archive and upload''' the &amp;quot;protected&amp;quot; folder to your server. Merge folders and overwrite existing files when prompted.&lt;br /&gt;
# '''Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor''' and find the line that looks like this:&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$updaterVersion = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# '''Change the text inside the pair of single quotes''' on that line so that it is the current updater version obtained in step #1. &lt;br /&gt;
# '''Save the file.'''&lt;br /&gt;
&lt;br /&gt;
=== Obtaining the Update Package ===&lt;br /&gt;
First, use the link in the ''Updater Settings'' page entitled &amp;quot;Latest Update Package for Version...&amp;quot;, to download the update package. If the link in the Updater Settings page does not work, use the following URL to download the update package (if using Open Source Edition), replacing &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; with your X2Engine installation's version:&lt;br /&gt;
&amp;lt;pre&amp;gt;https://x2planet.com/installs/updates/{version}/none&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should receive, from that URL, a zip archive file called &amp;quot;update.zip&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If using Professional Edition, and your download link is not working, contact [mailto:customersupport@x2engine.com customer support].&lt;br /&gt;
&lt;br /&gt;
=== Unpacking the Update ===&lt;br /&gt;
[[File:Update-in-Progress.png|200px|frame|right|System flash message displaying notification of pending update.]]&lt;br /&gt;
The update package will be a zip archive. The ''contents'' of this archive must be extracted ''into'' a folder called &amp;quot;update&amp;quot; in the installation directory. So, in other words, after extracting and uploading to the server, there should be a folder called &amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt; alongside &amp;lt;tt&amp;gt;protected&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;framework&amp;lt;/tt&amp;gt; (etc). Within that folder (&amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt;) should be the following contents:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A file containing important data to be used by the updater&lt;br /&gt;
;&amp;lt;tt&amp;gt;contents.md5&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A message digest file containing MD5 checksums of all files in the archive excluding itself.&lt;br /&gt;
;&amp;lt;tt&amp;gt;source&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A sub-folder/directory, whose internal structure mimics that of X2Engine, containing files that were changed in the update. If there were no files changed (i.e. only database changes were in the update), this directory will not be present.&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A directory containing flat data files. These files are typically not used by the updater, but are useful if performing manual updates or recovery.&lt;br /&gt;
&lt;br /&gt;
=== Applying the Update ===&lt;br /&gt;
[[File:Steps-post-Unpack.png|200px|frame|right|Steps of the update process in the event of manually-extracted package (or resuming a preexisting update)]]&lt;br /&gt;
After unpacking, if you run the command line updater, it should (if no problems arise, i.e. incompatible package version) apply the update straight away. Otherwise, log into X2Engine, and you should see a system flash message notifying you of an update in progress. This message shows up whenever there is an update package folder (&amp;quot;update&amp;quot;) present on the server. If you then go to the updater page, once the page has finished loading, you should see that there only remain the steps &amp;quot;Review and confirm changes&amp;quot;, &amp;quot;Apply changes&amp;quot; and &amp;quot;Reload&amp;quot; remain to be completed. To complete the update, when ready, click &amp;quot;Update&amp;quot;; otherwise, to delete the &amp;quot;update&amp;quot; folder and cancel the update, click &amp;quot;Cancel&amp;quot; either on the updater page or in the system flash message.&lt;br /&gt;
&lt;br /&gt;
== Performing Updates Manually ==&lt;br /&gt;
While it is preferable to avoid manually applying changes to X2Engine, and you may never need to, it may in some circumstance or another actually be necessary to do so. For all such situations, the raw update data files generated by the X2Engine update build process are bundled with every update package to help you perform the operations described in [[#Introduction|&amp;quot;Introduction&amp;quot;]]. This section will describe how to perform each of those operations. &lt;br /&gt;
&lt;br /&gt;
=== Prerequisites ===&lt;br /&gt;
First and foremost, all shell commands listed here, and in general, any operations that create or modify files, ''must be run as the same system user under which the server runs PHP scripts.'' Otherwise, the ownership and/or permissions of files and directories will have to be changed after completing all changes.&lt;br /&gt;
&lt;br /&gt;
To obtain the update package (which will be necessary), refer to &amp;quot;[[#Obtaining_the_Update_Package|Obtaining the Update Package]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Furthermore, it is strongly advised that you first make a back-up copy of all files and of X2Engine's database. Your database backup would best be a full, exact copy, such as one that would be made by [http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html mysqldump], or the built-in export tools of [http://www.phpmyadmin.net/home_page/index.php PHPMyAdmin].&lt;br /&gt;
&lt;br /&gt;
The X2Engine global import/export utility (&amp;quot;Import All Data&amp;quot;/&amp;quot;Export All Data&amp;quot;) in Admin '''is not recommended by itself as the sole backup tool'''. This is especially important to note because of how this tool cannot re-create custom fields; they must exist in the target installation before the import. However, using it to ''supplement'' your standard database backup method will prove useful in the event that you:&lt;br /&gt;
# don't have any custom fields,&lt;br /&gt;
# cannot manage to update X2Engine with success, and&lt;br /&gt;
# wish to start over with a fresh installation.&lt;br /&gt;
In such a case, you will then be able to import your old data into the new installation.&lt;br /&gt;
&lt;br /&gt;
=== Introduction: Data Files ===&lt;br /&gt;
In [[#Unpacking_the_Update|Unpacking the update]], a rough overview of an update package's contents were given. In this section, the anatomy and purpose of each of the data files will be introduced.&lt;br /&gt;
&lt;br /&gt;
Included in every update package is two copies of the following data:&lt;br /&gt;
* Files to copy&lt;br /&gt;
* Files to delete&lt;br /&gt;
* Database changes to enact&lt;br /&gt;
* Migration scripts to run, if any&lt;br /&gt;
&lt;br /&gt;
The first, immediately visible place where all of this data is stored is in the file &amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;, as a JSON-encoded array. See the next section for how to use this file.&lt;br /&gt;
&lt;br /&gt;
The second place, which can be useful to users who do not wish to automate or script any step of a manual update, is in the files contained in the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; directory. Each file is named after the version to which it corresponds, and contains the data required to update ''to'' that version ''from the version immediately preceding it.''' Thus, the versions you should see in each file name are the versions above your X2Engine installation's current version, up to and including the version being updated to.&lt;br /&gt;
&lt;br /&gt;
In an update, the directory will contain the following subdirectories:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of files to copy. The format is each line containing a file path in the list.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains files for each version, each containing the list of obsolete files for that version which will need to be deleted. The format is the same as in &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts corresponding to each version. Each file is formatted such that it can be directly run on the database as an SQL script, although this is not generally recommended. The list of SQL commands to run is a of the following format: &lt;br /&gt;
  [query 1];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [query 2];&lt;br /&gt;
  /*&amp;amp;*/&lt;br /&gt;
  [etc]&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Contains .sql scripts with similar naming and formatting conventions to the &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; scripts. See [[#The_structure_of_the_.22data.22_array|The structure of the &amp;quot;data&amp;quot; array]] for explanation of the purpose of &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== How to use the manifest for updating manually ===&lt;br /&gt;
If you so choose, you may write a script to perform all of the operations for an update in one fell swoop. If so, and if the preferred scripting language of choice has a JSON-parsing library available, you will be able to save yourself a lot of effort by using the main data file (&amp;lt;tt&amp;gt;manifest.json&amp;lt;/tt&amp;gt;) to load all of the necessary data for the update.&lt;br /&gt;
&lt;br /&gt;
==== The structure the manifest ====&lt;br /&gt;
In updates, the contents of the plain-text file manifest.json are a JSON-encoded object containing the following properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the data corresponding to each intermediate version. It is, in essence, all the data stored in the files in &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt;. If the update is for an installation of X2Engine that is only one version behind, it will contain only one element. Each element of this array is itself an array containing respective data for the intermediate version.&lt;br /&gt;
;&amp;lt;tt&amp;gt;versions&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array listing the version numbers of each intermediate version in the update. Similar to &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, this array will contain only one element if updating to the next version, and that version ''will be the next version''.&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of deletions of obsolete files across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An array containing the cumulative list of files added or changed across versions through which the update traverses.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''from'', or in other words, the version of X2Engine before the package is applied. '''This must be the same as the version of installation to which the package is being applied.''' If this is not so, the updater utility would exit immediately, as applying the changes in the update would have potentially harmful results to data and/or compatibility with future updates.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, containing the version number that the update is ''to'', or in other words, the version of X2Engine before the package is applied.&lt;br /&gt;
;&amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the version of the updater utility for which the package was compiled. If the structure of the update package or of the manifest changes, the updater must also change to accommodate and recognize the changes; else it might try to access properties that don't exist, or ignore important ones that do. This is the single property of manifest.json that should never change.&lt;br /&gt;
;&amp;lt;tt&amp;gt;scenario&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string, which should be &amp;quot;update&amp;quot; if the package is for an update. In the case of upgrades, which use most of the same functionality of the updater, it should be &amp;quot;upgrade&amp;quot;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;targetEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation after the package is applied. It should be &amp;quot;opensource&amp;quot; for open source edition, &amp;quot;pro&amp;quot; for professional edition. In the case of an upgrade package, it differs from the &amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
;&amp;lt;tt&amp;gt;fromEdition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string denoting the edition of the X2Engine installation before the package is applied. &lt;br /&gt;
;&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;&lt;br /&gt;
: An integer denoting the timestamp of the release date of the version of X2Engine denoted by &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The structure of the &amp;quot;data&amp;quot; array ====&lt;br /&gt;
In updates, each version's data, contained in each element of the &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, contains the following nested array properties:&lt;br /&gt;
;&amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files added for the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The list of files deleted in the version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of SQL commands to bring the database structure and contents into compliance with the version from the previous version&lt;br /&gt;
;&amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A prerequisite list of SQL commands that should be run before those in &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, and if any fail, the failures should be ignored. These are added to updates whenever it is necessary to normalize the database schema, i.e. in the event that some installations' databases may not be consistent with others, but the commands might fail on every other system. These SQL commands were intended to be &amp;quot;attempted&amp;quot;; they are not guaranteed to succeed on all installations. An example of when this is necessary would be adding a foreign key constraint when in previous versions the storage engine was left to the database's default settings in old versions. MyISAM does not natively support foreign key constraints. Thus, to standardize this across all installations, including installations on servers where the storage engine defaulted to MyISAM, the constraint must first be dropped (in a &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; command) before the relevant tables are changed to the InnoDB storage engine, and the constraint is added back again, in the usual &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;version&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The version number&lt;br /&gt;
;&amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The edition&lt;br /&gt;
;&amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A list of PHP scripts that would be run in an update for this version. These are a subset of fileList and are typically used for advanced database operations, i.e. restructuring values of JSON-encoded fields, which are extremely impractical (or impossible) to perform using raw SQL commands. The files contained in the list all have paths that look like &amp;quot;&amp;lt;tt&amp;gt;protected/migrations/{version}/{timestamp}-name-of-operations.php&amp;lt;/tt&amp;gt;&amp;quot;. Note, if this array is not empty, you may need to reverse-engineer or otherwise copy and re-purpose/re-write any migration scripts listed in order to perform their necessary operations. This is because they may depend on running within a Yii Framework environment, i.e. will contain references to components of the Yii application singleton such as &amp;quot;db&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Note that in upgrades, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; are replaced by &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; (respectively), and &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; are missing (because they aren't necessary).&lt;br /&gt;
&lt;br /&gt;
==== What you will need for the update, if using the manifest ====&lt;br /&gt;
''Among this data, what you will need:'' &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt;, which will give you the full list of files that need to be deleted, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, which will give you the full list of files to copy from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory, &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt;, which give you values with which to update the main X2Engine configuration file, and, within each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;, the sub-array properties &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt;, with which you will construct the list of database commands to run. &lt;br /&gt;
&lt;br /&gt;
A procedure for manually updating X2Engine, with data from manifest.json, thus might look like this:&lt;br /&gt;
# For each element of &amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; (or in other words, each version's changes):&lt;br /&gt;
## Run all the SQL commands in its corresponding &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; sub-array, ignoring errors&lt;br /&gt;
## Run all SQL commands in its &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt; array&lt;br /&gt;
## Perform database operations that would be equivalent to those performed in migration scripts&lt;br /&gt;
# For each file in the cumulative list &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, copy it from the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory into its analogous location in the installation directory of X2Engine, creating parent directories as necessary if they don't already exist.&lt;br /&gt;
# Delete all files in the cumulative list &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; from the installation directory&lt;br /&gt;
# Clear the data cache files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt; and the temporary asset folders in &amp;lt;tt&amp;gt;assets/&amp;lt;/tt&amp;gt;&lt;br /&gt;
# Clear all session data (&amp;lt;tt&amp;gt;x2_sessions&amp;lt;/tt&amp;gt;) and auth cache data (&amp;lt;tt&amp;gt;x2_auth_cache&amp;lt;/tt&amp;gt;).&lt;br /&gt;
# Update the main X2Engine configuration (&amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt;) file, setting:&lt;br /&gt;
## &amp;lt;tt&amp;gt;$buildDate&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;buildDate&amp;lt;/tt&amp;gt; property,&lt;br /&gt;
## &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;targetVersion&amp;lt;/tt&amp;gt; property, and &lt;br /&gt;
## &amp;lt;tt&amp;gt;$updaterVersion&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;updaterVersion&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
&lt;br /&gt;
=== Enacting Database Changes ===&lt;br /&gt;
Inside the &amp;lt;tt&amp;gt;update/sqlList&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/sqlForce&amp;lt;/tt&amp;gt; folder is a list of &amp;lt;tt&amp;gt;.sql&amp;lt;/tt&amp;gt; files, named according to their corresponding version's update, that can essentially be run as SQL scripts to perform the update. Note, however, that '''if the update traverses more than one version, the scripts must be run in the order of their corresponding version.''' This is because, in the database changes for any version, it is assumed that the database schema and contents conform to the version that immediately preceded it. The PHP function [[phpfunc:version-compare|version_compare]], and the method &amp;quot;[http://pythonhosted.org/setuptools/pkg_resources.html#parsing-utilities parse_version]&amp;quot;, in the pkg_resources module of setuptools, should correctly identify which version should come first. Note, the [[#The_structure_the_manifest|&amp;quot;versions&amp;quot; property of the manifest]] will contain the list of versions in the correct order.&lt;br /&gt;
&lt;br /&gt;
Furthermore, note, &amp;quot;sqlList&amp;quot; and &amp;quot;sqlForce&amp;quot; may not encompass all necessary database changes; if there are any migration scripts in the update, the operations they perform will similarly need to be performed in the current update (see the description of &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; in [[#The_structure_of_the_.22data.22_array|&amp;quot;the structure of the 'data' array&amp;quot;]]).&lt;br /&gt;
&lt;br /&gt;
In summary, for each version traversed in the update, the following operations should be run in order, with &amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version number of the version in question:&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlForce/{version}.sql&amp;lt;/tt&amp;gt; file,''' in the order they appear in the file. Ignore if any commands fail. Note, if passed to the MySQL client as a script, any failures might trigger a halt in execution and thus skip the remainder of the commands.&lt;br /&gt;
# '''Run all of the SQL commands in the &amp;lt;tt&amp;gt;data/sqlList/{version}.sql&amp;lt;/tt&amp;gt; file.'''&lt;br /&gt;
# '''Perform operations equivalent to those in each of the migration scripts, if any.''' The first part of a script file name is the timestamp it was added to the codebase, and this also dictates the order in which they should be run.&lt;br /&gt;
&lt;br /&gt;
=== Copying new files ===&lt;br /&gt;
Files added/changed in the new version are stored in the &amp;lt;tt&amp;gt;update/source&amp;lt;/tt&amp;gt; directory. In an update, they should be copied into the installation directory, replacing any existing files of the same name/path. If the &amp;lt;tt&amp;gt;rsync&amp;lt;/tt&amp;gt; utility is available on the system, you can use one command to safely copy these files into the installation directory as follows (executed in that same directory):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rsync -avc update/source/ ./&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, you will need to use &amp;lt;tt&amp;gt;cat&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;cp&amp;lt;/tt&amp;gt;, two utilities that should be available on every UNIX-like system, and the lists of files added in each intermediate version. These lists are stored in the folder &amp;lt;tt&amp;gt;update/data/fileList&amp;lt;/tt&amp;gt; and named like &amp;quot;{version}.txt&amp;quot; (&amp;lt;tt&amp;gt;{version}&amp;lt;/tt&amp;gt; being the version to which it applies), each file on its own line in that file. Run the following command, ignoring errors:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/fileList/*; do for l in $(cat $f); do cp update/source/&amp;quot;$l&amp;quot; ./&amp;quot;$l&amp;quot; ; done ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting old files ===&lt;br /&gt;
Files to be deleted are listed in a similar manner as the files to be added: one file per line. The list of files to delete are located at files with paths that look like this: &amp;lt;tt&amp;gt;update/data/deletionList/{version}.txt&amp;lt;/tt&amp;gt;&lt;br /&gt;
So, for an update from 5.5 to 5.7 with intermediate version 5.6 (for instance), the files would be &amp;lt;tt&amp;gt;update/data/deletionList/5.6&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update/data/deletionList/5.7&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there is only one version through which to update, the operation is very simple, and can be performed in one UNIX/Linux (bash) shell command as follows (if the version is 3.6.3, for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
xargs -a update/data/deletionList/3.6.3.txt -d&amp;quot;\n&amp;quot; rm -fv&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Otherwise, if there are multiple versions, this may be a more tricky proposition. The necessity arises to create a cumulative list, because there might be the possibility that a file is deleted in one version and re-established in a new version. Nevertheless, while this is possible, it is highly unlikely, and in most cases it is safe to simply iterate over all the &amp;lt;tt&amp;gt;deletionList&amp;lt;/tt&amp;gt; data files and delete all the files listed therein. As a bash command:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
for f in update/data/deletionList/*; do xargs -a $f -d&amp;quot;\n&amp;quot; rm -fv ; done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Editing the Configuration File ===&lt;br /&gt;
This final step in the update process, before refreshing, should not be done manually unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a fresh installation at the same version as the one to which you have updated.&lt;br /&gt;
&lt;br /&gt;
To update the configuration manually:&lt;br /&gt;
# Open the file &amp;lt;tt&amp;gt;protected/config/X2Config.php&amp;lt;/tt&amp;gt; in a text editor&lt;br /&gt;
# Find the line with the &amp;lt;tt&amp;gt;$version&amp;lt;/tt&amp;gt; variable in it. It should look like this (but with the version from which you are updating): &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;$version = '3.5.2';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change the value in quotes to the current version being updated to, and save the file.&lt;br /&gt;
&lt;br /&gt;
=== Clearing caches and refreshing the application ===&lt;br /&gt;
It is generally always a good idea to clear the application's server-side cache and other ephemeral files/data after updating. This is taken care of automatically by the built-in updater, but barring proper use and functioning of that, the operations can be performed manually as follows: &lt;br /&gt;
;clear the data cache&lt;br /&gt;
: delete all &amp;quot;&amp;lt;tt&amp;gt;.bin&amp;lt;/tt&amp;gt;&amp;quot; files in &amp;lt;tt&amp;gt;protected/runtime/cache&amp;lt;/tt&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -fv protected/runtime/cache/*.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear web assets&lt;br /&gt;
: delete all sub-folders of &amp;lt;tt&amp;gt;assets&amp;lt;/tt&amp;gt; by running this command (in the installation directory): &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -r assets/*/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;clear the &amp;quot;auth cache&amp;quot;&lt;br /&gt;
: Flush the auth cache table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;&lt;br /&gt;
DELETE FROM `x2_auth_cache` WHERE 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
;log all users out&lt;br /&gt;
: Flush the sessions table, i.e. run the following command on the database:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;DELETE FROM `x2_sessions` WHERE 1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Upgrading =&lt;br /&gt;
&lt;br /&gt;
== Using the Web Interface ==&lt;br /&gt;
[[File:Software-Upgrade-Registration.png|400px|thumb|right|Software upgrade page, in the first step (registration)]]&lt;br /&gt;
To upgrade to Professional Edition&lt;br /&gt;
# Purchase X2Engine Professional Edition Download. Note the registration info in the activation email.&lt;br /&gt;
# Go to the Admin page in X2Engine and find &amp;quot;Upgrade X2Engine&amp;quot; under &amp;quot;System Settings&amp;quot;.&lt;br /&gt;
# In the ''Upgrade'' page, enter your registration info into the form and click &amp;quot;Register&amp;quot; to continue. Once complete, you will see a list of changes similar to those displayed when performing an update.&lt;br /&gt;
# Review the changes to be applied and then proceed by clicking &amp;quot;Upgrade&amp;quot;, similar to performing an update.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Offline&amp;quot; Upgrading ==&lt;br /&gt;
This works in exactly the same way as offline updating. However, to obtain your upgrade package, which you will extract into a folder called &amp;quot;update&amp;quot; on the X2Engine web server, contact [mailto:customersupport@x2engine.com customer support]. Furthermore, you will need to run the following database command on your X2Engine database when done with the upgrade, replacing &amp;lt;tt&amp;gt;{product key}&amp;lt;/tt&amp;gt; with your product key, to allow your installation to receive Professional Edition software updates:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{product key}' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Manually Upgrading ==&lt;br /&gt;
The process of upgrading is very similar to that of updating, and uses all of the same underpinnings as the updater. Thus, almost all of the above information and procedures will apply to upgrades. However, note the following crucial differences:&lt;br /&gt;
&lt;br /&gt;
* '''X2Engine must be at the latest available version in order to upgrade.'''&lt;br /&gt;
* '''To obtain the upgrade package, you must be an owner of an X2Engine Professional Edition Download-type license.''' Contact [mailto:customersupport@x2engine.com customer support] for further details.&lt;br /&gt;
* '''There will be only one &amp;quot;intermediate&amp;quot; version.''' The changes to apply will move X2Engine from a version to that same version, applying instead the necessary changes to switch its ''edition'' from Open Source to Professional.&lt;br /&gt;
* '''There are no file deletions.'''&lt;br /&gt;
* '''The properties of the package manifest differ slightly.''' In upgrades, the properties of the data object are &amp;lt;tt&amp;gt;sqlUpgrade&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;fileUpgrade&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;sqlList&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;fileList&amp;lt;/tt&amp;gt;, etc. Note also, the update data is in aptly-named sub-folders of &amp;lt;tt&amp;gt;update/data&amp;lt;/tt&amp;gt; as well, and there are no &amp;lt;tt&amp;gt;sqlForce&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;migrationScripts&amp;lt;/tt&amp;gt; lists to handle.&lt;br /&gt;
* '''The configuration is not changed.''' Instead, the value stored in the field &amp;lt;tt&amp;gt;unique_id&amp;lt;/tt&amp;gt; of the admin record in the database table &amp;lt;tt&amp;gt;x2_admin&amp;lt;/tt&amp;gt; must be changed to the license key, and the &amp;lt;tt&amp;gt;edition&amp;lt;/tt&amp;gt; field must be changed to &amp;quot;pro&amp;quot;. To perform these both, run the following command on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''It is not necessary to clear caches or user sessions when done.''' These operations are performed in updates to eliminate ephemeral (session-ingrained) data that may no longer be applicable and thus cause problems. Upgrades will rarely, if ever, invalidate this data.&lt;br /&gt;
&lt;br /&gt;
= Enabling Beta Releases = &lt;br /&gt;
Beginning at version 4.1.2, X2Engine began a two-phase release process, in which each version would be released first as a beta, then to the broader public after initial bug finds by users of the beta versions.&lt;br /&gt;
&lt;br /&gt;
By default, the updater will not update to beta versions. The updater must be configured to use the proper URL on the update server for beta release data, by the following procedure:&lt;br /&gt;
# Create a file &amp;quot;constants-custom.php&amp;quot; in the root level of X2Engine. You can easily do this by renaming &amp;quot;constants-custom.example.php&amp;quot; to that name (remove &amp;quot;.custom&amp;quot;).&lt;br /&gt;
# Look for the following line, and in it, change &amp;quot;false&amp;quot; to &amp;quot;true&amp;quot;: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('X2_UPDATE_BETA') or define('X2_UPDATE_BETA',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
== User interface (UI) &amp;quot;bugs&amp;quot; after the update ==&lt;br /&gt;
It is extremely common (and easily remedied) that after an important update, certain features of the user interface of X2Engine may not work properly. This is caused, in nearly all cases, by the web browser's cache, which contains out-of-date copies of X2Engine's essential user interface files. To solve these problems, the first step to try is clearing the cache. A step-by-step guide to doing so is given in [[wikihow:Clear-Your-Browser%27s-Cache]].&lt;br /&gt;
&lt;br /&gt;
== Errors in the updater itself == &lt;br /&gt;
When all else fails, the updater may have to be patched due to an irreconcilable backwards compatibility issue. Fortunately, if one has read/write access to the source files on the webserver, this is straightforward to fix. See [[#Refreshing_and_Updating_the_Updater_Utility|Refreshing and Updating the Updater Utility]].&lt;br /&gt;
&lt;br /&gt;
== Professional Edition not in effect after upgrading ==&lt;br /&gt;
This may be caused by Professional Edition not getting &amp;quot;switched on&amp;quot; properly, despite all the necessary filesystem and database changes already being in effect. First, to see if this scenario applies to you, look for a folder &amp;quot;&amp;lt;tt&amp;gt;reports&amp;lt;/tt&amp;gt;&amp;quot; inside &amp;lt;tt&amp;gt;protected/modules&amp;lt;/tt&amp;gt;, and a table &amp;lt;tt&amp;gt;x2_reports&amp;lt;/tt&amp;gt; in the X2Engine database. If you find these things, you can try the following to enable Professional Edition:&lt;br /&gt;
# Find the file &amp;lt;tt&amp;gt;constants.php&amp;lt;/tt&amp;gt; in the installation directory. Open it in a text editor, and look for the following line: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;defined('PRO_VERSION') or define('PRO_VERSION',false);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Change &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; on that line, and save the file.&lt;br /&gt;
# Run the following SQL command (replacing &amp;lt;tt&amp;gt;{license key}&amp;lt;/tt&amp;gt; with your X2Engine license key) on the X2Engine database: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE `x2_admin` SET `unique_id`='{license key}',`edition`='pro' WHERE `id`=1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General Procedures to Try ==&lt;br /&gt;
It may sometimes be possible that the update did not finish cleanly. In such cases, it will be necessary to manually re-perform some of the final operations in the update, described in [[#Introduction|Introduction]]. &lt;br /&gt;
&lt;br /&gt;
=== Refresh the Source Code ===&lt;br /&gt;
This step is not really the best step to try first, as it is usually more time-consuming. It entails re-uploading the entire application, in order to reset all non-ephemeral (source code) files to what they should be in a fresh installation of X2Engine. To do this:&lt;br /&gt;
# Download the refresh package. There should be a download link for this in the ''Updater Settings'' page. Otherwise, if that doesn't work and you're using Open Source edition, use [https://x2planet.com/installs/refresh.zip this link], or if using Professional Edition, paste the following url (after replacing {key} with your license key) into your browser's address bar: &amp;lt;pre&amp;gt;https://x2planet.com/installs/{key}/refresh-pro.zip&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Upload the ''contents'' of the downloaded zip archive to the installation directory, overwriting existing files.&lt;br /&gt;
&lt;br /&gt;
=== Clear Caches ===&lt;br /&gt;
See &amp;quot;[[#Clearing_caches_and_refreshing_the_application|Clearing Caches...]]&amp;quot; for details.&lt;br /&gt;
&lt;br /&gt;
=== Regenerate the Configuration ===&lt;br /&gt;
Again, this should not be done unless you are completely certain that all the files (not counting customizations) and the structure of the database are equivalent to a new installation at the same version as the one you updated to. Refer to [[#Editing_the_Configuration_File|Editing the Configuration File]].&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1837</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1837"/>
				<updated>2014-06-16T18:33:52Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Partially-Supported Models */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module, by going to the edit page for that user. To summarize:&lt;br /&gt;
# As the administrator, go to the user management module. You'll find it under &amp;quot;Users&amp;quot;&lt;br /&gt;
# Click on the desired user&lt;br /&gt;
# Click &amp;quot;Update User&amp;quot;&lt;br /&gt;
# See the &amp;quot;API Key&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Caveats ===&lt;br /&gt;
'''Note, firstly:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
Furthermore, if you have password-protected any web directories that contain X2Engine, you will need to make a &amp;quot;Satisfy any&amp;quot; exception for URIs within the API, or clients may not be able to authenticate.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow; effectively, the comparison in the query is being performed on a &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt; column in a joined table&lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2Leads&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type can be fully accessed and manipulated, but there is not yet any built-in action available for directly converting a lead to an opportunity.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1836</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1836"/>
				<updated>2014-06-16T18:30:39Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Partially-Supported Models */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module, by going to the edit page for that user. To summarize:&lt;br /&gt;
# As the administrator, go to the user management module. You'll find it under &amp;quot;Users&amp;quot;&lt;br /&gt;
# Click on the desired user&lt;br /&gt;
# Click &amp;quot;Update User&amp;quot;&lt;br /&gt;
# See the &amp;quot;API Key&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Caveats ===&lt;br /&gt;
'''Note, firstly:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
Furthermore, if you have password-protected any web directories that contain X2Engine, you will need to make a &amp;quot;Satisfy any&amp;quot; exception for URIs within the API, or clients may not be able to authenticate.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow. &lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2Leads&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type can be fully accessed and manipulated, but there is not yet any built-in action available for directly converting a lead to an opportunity.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1835</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1835"/>
				<updated>2014-06-16T18:28:40Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Fully-Supported Model Classes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module, by going to the edit page for that user. To summarize:&lt;br /&gt;
# As the administrator, go to the user management module. You'll find it under &amp;quot;Users&amp;quot;&lt;br /&gt;
# Click on the desired user&lt;br /&gt;
# Click &amp;quot;Update User&amp;quot;&lt;br /&gt;
# See the &amp;quot;API Key&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Caveats ===&lt;br /&gt;
'''Note, firstly:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
Furthermore, if you have password-protected any web directories that contain X2Engine, you will need to make a &amp;quot;Satisfy any&amp;quot; exception for URIs within the API, or clients may not be able to authenticate.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow. &lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1834</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1834"/>
				<updated>2014-06-16T18:28:30Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Fully-Supported Model Classes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module, by going to the edit page for that user. To summarize:&lt;br /&gt;
# As the administrator, go to the user management module. You'll find it under &amp;quot;Users&amp;quot;&lt;br /&gt;
# Click on the desired user&lt;br /&gt;
# Click &amp;quot;Update User&amp;quot;&lt;br /&gt;
# See the &amp;quot;API Key&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Caveats ===&lt;br /&gt;
'''Note, firstly:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
Furthermore, if you have password-protected any web directories that contain X2Engine, you will need to make a &amp;quot;Satisfy any&amp;quot; exception for URIs within the API, or clients may not be able to authenticate.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;X2Leads&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow. &lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1833</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1833"/>
				<updated>2014-06-13T16:47:42Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Authentication */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module, by going to the edit page for that user. To summarize:&lt;br /&gt;
# As the administrator, go to the user management module. You'll find it under &amp;quot;Users&amp;quot;&lt;br /&gt;
# Click on the desired user&lt;br /&gt;
# Click &amp;quot;Update User&amp;quot;&lt;br /&gt;
# See the &amp;quot;API Key&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Caveats ===&lt;br /&gt;
'''Note, firstly:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
Furthermore, if you have password-protected any web directories that contain X2Engine, you will need to make a &amp;quot;Satisfy any&amp;quot; exception for URIs within the API, or clients may not be able to authenticate.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow. &lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1832</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1832"/>
				<updated>2014-06-13T16:44:32Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Access Credentials */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module, by going to the edit page for that user. To summarize:&lt;br /&gt;
# As the administrator, go to the user management module. You'll find it under &amp;quot;Users&amp;quot;&lt;br /&gt;
# Click on the desired user&lt;br /&gt;
# Click &amp;quot;Update User&amp;quot;&lt;br /&gt;
# See the &amp;quot;API Key&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Note:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow. &lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Manually_Resetting_Passwords&amp;diff=1831</id>
		<title>Manually Resetting Passwords</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Manually_Resetting_Passwords&amp;diff=1831"/>
				<updated>2014-06-10T17:49:50Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
&lt;br /&gt;
This is an alternate method for resetting passwords, in the event that using the native password reset feature does not work due to improper configuration.&lt;br /&gt;
&lt;br /&gt;
If an ordinary user has lost/forgotten their password:&lt;br /&gt;
# Log in to X2Engine as an administrator&lt;br /&gt;
# Go to the &amp;quot;Users&amp;quot; module by following the link in the top bar.&lt;br /&gt;
# Click on the user, and then click &amp;quot;update&amp;quot;&lt;br /&gt;
# Enter a new password into the ''Password'' field.&lt;br /&gt;
&lt;br /&gt;
If the web application's ''administrator's'' password has been lost, you will need:&lt;br /&gt;
* Direct access to the database using PHPMyAdmin or some other means&lt;br /&gt;
* (optionally) a software tool for generating MD5 hashes&lt;br /&gt;
&lt;br /&gt;
To reset the password:&lt;br /&gt;
# Come up with a new secure password and make note of it.&lt;br /&gt;
# Generate an MD5 hash of the new password, if an MD5 tool is available. Otherwise, the string &amp;lt;tt&amp;gt;5f4dcc3b5aa765d61d8327deb882cf99&amp;lt;/tt&amp;gt; can be used (it is the MD5 hash of &amp;quot;password&amp;quot; in all lower-case).&lt;br /&gt;
# Log into the database.&lt;br /&gt;
# Set the &amp;quot;password&amp;quot; field in the users table to the new MD5 hash on the admin user, i.e. to set it to lowercase &amp;quot;password&amp;quot; for user &amp;quot;admin&amp;quot;: &amp;lt;syntaxhighlight lang=&amp;quot;mysql&amp;quot;&amp;gt;UPDATE x2_users SET password='5f4dcc3b5aa765d61d8327deb882cf99' WHERE username='admin';&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# If &amp;quot;password&amp;quot; was used as the password, log in immediately as the administrator and change the password to something else.&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Customization_Framework&amp;diff=1829</id>
		<title>Customization Framework</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Customization_Framework&amp;diff=1829"/>
				<updated>2014-05-27T19:59:35Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Automatically Updating Custom Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
= Introduction =&lt;br /&gt;
X2Engine's source code is completely transparent and totally under your control. There's no need to compiled PHP, so altering your software is a simply a matter of hacking around in the files. Traditionally, this meant directly altering the source code within X2Engine's file structure. But this could be problematic to keep track of, given the vast number of files. On top of that, automatic updates to any of those files would wipe out all of your changes, so you would have to choose between customization and receiving updates.&lt;br /&gt;
&lt;br /&gt;
X2Engine provides a means to alter classes and templates without modifying the original source files. You can create an alternate version of any PHP file, while preserving the original, which makes it much easier to keep track of your changes as well as preserving your alterations through updates. You may still need to rework your custom code when an update is incompatible, but it will be much easier.&lt;br /&gt;
&lt;br /&gt;
= Overriding Files =&lt;br /&gt;
[[File:Custom_folder.gif|right]]&lt;br /&gt;
Practically any php file used in X2Engine can be overridden. X2Engine's customization framework resides in the &amp;lt;tt&amp;gt;/custom&amp;lt;/tt&amp;gt; directory, which mirrors the structure of the source code. For example, &amp;lt;tt&amp;gt;/custom/protected/components&amp;lt;/tt&amp;gt; corresponds to &amp;lt;tt&amp;gt;/protected/components&amp;lt;/tt&amp;gt;. Any php file placed in &amp;lt;tt&amp;gt;/custom&amp;lt;/tt&amp;gt; will be used instead of the original.&lt;br /&gt;
{{clear}}&lt;br /&gt;
= Extending Controllers =&lt;br /&gt;
X2Engine is built on Yii, which uses MVC (model-view-controller) architecture. Overriding an entire file is practical when dealing with views or smaller classes (such as models), but controllers can present a greater challenge. It's only a matter of time before we update one of the 1500+ lines of code in a controller. This means you have to manually find and transfer your changes from the old version, which is time-consuming. To remedy this, we allow substitution of any controller in X2Engine with an extended class.&lt;br /&gt;
&lt;br /&gt;
Whenever a controller is loaded, X2Engine checks for the same filename with &amp;quot;My&amp;quot; at the beginning. For example, if you want to override a single action to ContactsController, you can create a file called MyContactsController in &amp;lt;tt&amp;gt;/custom/protected/modules/contacts/controllers&amp;lt;/tt&amp;gt; containing the following:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
class MyContactsController extends ContactsController {&lt;br /&gt;
    function actionIndex() {&lt;br /&gt;
        echo 'Hello World!';&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This file will automatically be used instead of ContactsController and should still work if ContactsController is changed in an update. You may still need to manually merge changes if an update alters the same part of the controller that you changed, but it will be much easier to find.&lt;br /&gt;
&lt;br /&gt;
= Custom App Configuration =&lt;br /&gt;
The configuration for the Yii application can also be customized through the files &amp;quot;web.php&amp;quot;, &amp;quot;console.php&amp;quot; and &amp;quot;test.php&amp;quot; in &amp;lt;tt&amp;gt;custom/protected/config&amp;lt;/tt&amp;gt;. These files will be run in the default configuration files for X2Engine running as a web application, console application or unit testing environment (respectively). &lt;br /&gt;
&lt;br /&gt;
An array which mirrors the modified parts of main.php (the default configuration file) should be returned within the custom config file. To customize the configuration of the application, i.e. to add parameters to &amp;lt;tt&amp;gt;Yii::app()-&amp;gt;params&amp;lt;/tt&amp;gt;, one only need add/remove/alter the values and keys within &amp;lt;tt&amp;gt;$config&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Automatically Updating Custom Code =&lt;br /&gt;
For all non-controller components overriding core classes, a console command has been written to ease the process of merging upstream changes. This command, [[x2doc:MigrateCustomCommand]], will perform this by automating the following procedural steps:&lt;br /&gt;
&lt;br /&gt;
# Create a temporary branch for performing the merge in an up-to-date clone of the X2Engine Git repository, from the revision corresponding to the version updating from.&lt;br /&gt;
# Copy all custom code into the repository on that new branch&lt;br /&gt;
# Commit the custom changes&lt;br /&gt;
# Merge changes from the latter version into the temporary branch, leaving the user to sort out merge conflicts (if any)&lt;br /&gt;
# Copy the updated custom files back into place&lt;br /&gt;
&lt;br /&gt;
To run this command, you will need:&lt;br /&gt;
* A UNIX shell environment in which the rsync program is available&lt;br /&gt;
* An up-to-date clone of the X2Engine Git repository&lt;br /&gt;
&lt;br /&gt;
Finally, the process to update is as follows:&lt;br /&gt;
# In &amp;lt;tt&amp;gt;protected/&amp;lt;/tt&amp;gt;, run: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./yiic migratecustom --origin=&amp;lt;version of X2Engine updating from&amp;gt; --target=&amp;lt;version of X2Engine updating to&amp;gt; --gitdir=&amp;lt;path to base level of the Git repository&amp;gt; --source=&amp;lt;path to &amp;quot;custom&amp;quot; directory&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# If there were any merge conflicts, they should be displayed in the output. You should then resolve the conflicts, commit the changes, and follow the instructions printed by the command.&lt;br /&gt;
&lt;br /&gt;
= Known Issues =&lt;br /&gt;
* Currently, only PHP files can be substituted. To change a CSS or Javascript file you would have to edit the original file.&lt;br /&gt;
&lt;br /&gt;
* There is no guarantee a customized installation will still work after an update. You will still have to manually merge changes if you want (or need) to use an updated version of a customized file. Depending on the file, you may have to do this for most automatic updates (since some files are changed far more often than others). Luckilly, most of our changes are usually in controller files, which can be extended rather than just replaced, so the updates are less likely to be incompatible with your modifications.&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Customization_Framework&amp;diff=1828</id>
		<title>Customization Framework</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Customization_Framework&amp;diff=1828"/>
				<updated>2014-05-27T19:18:10Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Automatically Updating Custom Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
= Introduction =&lt;br /&gt;
X2Engine's source code is completely transparent and totally under your control. There's no need to compiled PHP, so altering your software is a simply a matter of hacking around in the files. Traditionally, this meant directly altering the source code within X2Engine's file structure. But this could be problematic to keep track of, given the vast number of files. On top of that, automatic updates to any of those files would wipe out all of your changes, so you would have to choose between customization and receiving updates.&lt;br /&gt;
&lt;br /&gt;
X2Engine provides a means to alter classes and templates without modifying the original source files. You can create an alternate version of any PHP file, while preserving the original, which makes it much easier to keep track of your changes as well as preserving your alterations through updates. You may still need to rework your custom code when an update is incompatible, but it will be much easier.&lt;br /&gt;
&lt;br /&gt;
= Overriding Files =&lt;br /&gt;
[[File:Custom_folder.gif|right]]&lt;br /&gt;
Practically any php file used in X2Engine can be overridden. X2Engine's customization framework resides in the &amp;lt;tt&amp;gt;/custom&amp;lt;/tt&amp;gt; directory, which mirrors the structure of the source code. For example, &amp;lt;tt&amp;gt;/custom/protected/components&amp;lt;/tt&amp;gt; corresponds to &amp;lt;tt&amp;gt;/protected/components&amp;lt;/tt&amp;gt;. Any php file placed in &amp;lt;tt&amp;gt;/custom&amp;lt;/tt&amp;gt; will be used instead of the original.&lt;br /&gt;
{{clear}}&lt;br /&gt;
= Extending Controllers =&lt;br /&gt;
X2Engine is built on Yii, which uses MVC (model-view-controller) architecture. Overriding an entire file is practical when dealing with views or smaller classes (such as models), but controllers can present a greater challenge. It's only a matter of time before we update one of the 1500+ lines of code in a controller. This means you have to manually find and transfer your changes from the old version, which is time-consuming. To remedy this, we allow substitution of any controller in X2Engine with an extended class.&lt;br /&gt;
&lt;br /&gt;
Whenever a controller is loaded, X2Engine checks for the same filename with &amp;quot;My&amp;quot; at the beginning. For example, if you want to override a single action to ContactsController, you can create a file called MyContactsController in &amp;lt;tt&amp;gt;/custom/protected/modules/contacts/controllers&amp;lt;/tt&amp;gt; containing the following:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
class MyContactsController extends ContactsController {&lt;br /&gt;
    function actionIndex() {&lt;br /&gt;
        echo 'Hello World!';&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This file will automatically be used instead of ContactsController and should still work if ContactsController is changed in an update. You may still need to manually merge changes if an update alters the same part of the controller that you changed, but it will be much easier to find.&lt;br /&gt;
&lt;br /&gt;
= Custom App Configuration =&lt;br /&gt;
The configuration for the Yii application can also be customized through the files &amp;quot;web.php&amp;quot;, &amp;quot;console.php&amp;quot; and &amp;quot;test.php&amp;quot; in &amp;lt;tt&amp;gt;custom/protected/config&amp;lt;/tt&amp;gt;. These files will be run in the default configuration files for X2Engine running as a web application, console application or unit testing environment (respectively). &lt;br /&gt;
&lt;br /&gt;
An array which mirrors the modified parts of main.php (the default configuration file) should be returned within the custom config file. To customize the configuration of the application, i.e. to add parameters to &amp;lt;tt&amp;gt;Yii::app()-&amp;gt;params&amp;lt;/tt&amp;gt;, one only need add/remove/alter the values and keys within &amp;lt;tt&amp;gt;$config&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Automatically Updating Custom Code =&lt;br /&gt;
For all non-controller components overriding core classes, a console command has been written to ease the process of merging upstream changes. This command, [[x2doc:MigrateCustomCommand]], will perform this by automating the following procedural steps:&lt;br /&gt;
&lt;br /&gt;
# Create a temporary branch for performing the merge in an up-to-date clone of the X2Engine Git repository, from the revision corresponding to the version updating from.&lt;br /&gt;
# Copy all custom code into the repository on that new branch&lt;br /&gt;
# Commit the custom changes&lt;br /&gt;
# Merge changes from the latter version into the temporary branch, leaving the user to sort out merge conflicts (if any)&lt;br /&gt;
# Copy the updated custom files back into place&lt;br /&gt;
&lt;br /&gt;
To run this command, you will need:&lt;br /&gt;
* A UNIX shell environment in which the rsync program is available&lt;br /&gt;
* An up-to-date clone of the X2Engine Git repository&lt;br /&gt;
&lt;br /&gt;
Finally, the process to update is as follows:&lt;br /&gt;
# In &amp;lt;tt&amp;gt;protected/&amp;lt;/tt&amp;gt;, run: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./yiic migratecustom --origin=&amp;lt;version of X2Engine updating from&amp;gt; --target=&amp;lt;version of X2Engine updating to&amp;gt; --gitdir=&amp;lt;path to the Git repository&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# If there were any merge conflicts, they should be displayed in the output. You should then resolve the conflicts, commit the changes, and follow the instructions printed by the command.&lt;br /&gt;
&lt;br /&gt;
Note, the &amp;quot;migratecustom&amp;quot; command also accepts an optional &amp;lt;tt&amp;gt;--source&amp;lt;/tt&amp;gt; argument pointing to the path of the custom source directory, if in a different directory than the installation of X2Engine in which the command is being run.&lt;br /&gt;
&lt;br /&gt;
= Known Issues =&lt;br /&gt;
* Currently, only PHP files can be substituted. To change a CSS or Javascript file you would have to edit the original file.&lt;br /&gt;
&lt;br /&gt;
* There is no guarantee a customized installation will still work after an update. You will still have to manually merge changes if you want (or need) to use an updated version of a customized file. Depending on the file, you may have to do this for most automatic updates (since some files are changed far more often than others). Luckilly, most of our changes are usually in controller files, which can be extended rather than just replaced, so the updates are less likely to be incompatible with your modifications.&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1827</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1827"/>
				<updated>2014-05-24T00:43:37Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Creating Web Hooks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module  administrator. Any user can authenticate via the API by visiting the ''Update'' page for any given user.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Note:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow. &lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1826</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1826"/>
				<updated>2014-05-24T00:42:34Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Events Reference */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module  administrator. Any user can authenticate via the API by visiting the ''Update'' page for any given user.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Note:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow. &lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Supported Event Names and X2Flow]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1825</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1825"/>
				<updated>2014-05-24T00:28:39Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Events Reference */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module  administrator. Any user can authenticate via the API by visiting the ''Update'' page for any given user.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Note:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow. &lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Supported Event Names and X2Flow]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1824</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1824"/>
				<updated>2014-05-24T00:26:49Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Events Reference */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module  administrator. Any user can authenticate via the API by visiting the ''Update'' page for any given user.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Note:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow. &lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Supported Event Names and X2Flow]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1823</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1823"/>
				<updated>2014-05-23T23:08:34Z</updated>
		
		<summary type="html">&lt;p&gt;Demitri: /* Deleting Web Hooks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module  administrator. Any user can authenticate via the API by visiting the ''Update'' page for any given user.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Note:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow. &lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Supported Event Names and X2Flow]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|'''model''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|'''model''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|'''model''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|'''model''': the contact who clicked their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Demitri</name></author>	</entry>

	</feed>