blob: 588df7281677e6d0f574aadafaaf607831d50ec5 [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- Generated by Apache Maven Doxia at 2021-11-12 -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Apache James Project &#x2013; Web administration for JAMES</title>
<style type="text/css" media="all">
@import url("../css/james.css");
@import url("../css/maven-base.css");
@import url("../css/maven-theme.css");
@import url("../css/site.css");
@import url("../js/jquery/css/custom-theme/jquery-ui-1.8.5.custom.css");
@import url("../js/jquery/css/print.css");
@import url("../js/fancybox/jquery.fancybox-1.3.4.css");
</style>
<script type="text/javascript" src="../js/jquery/js/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="../js/jquery/js/jquery-ui-1.8.5.custom.min.js"></script>
<script type="text/javascript" src="../js/fancybox/jquery.fancybox-1.3.4.js"></script>
<link rel="stylesheet" href="../css/print.css" type="text/css" media="print" />
<meta name="Date-Revision-yyyymmdd" content="20211112" />
<meta http-equiv="Content-Language" content="en" />
<!-- Google Analytics -->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-1384591-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script').item(0); s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body class="composite">
<div id="banner">
<a href="../index.html" id="bannerLeft" title="james-logo.png">
<img src="../images/logos/james-logo.png" alt="James Project" />
</a>
<a href="https://www.apache.org/index.html" id="bannerRight">
<img src="images/logos/asf_logo_small.png" alt="The Apache Software Foundation" />
</a>
<div class="clear">
<hr/>
</div>
</div>
<div id="breadcrumbs">
<div class="xleft">
<span id="publishDate">Last Published: 2021-11-12</span>
</div>
<div class="xright"> <a href="../index.html" title="Home">Home</a>
|
<a href="../documentation.html" title="James">James</a>
|
<a href="../mime4j/index.html" title="Mime4J">Mime4J</a>
|
<a href="../jsieve/index.html" title="jSieve">jSieve</a>
|
<a href="../jspf/index.html" title="jSPF">jSPF</a>
|
<a href="../jdkim/index.html" title="jDKIM">jDKIM</a>
</div>
<div class="clear">
<hr/>
</div>
</div>
<div id="leftColumn">
<div id="navcolumn">
<h5>James components</h5>
<ul>
<li class="collapsed">
<a href="../documentation.html" title="About James">About James</a>
</li>
<li class="expanded">
<a href="../server/index.html" title="Server">Server</a>
<ul>
<li class="none">
<a href="../server/advantages.html" title="Advantages">Advantages</a>
</li>
<li class="none">
<a href="../server/objectives.html" title="Objectives">Objectives</a>
</li>
<li class="expanded">
<a href="../server/quick-start.html" title="User Manual">User Manual</a>
<ul>
<li class="collapsed">
<a href="../server/features.html" title="1. Features">1. Features</a>
</li>
<li class="none">
<a href="../server/packaging.html" title="2. Packaging">2. Packaging</a>
</li>
<li class="collapsed">
<a href="../server/install.html" title="3. Install James">3. Install James</a>
</li>
<li class="collapsed">
<a href="../server/config.html" title="4. Configure James">4. Configure James</a>
</li>
<li class="expanded">
<a href="../server/manage.html" title="5. Manage">5. Manage</a>
<ul>
<li class="none">
<a href="../server/manage-cli.html" title="Command line">Command line</a>
</li>
<li class="none">
<a href="../server/metrics.html" title="Metrics">Metrics</a>
</li>
<li class="none">
<strong>WebAdmin</strong>
</li>
<li class="none">
<a href="../server/manage-guice-distributed-james.html" title="Distributed James">Distributed James</a>
</li>
</ul>
</li>
<li class="collapsed">
<a href="../server/monitor.html" title="6. Monitor">6. Monitor</a>
</li>
<li class="collapsed">
<a href="../server/upgrade.html" title="7. Upgrade">7. Upgrade</a>
</li>
<li class="collapsed">
<a href="../server/dev.html" title="8. Developers Corner">8. Developers Corner</a>
</li>
</ul>
</li>
<li class="none">
<a href="../mail.html#James_Mailing_lists" title="Mailing Lists">Mailing Lists</a>
</li>
<li class="none">
<a href="../server/release-notes.html" title="Release Notes">Release Notes</a>
</li>
<li class="none">
<a href="../server/apidocs/index.html" title="Javadoc">Javadoc</a>
</li>
<li class="none">
<a href="https://issues.apache.org/jira/browse/JAMES" title="Issue Tracker">Issue Tracker</a>
</li>
<li class="none">
<a href="https://github.com/apache/james-project" title="Sources">Sources</a>
</li>
<li class="none">
<a href="../server/rfcs.html" title="RFCs">RFCs</a>
</li>
<li class="none">
<a href="../download.cgi#Apache_James_Server" title="Download releases">Download releases</a>
</li>
</ul>
</li>
<li class="collapsed">
<a href="../mailet/index.html" title="Mailets">Mailets</a>
</li>
<li class="collapsed">
<a href="../mailbox/index.html" title="Mailbox">Mailbox</a>
</li>
<li class="collapsed">
<a href="../protocols/index.html" title="Protocols">Protocols</a>
</li>
<li class="collapsed">
<a href="../mpt/index.html" title="MPT">MPT</a>
</li>
</ul>
<h5>Apache Software Foundation</h5>
<ul>
<li>
<strong>
<a title="ASF" href="http://www.apache.org/">ASF</a>
</strong>
</li>
<li>
<a title="Get Involved" href="http://www.apache.org/foundation/getinvolved.html">Get Involved</a>
</li>
<li>
<a title="FAQ" href="http://www.apache.org/foundation/faq.html">FAQ</a>
</li>
<li>
<a title="License" href="http://www.apache.org/licenses/" >License</a>
</li>
<li>
<a title="Sponsorship" href="http://www.apache.org/foundation/sponsorship.html">Sponsorship</a>
</li>
<li>
<a title="Thanks" href="http://www.apache.org/foundation/thanks.html">Thanks</a>
</li>
<li>
<a title="Security" href="http://www.apache.org/security/">Security</a>
</li>
</ul>
<a href="http://maven.apache.org/" title="Built by Maven" class="poweredBy">
<img class="poweredBy" alt="Built by Maven" src="../images/logos/maven-feather.png" />
</a>
</div>
</div>
<div id="bodyColumn">
<div id="contentBox">
<h1>Web administration for JAMES</h1>
<p>The web administration supports for now the CRUD operations on the domains, the users, their mailboxes and their quotas, managing mail repositories, performing cassandra migrations, and much more, as described in the following sections.</p>
<p><b>WARNING</b>: This API allow authentication only via the use of JWT. If not configured with JWT, an administrator should ensure an attacker can not use this API.</p>
<p>By the way, some endpoints are not filtered by authentication. Those endpoints are not related to data stored in James, for example: Swagger documentation &amp; James health checks.</p>
<p>Please also note <b>webadmin</b> is only enabled with <b>Guice</b>. You can not use it when using James with <b>Spring</b>, as the required injections are not implemented.</p>
<p>In case of any error, the system will return an error message which is json format like this:</p>
<div class="source">
<div class="source">
<pre>{
statusCode: &lt;error_code&gt;,
type: &lt;error_type&gt;,
message: &lt;the_error_message&gt;
cause: &lt;the_detail_message_from_throwable&gt;
}
</pre></div></div>
<p>Also be aware that, in case things go wrong, all endpoints might return a 500 internal error (with a JSON body formatted as exposed above). To avoid information duplication, this is ommited on endpoint specific documentation.</p>
<p>Finally, please note that in case of a malformed URL the 400 bad request response will contain an HTML body.</p><section>
<h2><a name="Navigation_menu"></a>Navigation menu</h2>
<ul>
<li><a href="#HealthCheck">HealthCheck</a></li>
<li><a href="#Administrating_domains">Administrating domains</a></li>
<li><a href="#Administrating_users">Administrating users</a></li>
<li><a href="#Administrating_mailboxes">Administrating mailboxes</a></li>
<li><a href="#Administrating_Messages">Administrating messages</a></li>
<li><a href="#Administrating_user_mailboxes">Administrating user mailboxes</a></li>
<li><a href="#Administrating_quotas_by_users">Administrating quotas by users</a></li>
<li><a href="#Administrating_quotas_by_domains">Administrating quotas by domains</a></li>
<li><a href="#Administrating_global_quotas">Administrating global quotas</a></li>
<li><a href="#Cassandra_Schema_upgrades">Cassandra Schema upgrades</a></li>
<li><a href="#Correcting_ghost_mailbox">Correcting ghost mailbox</a></li>
<li><a href="#Creating_address_aliases">Creating address aliases</a></li>
<li><a href="#Creating_domain_mappings">Creating domain mappings</a></li>
<li><a href="#Creating_address_forwards">Creating address forwards</a></li>
<li><a href="#Creating_address_group">Creating address group</a></li>
<li><a href="#Creating_regex_mapping">Creating regex mapping</a></li>
<li><a href="#Address_Mappings">Address Mappings</a></li>
<li><a href="#User_Mappings">User Mappings</a></li>
<li><a href="#Administrating_mail_repositories">Administrating mail repositories</a></li>
<li><a href="#Administrating_mail_queues">Administrating mail queues</a></li>
<li><a href="#Sending_email_over_webAdmin">Sending email over webAdmin</a></li>
<li><a href="#Administrating_DLP_Configuration">Administrating DLP Configuration</a></li>
<li><a href="#Administrating_Sieve_quotas">Administrating Sieve quotas</a></li>
<li><a href="#Running_blob_garbage_collection">Running blob garbage collection</a></li>
<li><a href="#Administrating_jmap_uploads">Administrating jmap uploads</a></li>
<li><a href="#Deleted_Messages_Vault">Deleted Messages Vault</a></li>
<li><a href="#Task_management">Task management</a></li>
<li><a href="#Cassandra_extra_operations">Cassandra extra operations</a></li>
<li><a href="#Event_Dead_Letter">Event Dead Letter</a></li>
</ul></section><section>
<h2><a name="HealthCheck"></a>HealthCheck</h2>
<ul>
<li><a href="#Check_all_components">Check all components</a></li>
<li><a href="#Check_single_component">Check single component</a></li>
<li><a href="#List_all_health_checks">List all health checks</a></li>
</ul><section>
<h3><a name="Check_all_components"></a>Check all components</h3>
<p>This endpoint is simple for now and is just returning the http status code corresponding to the state of checks (see below). The user has to check in the logs in order to have more information about failing checks.</p>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/healthcheck
</pre></div></div>
<p>Will return a list of healthChecks execution result, with an aggregated result:</p>
<div class="source">
<div class="source">
<pre>{
&quot;status&quot;: &quot;healthy&quot;,
&quot;checks&quot;: [
{
&quot;componentName&quot;: &quot;Cassandra backend&quot;,
&quot;escapedComponentName&quot;: &quot;Cassandra%20backend&quot;,
&quot;status&quot;: &quot;healthy&quot;
&quot;cause&quot;: null
}
]
}
</pre></div></div>
<p><b>status</b> field can be:</p>
<ul>
<li><b>healthy</b>: Component works normally</li>
<li><b>degraded</b>: Component works in degraded mode. Some non-critical services may not be working, or latencies are high, for example. Cause contains explanations.</li>
<li><b>unhealthy</b>: The component is currently not working. Cause contains explanations.</li>
</ul>
<p>Supported health checks include:</p>
<ul>
<li><b>Cassandra backend</b>: Cassandra storage. Included in Cassandra Guice based products.</li>
<li><b>ElasticSearch Backend</b>: ElasticSearch storage. Included in Cassandra Guice based products.</li>
<li><b>EventDeadLettersHealthCheck</b>: Included in all Guice products.</li>
<li><b>Guice application lifecycle</b>: included in all Guice products.</li>
<li><b>JPA Backend</b>: JPA storage. Included in JPA Guice based products.</li>
<li><b>MailReceptionCheck</b> We rely on a configured user, send an email to him and assert that the email is well received, and can be read within the given configured period. Unhealthy means that the email could not be received before reacing the timeout.</li>
<li><b>MessageFastViewProjection</b>: included in memory and Cassandra based Guice products. Health check of the component storing JMAP properties which are fast to retrieve. Those properties are computed in advance from messages and persisted in order to archive a better performance. There are some latencies between a source update and its projections updates. Incoherency problems arise when reads are performed in this time-window. We piggyback the projection update on missed JMAP read in order to decrease the outdated time window for a given entry. The health is determined by the ratio of missed projection reads. (lower than 10% causes degraded)</li>
<li>
<p><b>RabbitMQ backend</b>: RabbitMQ messaging. Included in Distributed Guice based products.</p>
</li>
</ul>
<p>Response codes:</p>
<ul>
<li>200: All checks have answered with a Healthy or Degraded status. James services can still be used.</li>
<li>503: At least one check have answered with a Unhealthy status</li>
</ul></section><section>
<h3><a name="Check_single_component"></a>Check single component</h3>
<p>Performs a health check for the given component. The component is referenced by its URL encoded name.</p>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/healthcheck/checks/Cassandra%20backend
</pre></div></div>
<p>Will return the component&#x2019;s name, the component&#x2019;s escaped name, the health status and a cause.</p>
<div class="source">
<div class="source">
<pre>{
&quot;componentName&quot;: &quot;Cassandra backend&quot;,
&quot;escapedComponentName&quot;: &quot;Cassandra%20backend&quot;,
&quot;status&quot;: &quot;healthy&quot;
&quot;cause&quot;: null
}
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: The check has answered with a Healthy or Degraded status.</li>
<li>404: A component with the given name was not found.</li>
<li>503: The check has anwered with a Unhealthy status.</li>
</ul></section><section>
<h3><a name="List_all_health_checks"></a>List all health checks</h3>
<p>This endpoint lists all the available health checks.</p>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/healthcheck/checks
</pre></div></div>
<p>Will return the list of all available health checks.</p>
<div class="source">
<div class="source">
<pre>[
{
&quot;componentName&quot;: &quot;Cassandra backend&quot;,
&quot;escapedComponentName&quot;: &quot;Cassandra%20backend&quot;
}
]
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: List of available health checks</li>
</ul></section></section><section>
<h2><a name="Administrating_domains"></a>Administrating domains</h2>
<ul>
<li><a href="#Create_a_domain">Create a domain</a></li>
<li><a href="#Delete_a_domain">Delete a domain</a></li>
<li><a href="#Test_if_a_domain_exists">Test if a domain exists</a></li>
<li><a href="#Get_the_list_of_domains">Get the list of domains</a></li>
<li><a href="#Get_the_list_of_aliases_for_a_domain">Get the list of aliases for a domain</a></li>
<li><a href="#Create_an_alias_for_a_domain">Create an alias for a domain</a></li>
<li><a href="#Delete_an_alias_for_a_domain">Delete an alias for a domain</a></li>
</ul><section>
<h3><a name="Create_a_domain"></a>Create a domain</h3>
<div class="source">
<div class="source">
<pre>curl -XPUT http://ip:port/domains/domainToBeCreated
</pre></div></div>
<p>Resource name domainToBeCreated:</p>
<ul>
<li>can not be null or empty</li>
<li>can not contain &#x2018;@&#x2019;</li>
<li>can not be more than 255 characters</li>
<li>can not contain &#x2018;/&#x2019;</li>
</ul>
<p>Response codes:</p>
<ul>
<li>204: The domain was successfully added</li>
<li>400: The domain name is invalid</li>
</ul></section><section>
<h3><a name="Delete_a_domain"></a>Delete a domain</h3>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/domains/{domainToBeDeleted}
</pre></div></div>
<p>Note: Deletion of an auto-detected domain, default domain or of an auto-detected ip is not supported. We encourage you instead to review your <a class="externalLink" href="https://james.apache.org/server/config-domainlist.html">domain list configuration</a>.</p>
<p>Response codes:</p>
<ul>
<li>204: The domain was successfully removed</li>
</ul></section><section>
<h3><a name="Test_if_a_domain_exists"></a>Test if a domain exists</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/domains/{domainName}
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>204: The domain exists</li>
<li>404: The domain does not exist</li>
</ul></section><section>
<h3><a name="Get_the_list_of_domains"></a>Get the list of domains</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/domains
</pre></div></div>
<p>Possible response:</p>
<div class="source">
<div class="source">
<pre>[&quot;domain1&quot;, &quot;domain2&quot;]
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: The domain list was successfully retrieved</li>
</ul></section><section>
<h3><a name="Get_the_list_of_aliases_for_a_domain"></a>Get the list of aliases for a domain</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/domains/destination.domain.tld/aliases
</pre></div></div>
<p>Possible response:</p>
<div class="source">
<div class="source">
<pre>[
{&quot;source&quot;: &quot;source1.domain.tld&quot;},
{&quot;source&quot;: &quot;source2.domain.tld&quot;}
]
</pre></div></div>
<p>When sending an email to an email address having source1.domain.tld or source2.domain.tld as a domain part (example: user@source1.domain.tld), then the domain part will be rewritten into destination.domain.tld (so into user@destination.domain.tld).</p>
<p>Response codes:</p>
<ul>
<li>200: The domain aliases was successfully retrieved</li>
<li>400: destination.domain.tld has an invalid syntax</li>
<li>404: destination.domain.tld is not part of handled domains and does not have local domains as aliases.</li>
</ul></section><section>
<h3><a name="Create_an_alias_for_a_domain"></a>Create an alias for a domain</h3>
<p>To create a domain alias execute the following query:</p>
<div class="source">
<div class="source">
<pre>curl -XPUT http://ip:port/domains/destination.domain.tld/aliases/source.domain.tld
</pre></div></div>
<p>When sending an email to an email address having source.domain.tld as a domain part (example: user@source.domain.tld), then the domain part will be rewritten into destination.domain.tld (so into user@destination.domain.tld).</p>
<p>Response codes:</p>
<ul>
<li>204: The redirection now exists</li>
<li>400: source.domain.tld or destination.domain.tld have an invalid syntax</li>
<li>400: source, domain and destination domain are the same</li>
<li>404: source.domain.tld are not part of handled domains.</li>
</ul>
<p>Be aware that no checks to find possible loops that would result of this creation will be performed.</p></section><section>
<h3><a name="Delete_an_alias_for_a_domain"></a>Delete an alias for a domain</h3>
<p>To delete a domain alias execute the following query:</p>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/domains/destination.domain.tld/aliases/source.domain.tld
</pre></div></div>
<p>When sending an email to an email address having source.domain.tld as a domain part (example: user@source.domain.tld), then the domain part will be rewritten into destination.domain.tld (so into user@destination.domain.tld).</p>
<p>Response codes:</p>
<ul>
<li>204: The redirection now no longer exists</li>
<li>400: source.domain.tld or destination.domain.tld have an invalid syntax</li>
<li>400: source, domain and destination domain are the same</li>
<li>404: source.domain.tld are not part of handled domains.</li>
</ul></section></section><section>
<h2><a name="Administrating_users"></a>Administrating users</h2>
<ul>
<li><a href="#Create_a_user">Create a user</a></li>
<li><a href="#Updating_a_user_password">Updating a user password</a></li>
<li><a href="#Testing_a_user_existence">Testing a user existence</a></li>
<li><a href="#Deleting_a_user">Deleting a user</a></li>
<li><a href="#Retrieving_the_user_list">Retrieving the user list</a></li>
<li><a href="Retrieving_the_list_of_allowed_From_headers_for_a_given_user">Retrieving the list of allowed From headers for a given user</a></li>
</ul><section>
<h3><a name="Create_a_user"></a>Create a user</h3>
<div class="source">
<div class="source">
<pre>curl -XPUT http://ip:port/users/usernameToBeUsed \
-d '{&quot;password&quot;:&quot;passwordToBeUsed&quot;}' \
-H &quot;Content-Type: application/json&quot;
</pre></div></div>
<p>Resource name usernameToBeUsed representing valid users, hence it should match the criteria at <a href="/server/config-users.html">User Repositories documentation</a></p>
<p>Response codes:</p>
<ul>
<li>204: The user was successfully created</li>
<li>400: The user name or the payload is invalid</li>
<li>409: The user name already exists</li>
</ul>
<p>Note: If the user exists already, its password cannot be updated using this. If you want to update a user&#x2019;s password, please have a look at <a href="#Updating_a_user_password">Update a user password</a>.</p></section><section>
<h3><a name="Updating_a_user_password"></a>Updating a user password</h3>
<div class="source">
<div class="source">
<pre>curl -XPUT http://ip:port/users/usernameToBeUsed?force \
-d '{&quot;password&quot;:&quot;passwordToBeUsed&quot;}' \
-H &quot;Content-Type: application/json&quot;
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>204: The user&#x2019;s password was successfully updated</li>
<li>400: The user name or the payload is invalid</li>
</ul>
<p>This also can be used to create a new user.</p></section><section>
<h3><a name="Testing_a_user_existence"></a>Testing a user existence</h3>
<div class="source">
<div class="source">
<pre>curl -XHEAD http://ip:port/users/usernameToBeUsed
</pre></div></div>
<p>Resource name &#x201c;usernameToBeUsed&#x201d; represents a valid user, hence it should match the criteria at <a href="/server/config-users.html">User Repositories documentation</a></p>
<p>Response codes:</p>
<ul>
<li>200: The user exists</li>
<li>400: The user name is invalid</li>
<li>404: The user does not exist</li>
</ul></section><section>
<h3><a name="Updating_a_user_password"></a>Updating a user password</h3>
<p>Same than Create, but a user need to exist.</p>
<p>If the user do not exist, then it will be created.</p></section><section>
<h3><a name="Deleting_a_user"></a>Deleting a user</h3>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/users/{userToBeDeleted}
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>204: The user was successfully deleted</li>
</ul></section><section>
<h3><a name="Retrieving_the_user_list"></a>Retrieving the user list</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/users
</pre></div></div>
<p>The answer looks like:</p>
<div class="source">
<div class="source">
<pre>[{&quot;username&quot;:&quot;username@domain-jmapauthentication.tld&quot;},{&quot;username&quot;:&quot;username@domain.tld&quot;}]
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: The user name list was successfully retrieved</li>
</ul></section><section>
<h3><a name="Retrieving_the_list_of_allowed_From_headers_for_a_given_user"></a>Retrieving the list of allowed From headers for a given user</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/users/givenUser/allowedFromHeaders
</pre></div></div>
<p>The answer looks like:</p>
<div class="source">
<div class="source">
<pre>[&quot;user@domain.tld&quot;,&quot;alias@domain.tld&quot;]
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: The list was successfully retrieved</li>
<li>400: The user is invalid</li>
<li>404: The user is unknown</li>
</ul></section></section><section>
<h2><a name="Administrating_mailboxes"></a>Administrating mailboxes</h2><section>
<h3><a name="All_mailboxes"></a>All mailboxes</h3>
<p>Several actions can be performed on the server mailboxes.</p>
<p>Request pattern is:</p>
<div class="source">
<div class="source">
<pre>curl -XPOST /mailboxes?action={action1},...
</pre></div></div>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>Response codes:</p>
<ul>
<li>201: Success. Corresponding task id is returned.</li>
<li>400: Error in the request. Details can be found in the reported error.</li>
</ul>
<p>The kind of task scheduled depends on the action parameter. See below for details.</p><section>
<h4><a name="Fixing_mailboxes_inconsistencies"></a>Fixing mailboxes inconsistencies</h4>
<p>This task is only available on top of Guice Cassandra products.</p>
<div class="source">
<div class="source">
<pre>curl -XPOST /mailboxes?task=SolveInconsistencies
</pre></div></div>
<p>Will schedule a task for fixing inconsistencies for the mailbox deduplicated object stored in Cassandra.</p>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>The I-KNOW-WHAT-I-M-DOING header is mandatory (you can read more information about it in the warning section below).</p>
<p>The scheduled task will have the following type solve-mailbox-inconsistencies and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{
&quot;type&quot;:&quot;solve-mailbox-inconsistencies&quot;,
&quot;processedMailboxEntries&quot;: 3,
&quot;processedMailboxPathEntries&quot;: 3,
&quot;fixedInconsistencies&quot;: 2,
&quot;errors&quot;: 1,
&quot;conflictingEntries&quot;:[{
&quot;mailboxDaoEntry&quot;:{
&quot;mailboxPath&quot;:&quot;#private:user:mailboxName&quot;,
&quot;mailboxId&quot;:&quot;464765a0-e4e7-11e4-aba4-710c1de3782b&quot;
},&quot; +
&quot;mailboxPathDaoEntry&quot;:{
&quot;mailboxPath&quot;:&quot;#private:user:mailboxName2&quot;,
&quot;mailboxId&quot;:&quot;464765a0-e4e7-11e4-aba4-710c1de3782b&quot;
}
}]
}
</pre></div></div>
<p>Note that conflicting entry inconsistencies will not be fixed and will require to explicitly use <a href="#correcting-ghost-mailbox">ghost mailbox</a> endpoint in order to merge the conflicting mailboxes and prevent any message loss.</p>
<p><b>WARNING</b>: this task can cancel concurrently running legitimate user operations upon dirty read. As such this task should be run offline.</p>
<p>A dirty read is when data is read between the two writes of the denormalization operations (no isolation).</p>
<p>In order to ensure being offline, stop the traffic on SMTP, JMAP and IMAP ports, for example via re-configuration or firewall rules.</p>
<p>Due to all of those risks, a I-KNOW-WHAT-I-M-DOING header should be positioned to ALL-SERVICES-ARE-OFFLINE in order to prevent accidental calls.</p></section><section>
<h4><a name="Recomputing_mailbox_counters"></a>Recomputing mailbox counters</h4>
<p>This task is only available on top of Guice Cassandra products.</p>
<div class="source">
<div class="source">
<pre>curl -XPOST /mailboxes?task=RecomputeMailboxCounters
</pre></div></div>
<p>Will recompute counters (unseen &amp; total count) for the mailbox object stored in Cassandra.</p>
<p>Cassandra maintains a per mailbox projection for message count and unseen message count. As with any projection, it can go out of sync, leading to inconsistent results being returned to the client.</p>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>The scheduled task will have the following type recompute-mailbox-counters and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{
&quot;type&quot;:&quot;recompute-mailbox-counters&quot;,
&quot;processedMailboxes&quot;: 3,
&quot;failedMailboxes&quot;: [&quot;464765a0-e4e7-11e4-aba4-710c1de3782b&quot;]
}
</pre></div></div>
<p>Note that conflicting inconsistencies entries will not be fixed and will require to explicitly use <a href="#correcting-ghost-mailbox">ghost mailbox</a> endpoint in order to merge the conflicting mailboxes and prevent any message loss.</p>
<p><b>WARNING</b>: this task do not take into account concurrent modifications upon a single mailbox counter recomputation. Rerunning the task will <i>eventually</i> provide the consistent result. As such we advise to run this task offline.</p>
<p>In order to ensure being offline, stop the traffic on SMTP, JMAP and IMAP ports, for example via re-configuration or firewall rules.</p>
<p>trustMessageProjection query parameter can be set to true. Content of messageIdTable (listing messages by their mailbox context) table will be trusted and not compared against content of imapUidTable table (listing messages by their messageId mailbox independent identifier). This will result in a better performance running the task at the cost of safety in the face of message denormalization inconsistencies.</p>
<p>Defaults to false, which generates additional checks. You can read <a class="externalLink" href="https://github.com/apache/james-project/blob/master/src/adr/0022-cassandra-message-inconsistency.md">this ADR</a> to better understand the message projection and how it can become inconsistent.</p></section><section>
<h4><a name="Recomputing_Global_JMAP_fast_message_view_projection"></a>Recomputing Global JMAP fast message view projection</h4>
<p>This action is only available for backends supporting JMAP protocol.</p>
<p>Message fast view projection stores message properties expected to be fast to fetch but are actually expensive to compute, in order for GetMessages operation to be fast to execute for these properties.</p>
<p>These projection items are asynchronously computed on mailbox events.</p>
<p>You can force the full projection recomputation by calling the following endpoint:</p>
<div class="source">
<div class="source">
<pre>curl -XPOST /mailboxes?task=recomputeFastViewProjectionItems
</pre></div></div>
<p>Will schedule a task for recomputing the fast message view projection for all mailboxes.</p>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>An admin can specify the concurrency that should be used when running the task:</p>
<ul>
<li>messagesPerSecond rate at which messages should be processed, per second. Defaults to 10.</li>
</ul>
<p>This optional parameter must have a strictly positive integer as a value and be passed as query parameters.</p>
<p>Example:</p>
<div class="source">
<div class="source">
<pre>curl -XPOST /mailboxes?task=recomputeFastViewProjectionItems&amp;messagesPerSecond=20
</pre></div></div>
<p>The scheduled task will have the following type RecomputeAllFastViewProjectionItemsTask and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{
&quot;type&quot;:&quot;RecomputeAllPreviewsTask&quot;,
&quot;processedUserCount&quot;: 3,
&quot;processedMessageCount&quot;: 3,
&quot;failedUserCount&quot;: 2,
&quot;failedMessageCount&quot;: 1,
&quot;runningOptions&quot;: {
&quot;messagesPerSecond&quot;:20
}
}
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>201: Success. Corresponding task id is returned.</li>
<li>400: Error in the request. Details can be found in the reported error.</li>
</ul></section><section>
<h4><a name="ReIndexing_action"></a>ReIndexing action</h4>
<p>These tasks are only available on top of Guice Cassandra products or Guice JPA products. They are not part of Memory Guice product.</p>
<p>Be also aware of the limits of this API:</p>
<p>Warning: During the re-indexing, the result of search operations might be altered.</p>
<p>Warning: Canceling this task should be considered unsafe as it will leave the currently reIndexed mailbox as partially indexed.</p>
<p>Warning: While we have been trying to reduce the inconsistency window to a maximum (by keeping track of ongoing events), concurrent changes done during the reIndexing might be ignored.</p>
<p>The following actions can be performed:</p>
<ul>
<li><a href="#ReIndexing_all_mails">ReIndexing all mails</a></li>
<li><a href="#Fixing_previously_failed_ReIndexing">Fixing previously failed ReIndexing</a></li>
</ul><section>
<h5><a name="ReIndexing_all_mails"></a>ReIndexing all mails</h5>
<div class="source">
<div class="source">
<pre>curl -XPOST http://ip:port/mailboxes?task=reIndex
</pre></div></div>
<p>Will schedule a task for reIndexing all the mails stored on this James server.</p>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>An admin can specify the concurrency that should be used when running the task:</p>
<ul>
<li>messagesPerSecond rate at which messages should be processed per second. Default is 50.</li>
</ul>
<p>This optional parameter must have a strictly positive integer as a value and be passed as query parameter.</p>
<p>An admin can also specify the reindexing mode it wants to use when running the task:</p>
<ul>
<li>mode the reindexing mode used. There are 2 modes for the moment:</li>
<li>rebuildAll allows to rebuild all indexes. It drops indexed entries prior reindexing. This is the default mode.</li>
<li>rebuildAllNoCleanup allows to rebuild all indexes. It skips the cleanup phase thus will not remove evicted entries upon reindex. However it yields better performances on a known to be empty index.</li>
<li>fixOutdated will check for outdated indexed document and reindex only those.</li>
</ul>
<p>This optional parameter must be passed as query parameter.</p>
<p>It&#x2019;s good to note as well that there is a limitation with the fixOutdated mode. As we first collect metadata of stored messages to compare them with the ones in the index, a failed expunged operation might not be well corrected (as the message might not exist anymore but still be indexed).</p>
<p>Example:</p>
<p>curl -XPOST http://ip:port/mailboxes?task=reIndex&amp;messagesPerSecond=200&amp;mode=rebuildAll</p>
<p>The scheduled task will have the following type full-reindexing and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{
&quot;type&quot;:&quot;full-reindexing&quot;,
&quot;runningOptions&quot;:{
&quot;messagesPerSecond&quot;:200,
&quot;mode&quot;:&quot;REBUILD_ALL&quot;
},
&quot;successfullyReprocessedMailCount&quot;:18,
&quot;failedReprocessedMailCount&quot;: 3,
&quot;mailboxFailures&quot;: [&quot;12&quot;, &quot;23&quot; ],
&quot;messageFailures&quot;: [
{
&quot;mailboxId&quot;: &quot;1&quot;,
&quot;uids&quot;: [1, 36]
}]
}
</pre></div></div>
</section><section>
<h5><a name="Fixing_previously_failed_ReIndexing"></a>Fixing previously failed ReIndexing</h5>
<p>Will schedule a task for reIndexing all the mails which had failed to be indexed from the ReIndexingAllMails task.</p>
<p>Given bbdb69c9-082a-44b0-a85a-6e33e74287a5 being a taskId generated for a reIndexing tasks</p>
<div class="source">
<div class="source">
<pre>curl -XPOST 'http://ip:port/mailboxes?task=reIndex&amp;reIndexFailedMessagesOf=bbdb69c9-082a-44b0-a85a-6e33e74287a5'
</pre></div></div>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>An admin can specify the concurrency that should be used when running the task:</p>
<ul>
<li>messagesPerSecond rate at which messages should be processed per second. Default is 50.</li>
</ul>
<p>This optional parameter must have a strictly positive integer as a value and be passed as query parameter.</p>
<p>An admin can also specify the reindexing mode it wants to use when running the task:</p>
<ul>
<li>mode the reindexing mode used. There are 2 modes for the moment:</li>
<li>rebuildAll allows to rebuild all indexes. It drops indexed entries prior reindexing. This is the default mode.</li>
<li>rebuildAllNoCleanup allows to rebuild all indexes. It skips the cleanup phase thus will not remove evicted entries upon reindex. However it yields better performances on a known to be empty index.</li>
<li>fixOutdated will check for outdated indexed document and reindex only those.</li>
</ul>
<p>This optional parameter must be passed as query parameter.</p>
<p>It&#x2019;s good to note as well that there is a limitation with the fixOutdated mode. As we first collect metadata of stored messages to compare them with the ones in the index, a failed expunged operation might not be well corrected (as the message might not exist anymore but still be indexed).</p>
<p>Example:</p>
<div class="source">
<div class="source">
<pre>curl -XPOST http://ip:port/mailboxes?task=reIndex&amp;reIndexFailedMessagesOf=bbdb69c9-082a-44b0-a85a-6e33e74287a5&amp;messagesPerSecond=200&amp;mode=rebuildAll
</pre></div></div>
<p>The scheduled task will have the following type error-recovery-indexation and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{
&quot;type&quot;:&quot;error-recovery-indexation&quot;
&quot;runningOptions&quot;:{
&quot;messagesPerSecond&quot;:200,
&quot;mode&quot;:&quot;REBUILD_ALL&quot;
},
&quot;successfullyReprocessedMailCount&quot;:18,
&quot;failedReprocessedMailCount&quot;: 3,
&quot;mailboxFailures&quot;: [&quot;12&quot;, &quot;23&quot; ],
&quot;messageFailures&quot;: [{
&quot;mailboxId&quot;: &quot;1&quot;,
&quot;uids&quot;: [1, 36]
}]
}
</pre></div></div>
</section><section>
<h5><a name="Create_missing_parent_mailboxes"></a>Create missing parent mailboxes</h5>
<p>Will schedule a task for creating all the missing parent mailboxes in a hierarchical mailbox tree, which is the result of a partially failed rename operation of a child mailbox.</p>
<div class="source">
<div class="source">
<pre>curl -XPOST 'http://ip:port/mailboxes?task=createMissingParents'
</pre></div></div>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>Response codes:</p>
<ul>
<li>201: Success. Corresponding task id is returned.</li>
<li>400: Error in the request. Details can be found in the reported error.</li>
</ul>
<p>The scheduled task will have the following type createMissingParents and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{
&quot;type&quot;:&quot;createMissingParents&quot;
&quot;created&quot;: [&quot;1&quot;, &quot;2&quot; ],
&quot;totalCreated&quot;: 2,
&quot;failures&quot;: [],
&quot;totalFailure&quot;: 0
}
</pre></div></div>
</section></section></section><section>
<h3><a name="Single_mailbox"></a>Single mailbox</h3><section>
<h4><a name="ReIndexing_a_mailbox_mails"></a>ReIndexing a mailbox mails</h4>
<p>This task is only available on top of Guice Cassandra products or Guice JPA products. It is not part of Memory Guice product.</p>
<div class="source">
<div class="source">
<pre>curl -XPOST http://ip:port/mailboxes/{mailboxId}?task=reIndex
</pre></div></div>
<p>Will schedule a task for reIndexing all the mails in one mailbox.</p>
<p>Note that &#x2018;mailboxId&#x2019; path parameter needs to be a (implementation dependent) valid mailboxId.</p>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>An admin can specify the concurrency that should be used when running the task:</p>
<ul>
<li>messagesPerSecond rate at which messages should be processed per second. Default is 50.</li>
</ul>
<p>This optional parameter must have a strictly positive integer as a value and be passed as query parameter.</p>
<p>An admin can also specify the reindexing mode it wants to use when running the task:</p>
<ul>
<li>mode the reindexing mode used. There are 2 modes for the moment:</li>
<li>rebuildAll allows to rebuild all indexes. It drops indexed entries prior reindexing. This is the default mode.</li>
<li>rebuildAllNoCleanup allows to rebuild all indexes. It skips the cleanup phase thus will not remove evicted entries upon reindex. However it yields better performances on a known to be empty index.</li>
<li>fixOutdated will check for outdated indexed document and reindex only those.</li>
</ul>
<p>This optional parameter must be passed as query parameter.</p>
<p>It&#x2019;s good to note as well that there is a limitation with the fixOutdated mode. As we first collect metadata of stored messages to compare them with the ones in the index, a failed expunged operation might not be well corrected (as the message might not exist anymore but still be indexed).</p>
<p>Example:</p>
<div class="source">
<div class="source">
<pre>curl -XPOST http://ip:port/mailboxes/{mailboxId}?task=reIndex&amp;messagesPerSecond=200&amp;mode=fixOutdated
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>201: Success. Corresponding task id is returned.</li>
<li>400: Error in the request. Details can be found in the reported error.</li>
</ul>
<p>The scheduled task will have the following type mailbox-reindexing and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{
&quot;type&quot;:&quot;mailbox-reindexing&quot;,
&quot;runningOptions&quot;:{
&quot;messagesPerSecond&quot;:200,
&quot;mode&quot;:&quot;FIX_OUTDATED&quot;
},
&quot;mailboxId&quot;:&quot;{mailboxId}&quot;,
&quot;successfullyReprocessedMailCount&quot;:18,
&quot;failedReprocessedMailCount&quot;: 3,
&quot;mailboxFailures&quot;: [&quot;12&quot;],
&quot;messageFailures&quot;: [
{
&quot;mailboxId&quot;: &quot;1&quot;,
&quot;uids&quot;: [1, 36]
}]
}
</pre></div></div>
<p>Warning: During the re-indexing, the result of search operations might be altered.</p>
<p>Warning: Canceling this task should be considered unsafe as it will leave the currently reIndexed mailbox as partially indexed.</p>
<p>Warning: While we have been trying to reduce the inconsistency window to a maximum (by keeping track of ongoing events), concurrent changes done during the reIndexing might be ignored.</p></section><section>
<h4><a name="ReIndexing_a_single_mail"></a>ReIndexing a single mail</h4>
<p>This task is only available on top of Guice Cassandra products or Guice JPA products. It is not part of Memory Guice product.</p>
<div class="source">
<div class="source">
<pre>curl -XPOST http://ip:port/mailboxes/{mailboxId}/uid/{uid}?task=reIndex
</pre></div></div>
<p>Will schedule a task for reIndexing a single email.</p>
<p>Note that &#x2018;mailboxId&#x2019; path parameter needs to be a (implementation dependent) valid mailboxId.</p>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>Response codes:</p>
<ul>
<li>201: Success. Corresponding task id is returned.</li>
<li>400: Error in the request. Details can be found in the reported error.</li>
</ul>
<p>The scheduled task will have the following type message-reindexing and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{
&quot;mailboxId&quot;:&quot;{mailboxId}&quot;,
&quot;uid&quot;:18
}
</pre></div></div>
<p>Warning: During the re-indexing, the result of search operations might be altered.</p>
<p>Warning: Canceling this task should be considered unsafe as it will leave the currently reIndexed mailbox as partially indexed.</p></section></section></section><section>
<h2><a name="Administrating_Messages"></a>Administrating Messages</h2><section>
<h3><a name="ReIndexing_a_single_mail_by_messageId"></a>ReIndexing a single mail by messageId</h3>
<p>This task is only available on top of Guice Cassandra products or Guice JPA products. It is not part of Memory Guice product.</p>
<div class="source">
<div class="source">
<pre>curl -XPOST http://ip:port/messages/{messageId}?task=reIndex
</pre></div></div>
<p>Will schedule a task for reIndexing a single email in all the mailboxes containing it.</p>
<p>Note that &#x2018;messageId&#x2019; path parameter needs to be a (implementation dependent) valid messageId.</p>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>Response codes:</p>
<ul>
<li>201: Success. Corresponding task id is returned.</li>
<li>400: Error in the request. Details can be found in the reported error.</li>
</ul>
<p>The scheduled task will have the following type messageId-reindexing and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{
&quot;messageId&quot;:&quot;18&quot;
}
</pre></div></div>
<p>Warning: During the re-indexing, the result of search operations might be altered.</p></section><section>
<h3><a name="Fixing_message_inconsistencies"></a>Fixing message inconsistencies</h3>
<p>This task is only available on top of Guice Cassandra products.</p>
<div class="source">
<div class="source">
<pre>curl -XPOST /messages?task=SolveInconsistencies
</pre></div></div>
<p>Will schedule a task for fixing message inconsistencies created by the message denormalization process.</p>
<p>Messages are denormalized and stored in separated data tables in Cassandra, so they can be accessed by their unique identifier or mailbox identifier &amp; local mailbox identifier through different protocols.</p>
<p>Failure in the denormalization process will lead to inconsistencies, for example:</p>
<div class="source">
<div class="source">
<pre>BOB receives a message
The denormalization process fails
BOB can read the message via JMAP
BOB cannot read the message via IMAP
BOB marks a message as SEEN
The denormalization process fails
The message is SEEN via JMAP
The message is UNSEEN via IMAP
</pre></div></div>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>An admin can specify the concurrency that should be used when running the task:</p>
<ul>
<li>messagesPerSecond rate of messages to be processed per second. Default is 100.</li>
</ul>
<p>This optional parameter must have a strictly positive integer as a value and be passed as query parameter.</p>
<p>An admin can also specify the reindexing mode it wants to use when running the task:</p>
<ul>
<li>mode the reindexing mode used. There are 2 modes for the moment:</li>
<li>rebuildAll allows to rebuild all indexes. It drops indexed entries prior reindexing. This is the default mode.</li>
<li>rebuildAllNoCleanup allows to rebuild all indexes. It skips the cleanup phase thus will not remove evicted entries upon reindex. However it yields better performances on a known to be empty index.</li>
<li>fixOutdated will check for outdated indexed document and reindex only those.</li>
</ul>
<p>This optional parameter must be passed as query parameter.</p>
<p>It&#x2019;s good to note as well that there is a limitation with the fixOutdated mode. As we first collect metadata of stored messages to compare them with the ones in the index, a failed expunged operation might not be well corrected (as the message might not exist anymore but still be indexed).</p>
<p>Example:</p>
<div class="source">
<div class="source">
<pre>curl -XPOST /messages?task=SolveInconsistencies&amp;messagesPerSecond=200&amp;mode=rebuildAll
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>201: Success. Corresponding task id is returned.</li>
<li>400: Error in the request. Details can be found in the reported error.</li>
</ul>
<p>The scheduled task will have the following type solve-message-inconsistencies and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{
&quot;type&quot;:&quot;solve-message-inconsistencies&quot;,
&quot;timestamp&quot;:&quot;2007-12-03T10:15:30Z&quot;,
&quot;processedImapUidEntries&quot;: 2,
&quot;processedMessageIdEntries&quot;: 1,
&quot;addedMessageIdEntries&quot;: 1,
&quot;updatedMessageIdEntries&quot;: 0,
&quot;removedMessageIdEntries&quot;: 1,
&quot;runningOptions&quot;:{
&quot;messagesPerSecond&quot;: 200,
&quot;mode&quot;:&quot;REBUILD_ALL&quot;
},
&quot;fixedInconsistencies&quot;: [
{
&quot;mailboxId&quot;: &quot;551f0580-82fb-11ea-970e-f9c83d4cf8c2&quot;,
&quot;messageId&quot;: &quot;d2bee791-7e63-11ea-883c-95b84008f979&quot;,
&quot;uid&quot;: 1
},
{
&quot;mailboxId&quot;: &quot;551f0580-82fb-11ea-970e-f9c83d4cf8c2&quot;,
&quot;messageId&quot;: &quot;d2bee792-7e63-11ea-883c-95b84008f979&quot;,
&quot;uid&quot;: 2
}
],
&quot;errors&quot;: [
{
&quot;mailboxId&quot;: &quot;551f0580-82fb-11ea-970e-f9c83d4cf8c2&quot;,
&quot;messageId&quot;: &quot;ffffffff-7e63-11ea-883c-95b84008f979&quot;,
&quot;uid&quot;: 3
}
]
}
</pre></div></div>
<p>User actions concurrent to the inconsistency fixing task could result in concurrency issues. New inconsistencies could be created.</p>
<p>However the source of truth will not be impacted, hence rerunning the task will eventually fix all issues.</p>
<p>This task could be run safely online and can be scheduled on a recurring basis outside of peak traffic by an admin to ensure Cassandra message consistency.</p></section></section><section>
<h2><a name="Administrating_user_mailboxes"></a>Administrating user mailboxes</h2>
<ul>
<li><a href="#Creating_a_mailbox">Creating a mailbox</a></li>
<li><a href="#Deleting_a_mailbox_and_its_children">Deleting a mailbox and its children</a></li>
<li><a href="#Testing_existence_of_a_mailbox">Testing existence of a mailbox</a></li>
<li><a href="#Listing_user_mailboxes">Listing user mailboxes</a></li>
<li><a href="#Deleting_user_mailboxes">Deleting user mailboxes</a></li>
<li><a href="#Exporting_user_mailboxes">Exporting user mailboxes</a></li>
<li><a href="#ReIndexing_a_user_mails">ReIndexing a user mails</a></li>
<li><a href="#Recomputing_User_JMAP_fast_message_view_projection">Recomputing User JMAP fast message view projection</a></li>
<li><a href="#Counting_emails">Counting emails</a></li>
<li><a href="#Couting_unseen_emails">Counting unseen emails</a></li>
<li>[Clearing mailbox content][#Clearing_mailbox_content]</li>
</ul><section>
<h3><a name="Creating_a_mailbox"></a>Creating a mailbox</h3>
<div class="source">
<div class="source">
<pre>curl -XPUT http://ip:port/users/{usernameToBeUsed}/mailboxes/{mailboxNameToBeCreated}
</pre></div></div>
<p>Resource name usernameToBeUsed should be an existing user Resource name mailboxNameToBeCreated should not be empty, nor contain % * characters, nor starting with #.</p>
<p>Response codes:</p>
<ul>
<li>204: The mailbox now exists on the server</li>
<li>400: Invalid mailbox name</li>
<li>404: The user name does not exist</li>
</ul>
<p>To create nested mailboxes, for instance a work mailbox inside the INBOX mailbox, people should use the . separator. The sample query is:</p>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/users/{usernameToBeUsed}/mailboxes/INBOX.work
</pre></div></div>
</section><section>
<h3><a name="Deleting_a_mailbox_and_its_children"></a>Deleting a mailbox and its children</h3>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/users/{usernameToBeUsed}/mailboxes/{mailboxNameToBeDeleted}
</pre></div></div>
<p>Resource name usernameToBeUsed should be an existing user Resource name mailboxNameToBeDeleted should not be empty</p>
<p>Response codes:</p>
<ul>
<li>204: The mailbox now does not exist on the server</li>
<li>400: Invalid mailbox name</li>
<li>404: The user name does not exist</li>
</ul></section><section>
<h3><a name="Testing_existence_of_a_mailbox"></a>Testing existence of a mailbox</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/users/{usernameToBeUsed}/mailboxes/{mailboxNameToBeTested}
</pre></div></div>
<p>Resource name usernameToBeUsed should be an existing user Resource name mailboxNameToBeTested should not be empty</p>
<p>Response codes:</p>
<ul>
<li>204: The mailbox exists</li>
<li>400: Invalid mailbox name</li>
<li>404: The user name does not exist, the mailbox does not exist</li>
</ul></section><section>
<h3><a name="Listing_user_mailboxes"></a>Listing user mailboxes</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/users/{usernameToBeUsed}/mailboxes
</pre></div></div>
<p>The answer looks like:</p>
<div class="source">
<div class="source">
<pre>[{&quot;mailboxName&quot;:&quot;INBOX&quot;},{&quot;mailboxName&quot;:&quot;outbox&quot;}]
</pre></div></div>
<p>Resource name usernameToBeUsed should be an existing user</p>
<p>Response codes:</p>
<ul>
<li>200: The mailboxes list was successfully retrieved</li>
<li>404: The user name does not exist</li>
</ul></section><section>
<h3><a name="Deleting_user_mailboxes"></a>Deleting user mailboxes</h3>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/users/{usernameToBeUsed}/mailboxes
</pre></div></div>
<p>Resource name usernameToBeUsed should be an existing user</p>
<p>Response codes:</p>
<ul>
<li>204: The user do not have mailboxes anymore</li>
<li>404: The user name does not exist</li>
</ul></section><section>
<h3><a name="Exporting_user_mailboxes"></a>Exporting user mailboxes</h3>
<div class="source">
<div class="source">
<pre>curl -XPOST http://ip:port/users/{usernameToBeUsed}/mailboxes?action=export
</pre></div></div>
<p>Resource name usernameToBeUsed should be an existing user</p>
<p>Response codes:</p>
<ul>
<li>201: Success. Corresponding task id is returned</li>
<li>404: The user name does not exist</li>
</ul>
<p>The scheduled task will have the following type MailboxesExportTask and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{
&quot;type&quot;:&quot;MailboxesExportTask&quot;,
&quot;timestamp&quot;:&quot;2007-12-03T10:15:30Z&quot;,
&quot;username&quot;: &quot;user&quot;,
&quot;stage&quot;: &quot;STARTING&quot;
}
</pre></div></div>
</section><section>
<h3><a name="ReIndexing_a_user_mails"></a>ReIndexing a user mails</h3>
<div class="source">
<div class="source">
<pre>curl -XPOST http://ip:port/users/{usernameToBeUsed}/mailboxes?task=reIndex
</pre></div></div>
<p>Will schedule a task for reIndexing all the mails in &#x201c;<a class="externalLink" href="mailto:user@domain.com">user@domain.com</a>&#x201d; mailboxes (encoded above).</p>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>An admin can specify the concurrency that should be used when running the task:</p>
<ul>
<li>messagesPerSecond rate at which messages should be processed per second. Default is 50.</li>
</ul>
<p>This optional parameter must have a strictly positive integer as a value and be passed as query parameter.</p>
<p>An admin can also specify the reindexing mode it wants to use when running the task:</p>
<ul>
<li>mode the reindexing mode used. There are 2 modes for the moment:</li>
<li>rebuildAll allows to rebuild all indexes. It drops indexed entries prior reindexing. This is the default mode.</li>
<li>rebuildAllNoCleanup allows to rebuild all indexes. It skips the cleanup phase thus will not remove evicted entries upon reindex. However it yields better performances on a known to be empty index.</li>
<li>fixOutdated will check for outdated indexed document and reindex only those.</li>
</ul>
<p>This optional parameter must be passed as query parameter.</p>
<p>It&#x2019;s good to note as well that there is a limitation with the fixOutdated mode. As we first collect metadata of stored messages to compare them with the ones in the index, a failed expunged operation might not be well corrected (as the message might not exist anymore but still be indexed).</p>
<p>Example:</p>
<div class="source">
<div class="source">
<pre>curl -XPOST http://ip:port/users/{usernameToBeUsed}/mailboxes?task=reIndex&amp;messagesPerSecond=200&amp;mode=fixOutdated
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>201: Success. Corresponding task id is returned.</li>
<li>400: Error in the request. Details can be found in the reported error.</li>
</ul>
<p>The scheduled task will have the following type user-reindexing and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{
&quot;type&quot;:&quot;user-reindexing&quot;,
&quot;runningOptions&quot;:{
&quot;messagesPerSecond&quot;:200,
&quot;mode&quot;:&quot;FIX_OUTDATED&quot;
},
&quot;user&quot;:&quot;user@domain.com&quot;,
&quot;successfullyReprocessedMailCount&quot;:18,
&quot;failedReprocessedMailCount&quot;: 3,
&quot;mailboxFailures&quot;: [&quot;12&quot;, &quot;23&quot; ],
&quot;messageFailures&quot;: [
{
&quot;mailboxId&quot;: &quot;1&quot;,
&quot;uids&quot;: [1, 36]
}]
}
</pre></div></div>
<p>Warning: During the re-indexing, the result of search operations might be altered.</p>
<p>Warning: Canceling this task should be considered unsafe as it will leave the currently reIndexed mailbox as partially indexed.</p>
<p>Warning: While we have been trying to reduce the inconsistency window to a maximum (by keeping track of ongoing events), concurrent changes done during the reIndexing might be ignored.</p></section><section>
<h3><a name="Counting_emails"></a>Counting emails</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/users/{usernameToBeUsed}/mailboxes/{mailboxName}/messageCount
</pre></div></div>
<p>Will return the total count of messages within the mailbox of that user.</p>
<p>Resource name usernameToBeUsed should be an existing user.<br />
Resource name mailboxName should not be empty, nor contain % * characters, nor starting with #.</p>
<p>Response codes:</p>
<ul>
<li>200: The number of emails in a given mailbox</li>
<li>400: Invalid mailbox name</li>
<li>404: Invalid get on user mailboxes. The usernameToBeUsed or mailboxName does not exit&#x2019;</li>
</ul></section><section>
<h3><a name="Counting_unseen_emails"></a>Counting unseen emails</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/users/{usernameToBeUsed}/mailboxes/{mailboxName}/unseenMessageCount
</pre></div></div>
<p>Will return the total count of unseen messages within the mailbox of that user.</p>
<p>Resource name usernameToBeUsed should be an existing user.<br />
Resource name mailboxName should not be empty, nor contain % * characters, nor starting with #.</p>
<p>Response codes:</p>
<ul>
<li>200: The number of unseen emails in a given mailbox</li>
<li>400: Invalid mailbox name</li>
<li>404: Invalid get on user mailboxes. The usernameToBeUsed or mailboxName does not exit&#x2019;</li>
</ul></section><section>
<h3><a name="Clearing_mailbox_content"></a>Clearing mailbox content</h3>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/users/{usernameToBeUsed}/mailboxes/{mailboxName}/messages
</pre></div></div>
<p>Will schedule a task for clearing all the mails in mailboxName mailbox of usernameToBeUsed.</p>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>Resource name usernameToBeUsed should be an existing user.</p>
<p>Resource name mailboxName should not be empty, nor contain % * characters, nor starting with #.</p>
<p>Response codes:</p>
<ul>
<li>201: Success. Corresponding task id is returned.</li>
<li>400: Invalid mailbox name</li>
<li>404: Invalid get on user mailboxes. The username or mailboxName does not exit</li>
</ul>
<p>The scheduled task will have the following type ClearMailboxContentTask and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{
&quot;mailboxName&quot;: &quot;mbx1&quot;,
&quot;messagesFailCount&quot;: 9,
&quot;messagesSuccessCount&quot;: 10,
&quot;timestamp&quot;: &quot;2007-12-03T10:15:30Z&quot;,
&quot;type&quot;: &quot;ClearMailboxContentTask&quot;,
&quot;username&quot;: &quot;bob@domain.tld&quot;
}
</pre></div></div>
</section><section>
<h3><a name="Subscribing_a_user_to_all_of_its_mailboxes"></a>Subscribing a user to all of its mailboxes</h3>
<div class="source">
<div class="source">
<pre>curl -XPOST http://ip:port/users/{usernameToBeUsed}/mailboxes?task=subscribeAll
</pre></div></div>
<p>Will schedule a task for subscribing a user to all of its mailboxes.</p>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>Most users are unaware of what an IMAP subscription is, nor how they can manage it. If the subscription list gets out of sync with the mailbox list, it could result in downgraded user experience (see MAILBOX-405). This task allow to reset the subscription list to the mailbox list on a per user basis thus working around the aforementioned issues.</p>
<p>Response codes:</p>
<ul>
<li>201: Success. Corresponding task id is returned.</li>
<li>404: No such user</li>
</ul>
<p>The scheduled task will have the following type SubscribeAllTask and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{
&quot;type&quot;:&quot;SubscribeAllTask&quot;,
&quot;username&quot;:&quot;user@domain.com&quot;,
&quot;subscribedCount&quot;:18,
&quot;unsubscribedCount&quot;: 3
}
</pre></div></div>
</section><section>
<h3><a name="Recomputing_User_JMAP_fast_message_view_projection"></a>Recomputing User JMAP fast message view projection</h3>
<p>This action is only available for backends supporting JMAP protocol.</p>
<p>Message fast view projection stores message properties expected to be fast to fetch but are actually expensive to compute, in order for GetMessages operation to be fast to execute for these properties.</p>
<p>These projection items are asynchronously computed on mailbox events.</p>
<p>You can force the full projection recomputation by calling the following endpoint:</p>
<div class="source">
<div class="source">
<pre>curl -XPOST /users/{usernameToBeUsed}/mailboxes?task=recomputeFastViewProjectionItems
</pre></div></div>
<p>Will schedule a task for recomputing the fast message view projection for all mailboxes of usernameToBeUsed.</p>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>An admin can specify the concurrency that should be used when running the task:</p>
<ul>
<li>messagesPerSecond rate at which messages should be processed, per second. Defaults to 10.</li>
</ul>
<p>This optional parameter must have a strictly positive integer as a value and be passed as query parameters.</p>
<p>Example:</p>
<div class="source">
<div class="source">
<pre>curl -XPOST /mailboxes?task=recomputeFastViewProjectionItems&amp;messagesPerSecond=20
</pre></div></div>
<p>The scheduled task will have the following type RecomputeUserFastViewProjectionItemsTask and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{
&quot;type&quot;:&quot;RecomputeUserFastViewProjectionItemsTask&quot;,
&quot;username&quot;: &quot;{usernameToBeUsed}&quot;,
&quot;processedMessageCount&quot;: 3,
&quot;failedMessageCount&quot;: 1,
&quot;runningOptions&quot;: {
&quot;messagesPerSecond&quot;:20
}
}
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>201: Success. Corresponding task id is returned.</li>
<li>400: Error in the request. Details can be found in the reported error.</li>
<li>404: User not found.</li>
</ul></section></section><section>
<h2><a name="Administrating_quotas_by_users"></a>Administrating quotas by users</h2>
<ul>
<li><a href="#Getting_the_quota_for_a_user">Getting the quota for a user</a></li>
<li><a href="#Updating_the_quota_for_a_user">Updating the quota for a user</a></li>
<li><a href="#Getting_the_quota_count_for_a_user">Getting the quota count for a user</a></li>
<li><a href="#Updating_the_quota_count_for_a_user">Updating the quota count for a user</a></li>
<li><a href="#Deleting_the_quota_count_for_a_user">Deleting the quota count for a user</a></li>
<li><a href="#Getting_the_quota_size_for_a_user">Getting the quota size for a user</a></li>
<li><a href="#Updating_the_quota_size_for_a_user">Updating the quota size for a user</a></li>
<li><a href="#Deleting_the_quota_size_for_a_user">Deleting the quota size for a user</a></li>
<li><a href="#Searching_user_by_quota_ratio">Searching user by quota ratio</a></li>
<li><a href="#Recomputing_current_quotas_for_users">Recomputing current quotas for users</a></li>
</ul><section>
<h3><a name="Getting_the_quota_for_a_user"></a>Getting the quota for a user</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/quota/users/{usernameToBeUsed}
</pre></div></div>
<p>Resource name usernameToBeUsed should be an existing user</p>
<p>The answer is the details of the quota of that user.</p>
<div class="source">
<div class="source">
<pre>{
&quot;global&quot;: {
&quot;count&quot;:252,
&quot;size&quot;:242
},
&quot;domain&quot;: {
&quot;count&quot;:152,
&quot;size&quot;:142
},
&quot;user&quot;: {
&quot;count&quot;:52,
&quot;size&quot;:42
},
&quot;computed&quot;: {
&quot;count&quot;:52,
&quot;size&quot;:42
},
&quot;occupation&quot;: {
&quot;size&quot;:13,
&quot;count&quot;:21,
&quot;ratio&quot;: {
&quot;size&quot;:0.25,
&quot;count&quot;:0.5,
&quot;max&quot;:0.5
}
}
}
</pre></div></div>
<ul>
<li>The global entry represent the quota limit allowed on this James server.</li>
<li>The domain entry represent the quota limit allowed for the user of that domain.</li>
<li>The user entry represent the quota limit allowed for this specific user.</li>
<li>The computed entry represent the quota limit applied for this user, resolved from the upper values.</li>
<li>The occupation entry represent the occupation of the quota for this user. This includes used count and size as well as occupation ratio (used / limit).</li>
</ul>
<p>Note that quota object can contain a fixed value, an empty value (null) or an unlimited value (-1):</p>
<div class="source">
<div class="source">
<pre>{&quot;count&quot;:52,&quot;size&quot;:42}
{&quot;count&quot;:null,&quot;size&quot;:null}
{&quot;count&quot;:52,&quot;size&quot;:-1}
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: The user&#x2019;s quota was successfully retrieved</li>
<li>404: The user does not exist</li>
</ul></section><section>
<h3><a name="Updating_the_quota_for_a_user"></a>Updating the quota for a user</h3>
<div class="source">
<div class="source">
<pre>curl -XPUT http://ip:port/quota/users/{usernameToBeUsed}
</pre></div></div>
<p>Resource name usernameToBeUsed should be an existing user</p>
<p>The body can contain a fixed value, an empty value (null) or an unlimited value (-1):</p>
<div class="source">
<div class="source">
<pre>{&quot;count&quot;:52,&quot;size&quot;:42}
{&quot;count&quot;:null,&quot;size&quot;:null}
{&quot;count&quot;:52,&quot;size&quot;:-1}
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>204: The quota has been updated</li>
<li>400: The body is not a positive integer neither an unlimited value (-1).</li>
<li>404: The user does not exist</li>
</ul></section><section>
<h3><a name="Getting_the_quota_count_for_a_user"></a>Getting the quota count for a user</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/quota/users/{usernameToBeUsed}/count
</pre></div></div>
<p>Resource name usernameToBeUsed should be an existing user</p>
<p>The answer looks like:</p>
<div class="source">
<div class="source">
<pre>52
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: The user&#x2019;s quota was successfully retrieved</li>
<li>204: No quota count limit is defined at the user level for this user</li>
<li>404: The user does not exist</li>
</ul></section><section>
<h3><a name="Updating_the_quota_count_for_a_user"></a>Updating the quota count for a user</h3>
<div class="source">
<div class="source">
<pre>curl -XPUT http://ip:port/quota/users/{usernameToBeUsed}/count
</pre></div></div>
<p>Resource name usernameToBeUsed should be an existing user</p>
<p>The body can contain a fixed value or an unlimited value (-1):</p>
<div class="source">
<div class="source">
<pre>52
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>204: The quota has been updated</li>
<li>400: The body is not a positive integer neither an unlimited value (-1).</li>
<li>404: The user does not exist</li>
</ul></section><section>
<h3><a name="Deleting_the_quota_count_for_a_user"></a>Deleting the quota count for a user</h3>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/quota/users/{usernameToBeUsed}/count
</pre></div></div>
<p>Resource name usernameToBeUsed should be an existing user</p>
<p>Response codes:</p>
<ul>
<li>204: The quota has been updated to unlimited value.</li>
<li>404: The user does not exist</li>
</ul></section><section>
<h3><a name="Getting_the_quota_size_for_a_user"></a>Getting the quota size for a user</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/quota/users/{usernameToBeUsed}/size
</pre></div></div>
<p>Resource name usernameToBeUsed should be an existing user</p>
<p>The answer looks like:</p>
<div class="source">
<div class="source">
<pre>52
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: The user&#x2019;s quota was successfully retrieved</li>
<li>204: No quota size limit is defined at the user level for this user</li>
<li>404: The user does not exist</li>
</ul></section><section>
<h3><a name="Updating_the_quota_size_for_a_user"></a>Updating the quota size for a user</h3>
<div class="source">
<div class="source">
<pre>curl -XPUT http://ip:port/quota/users/{usernameToBeUsed}/size
</pre></div></div>
<p>Resource name usernameToBeUsed should be an existing user</p>
<p>The body can contain a fixed value or an unlimited value (-1):</p>
<div class="source">
<div class="source">
<pre>52
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>204: The quota has been updated</li>
<li>400: The body is not a positive integer neither an unlimited value (-1).</li>
<li>404: The user does not exist</li>
</ul></section><section>
<h3><a name="Deleting_the_quota_size_for_a_user"></a>Deleting the quota size for a user</h3>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/quota/users/{usernameToBeUsed}/size
</pre></div></div>
<p>Resource name usernameToBeUsed should be an existing user</p>
<p>Response codes:</p>
<ul>
<li>204: The quota has been updated to unlimited value.</li>
<li>404: The user does not exist</li>
</ul></section><section>
<h3><a name="Searching_user_by_quota_ratio"></a>Searching user by quota ratio</h3>
<div class="source">
<div class="source">
<pre>curl -XGET 'http://ip:port/quota/users?minOccupationRatio=0.8&amp;maxOccupationRatio=0.99&amp;limit=100&amp;offset=200&amp;domain=domain.com'
</pre></div></div>
<p>Will return:</p>
<div class="source">
<div class="source">
<pre>[
{
&quot;username&quot;:&quot;user@domain.com&quot;,
&quot;detail&quot;: {
&quot;global&quot;: {
&quot;count&quot;:252,
&quot;size&quot;:242
},
&quot;domain&quot;: {
&quot;count&quot;:152,
&quot;size&quot;:142
},
&quot;user&quot;: {
&quot;count&quot;:52,
&quot;size&quot;:42
},
&quot;computed&quot;: {
&quot;count&quot;:52,
&quot;size&quot;:42
},
&quot;occupation&quot;: {
&quot;size&quot;:48,
&quot;count&quot;:21,
&quot;ratio&quot;: {
&quot;size&quot;:0.9230,
&quot;count&quot;:0.5,
&quot;max&quot;:0.9230
}
}
}
},
...
]
</pre></div></div>
<p>Where:</p>
<ul>
<li><b>minOccupationRatio</b> is a query parameter determining the minimum occupation ratio of users to be returned.</li>
<li><b>maxOccupationRatio</b> is a query parameter determining the maximum occupation ratio of users to be returned.</li>
<li><b>domain</b> is a query parameter determining the domain of users to be returned.</li>
<li><b>limit</b> is a query parameter determining the maximum number of users to be returned.</li>
<li><b>offset</b> is a query parameter determining the number of users to skip.</li>
</ul>
<p>Please note that users are alphabetically ordered on username.</p>
<p>The response is a list of usernames, with attached quota details as defined <a href="#getting-the-quota-for-a-user">here</a>.</p>
<p>Response codes:</p>
<ul>
<li>200: List of users had successfully been returned.</li>
<li>400: Validation issues with parameters</li>
</ul></section><section>
<h3><a name="Recomputing_current_quotas_for_users"></a>Recomputing current quotas for users</h3>
<p>This task is available on top of Cassandra &amp; JPA products.</p>
<div class="source">
<div class="source">
<pre>curl -XPOST /quota/users?task=RecomputeCurrentQuotas
</pre></div></div>
<p>Will recompute current quotas (count and size) for all users stored in James.</p>
<p>James maintains per quota a projection for current quota count and size. As with any projection, it can go out of sync, leading to inconsistent results being returned to the client.</p>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>An admin can specify the concurrency that should be used when running the task:</p>
<ul>
<li>usersPerSecond rate at which users quotas should be reprocessed, per second. Defaults to 1.</li>
</ul>
<p>This optional parameter must have a strictly positive integer as a value and be passed as query parameters.</p>
<p>Example:</p>
<div class="source">
<div class="source">
<pre>curl -XPOST /quota/users?task=RecomputeCurrentQuotas&amp;usersPerSecond=20
</pre></div></div>
<p>The scheduled task will have the following type recompute-current-quotas and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{
&quot;type&quot;:&quot;recompute-current-quotas&quot;,
&quot;processedQuotaRoots&quot;: 3,
&quot;failedQuotaRoots&quot;: [&quot;#private&amp;bob@localhost&quot;],
&quot;runningOptions&quot;: {
&quot;usersPerSecond&quot;:20
}
}
</pre></div></div>
<p><b>WARNING</b>: this task do not take into account concurrent modifications upon a single current quota recomputation. Rerunning the task will <i>eventually</i> provide the consistent result.</p></section></section><section>
<h2><a name="Administrating_quotas_by_domains"></a>Administrating quotas by domains</h2>
<ul>
<li><a href="#Getting_the_quota_for_a_domain">Getting the quota for a domain</a></li>
<li><a href="#Updating_the_quota_for_a_domain">Updating the quota for a domain</a></li>
<li><a href="#Getting_the_quota_count_for_a_domain">Getting the quota count for a domain</a></li>
<li><a href="#Updating_the_quota_count_for_a_domain">Updating the quota count for a domain</a></li>
<li><a href="#Deleting_the_quota_count_for_a_domain">Deleting the quota count for a domain</a></li>
<li><a href="#Getting_the_quota_size_for_a_domain">Getting the quota size for a domain</a></li>
<li><a href="#Updating_the_quota_size_for_a_domain">Updating the quota size for a domain</a></li>
<li><a href="#Deleting_the_quota_size_for_a_domain">Deleting the quota size for a domain</a></li>
</ul><section>
<h3><a name="Getting_the_quota_for_a_domain"></a>Getting the quota for a domain</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/quota/domains/{domainToBeUsed}
</pre></div></div>
<p>Resource name domainToBeUsed should be an existing domain. For example:</p>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/quota/domains/james.org
</pre></div></div>
<p>The answer will detail the default quota applied to users belonging to that domain:</p>
<div class="source">
<div class="source">
<pre>{
&quot;global&quot;: {
&quot;count&quot;:252,
&quot;size&quot;:null
},
&quot;domain&quot;: {
&quot;count&quot;:null,
&quot;size&quot;:142
},
&quot;computed&quot;: {
&quot;count&quot;:252,
&quot;size&quot;:142
}
}
</pre></div></div>
<ul>
<li>The global entry represents the quota limit defined on this James server by default.</li>
<li>The domain entry represents the quota limit allowed for the user of that domain by default.</li>
<li>The computed entry represents the quota limit applied for the users of that domain, by default, resolved from the upper values.</li>
</ul>
<p>Note that quota object can contain a fixed value, an empty value (null) or an unlimited value (-1):</p>
<div class="source">
<div class="source">
<pre>{&quot;count&quot;:52,&quot;size&quot;:42}
{&quot;count&quot;:null,&quot;size&quot;:null}
{&quot;count&quot;:52,&quot;size&quot;:-1}
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: The domain&#x2019;s quota was successfully retrieved</li>
<li>404: The domain does not exist</li>
<li>405: Domain Quota configuration not supported when virtual hosting is desactivated.</li>
</ul></section><section>
<h3><a name="Updating_the_quota_for_a_domain"></a>Updating the quota for a domain</h3>
<div class="source">
<div class="source">
<pre>curl -XPUT http://ip:port/quota/domains/{domainToBeUsed}
</pre></div></div>
<p>Resource name domainToBeUsed should be an existing domain.</p>
<p>The body can contain a fixed value, an empty value (null) or an unlimited value (-1):</p>
<div class="source">
<div class="source">
<pre>{&quot;count&quot;:52,&quot;size&quot;:42}
{&quot;count&quot;:null,&quot;size&quot;:null}
{&quot;count&quot;:52,&quot;size&quot;:-1}
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>204: The quota has been updated</li>
<li>400: The body is not a positive integer neither an unlimited value (-1).</li>
<li>404: The domain does not exist</li>
<li>405: Domain Quota configuration not supported when virtual hosting is desactivated.</li>
</ul></section><section>
<h3><a name="Getting_the_quota_count_for_a_domain"></a>Getting the quota count for a domain</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/quota/domains/{domainToBeUsed}/count
</pre></div></div>
<p>Resource name domainToBeUsed should be an existing domain.</p>
<p>The answer looks like:</p>
<div class="source">
<div class="source">
<pre>52
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: The domain&#x2019;s quota was successfully retrieved</li>
<li>204: No quota count limit is defined at the domain level for this domain</li>
<li>404: The domain does not exist</li>
<li>405: Domain Quota configuration not supported when virtual hosting is desactivated.</li>
</ul></section><section>
<h3><a name="Updating_the_quota_count_for_a_domain"></a>Updating the quota count for a domain</h3>
<div class="source">
<div class="source">
<pre>curl -XPUT http://ip:port/quota/domains/{domainToBeUsed}/count
</pre></div></div>
<p>Resource name domainToBeUsed should be an existing domain.</p>
<p>The body can contain a fixed value or an unlimited value (-1):</p>
<div class="source">
<div class="source">
<pre>52
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>204: The quota has been updated</li>
<li>400: The body is not a positive integer neither an unlimited value (-1).</li>
<li>404: The domain does not exist</li>
<li>405: Domain Quota configuration not supported when virtual hosting is desactivated.</li>
</ul></section><section>
<h3><a name="Deleting_the_quota_count_for_a_domain"></a>Deleting the quota count for a domain</h3>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/quota/domains/{domainToBeUsed}/count
</pre></div></div>
<p>Resource name domainToBeUsed should be an existing domain.</p>
<p>Response codes:</p>
<ul>
<li>204: The quota has been updated to unlimited value.</li>
<li>404: The domain does not exist</li>
<li>405: Domain Quota configuration not supported when virtual hosting is desactivated.</li>
</ul></section><section>
<h3><a name="Getting_the_quota_size_for_a_domain"></a>Getting the quota size for a domain</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/quota/domains/{domainToBeUsed}/size
</pre></div></div>
<p>Resource name domainToBeUsed should be an existing domain.</p>
<p>The answer looks like:</p>
<div class="source">
<div class="source">
<pre>52
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: The domain&#x2019;s quota was successfully retrieved</li>
<li>204: No quota size limit is defined at the domain level for this domain</li>
<li>404: The domain does not exist</li>
<li>405: Domain Quota configuration not supported when virtual hosting is desactivated.</li>
</ul></section><section>
<h3><a name="Updating_the_quota_size_for_a_domain"></a>Updating the quota size for a domain</h3>
<div class="source">
<div class="source">
<pre>curl -XPUT http://ip:port/quota/domains/{domainToBeUsed}/size
</pre></div></div>
<p>Resource name domainToBeUsed should be an existing domain.</p>
<p>The body can contain a fixed value or an unlimited value (-1):</p>
<div class="source">
<div class="source">
<pre>52
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>204: The quota has been updated</li>
<li>400: The body is not a positive integer neither an unlimited value (-1).</li>
<li>404: The domain does not exist</li>
<li>405: Domain Quota configuration not supported when virtual hosting is desactivated.</li>
</ul></section><section>
<h3><a name="Deleting_the_quota_size_for_a_domain"></a>Deleting the quota size for a domain</h3>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/quota/domains/{domainToBeUsed}/size
</pre></div></div>
<p>Resource name domainToBeUsed should be an existing domain.</p>
<p>Response codes:</p>
<ul>
<li>204: The quota has been updated to unlimited value.</li>
<li>404: The domain does not exist</li>
</ul></section></section><section>
<h2><a name="Administrating_global_quotas"></a>Administrating global quotas</h2>
<ul>
<li><a href="#Getting_the_global_quota">Getting the global quota</a></li>
<li><a href="#Updating_global_quota">Updating global quota</a></li>
<li><a href="#Getting_the_global_quota_count">Getting the global quota count</a></li>
<li><a href="#Updating_the_global_quota_count">Updating the global quota count</a></li>
<li><a href="#Deleting_the_global_quota_count">Deleting the global quota count</a></li>
<li><a href="#Getting_the_global_quota_size">Getting the global quota size</a></li>
<li><a href="#Updating_the_global_quota_size">Updating the global quota size</a></li>
<li><a href="#Deleting_the_global_quota_size">Deleting the global quota size</a></li>
</ul><section>
<h3><a name="Getting_the_global_quota"></a>Getting the global quota</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/quota
</pre></div></div>
<p>The answer is the details of the global quota.</p>
<div class="source">
<div class="source">
<pre>{
&quot;count&quot;:252,
&quot;size&quot;:242
}
</pre></div></div>
<p>Note that quota object can contain a fixed value, an empty value (null) or an unlimited value (-1):</p>
<div class="source">
<div class="source">
<pre>{&quot;count&quot;:52,&quot;size&quot;:42}
{&quot;count&quot;:null,&quot;size&quot;:null}
{&quot;count&quot;:52,&quot;size&quot;:-1}
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: The quota was successfully retrieved</li>
</ul></section><section>
<h3><a name="Updating_global_quota"></a>Updating global quota</h3>
<div class="source">
<div class="source">
<pre>curl -XPUT http://ip:port/quota
</pre></div></div>
<p>The body can contain a fixed value, an empty value (null) or an unlimited value (-1):</p>
<div class="source">
<div class="source">
<pre>{&quot;count&quot;:52,&quot;size&quot;:42}
{&quot;count&quot;:null,&quot;size&quot;:null}
{&quot;count&quot;:52,&quot;size&quot;:-1}
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>204: The quota has been updated</li>
<li>400: The body is not a positive integer neither an unlimited value (-1).</li>
</ul></section><section>
<h3><a name="Getting_the_global_quota_count"></a>Getting the global quota count</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/quota/count
</pre></div></div>
<p>Resource name usernameToBeUsed should be an existing user</p>
<p>The answer looks like:</p>
<div class="source">
<div class="source">
<pre>52
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: The quota was successfully retrieved</li>
<li>204: No quota count limit is defined at the global level</li>
</ul></section><section>
<h3><a name="Updating_the_global_quota_count"></a>Updating the global quota count</h3>
<div class="source">
<div class="source">
<pre>curl -XPUT http://ip:port/quota/count
</pre></div></div>
<p>The body can contain a fixed value or an unlimited value (-1):</p>
<div class="source">
<div class="source">
<pre>52
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>204: The quota has been updated</li>
<li>400: The body is not a positive integer neither an unlimited value (-1).</li>
</ul></section><section>
<h3><a name="Deleting_the_global_quota_count"></a>Deleting the global quota count</h3>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/quota/count
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>204: The quota has been updated to unlimited value.</li>
</ul></section><section>
<h3><a name="Getting_the_global_quota_size"></a>Getting the global quota size</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/quota/size
</pre></div></div>
<p>The answer looks like:</p>
<div class="source">
<div class="source">
<pre>52
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: The quota was successfully retrieved</li>
<li>204: No quota size limit is defined at the global level</li>
</ul></section><section>
<h3><a name="Updating_the_global_quota_size"></a>Updating the global quota size</h3>
<div class="source">
<div class="source">
<pre>curl -XPUT http://ip:port/quota/size
</pre></div></div>
<p>The body can contain a fixed value or an unlimited value (-1):</p>
<div class="source">
<div class="source">
<pre>52
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>204: The quota has been updated</li>
<li>400: The body is not a positive integer neither an unlimited value (-1).</li>
</ul></section><section>
<h3><a name="Deleting_the_global_quota_size"></a>Deleting the global quota size</h3>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/quota/size
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>204: The quota has been updated to unlimited value.</li>
</ul></section></section><section>
<h2><a name="Cassandra_Schema_upgrades"></a>Cassandra Schema upgrades</h2>
<p>Cassandra upgrades implies the creation of a new table. Thus restarting James is needed, as new tables are created on restart.</p>
<p>Once done, we ship code that tries to read from new tables, and if not possible backs up to old tables. You can thus safely run without running additional migrations.</p>
<p>On the fly migration can be enabled. However, one might want to force the migration in a controlled fashion, and update automatically current schema version used (assess in the database old versions is no more used, as the corresponding tables are empty). Note that this process is safe: we ensure the service is not running concurrently on this James instance, that it does not bump version upon partial failures, that race condition in version upgrades will be idempotent, etc&#x2026;</p>
<p>These schema updates can be triggered by webadmin using the Cassandra backend.</p>
<p>Note that currently the progress can be tracked by logs.</p>
<ul>
<li><a href="#Retrieving_current_Cassandra_schema_version">Retrieving current Cassandra schema version</a></li>
<li><a href="#Retrieving_latest_available_Cassandra_schema_version">Retrieving latest available Cassandra schema version</a></li>
<li><a href="#Upgrading_to_a_specific_version">Upgrading to a specific version</a></li>
<li><a href="#Upgrading_to_the_latest_version">Upgrading to the latest version</a></li>
</ul><section>
<h3><a name="Retrieving_current_Cassandra_schema_version"></a>Retrieving current Cassandra schema version</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/cassandra/version
</pre></div></div>
<p>Will return:</p>
<div class="source">
<div class="source">
<pre>{&quot;version&quot;: 2}
</pre></div></div>
<p>Where the number corresponds to the current schema version of the database you are using.</p>
<p>Response codes:</p>
<ul>
<li>200: Success</li>
</ul></section><section>
<h3><a name="Retrieving_latest_available_Cassandra_schema_version"></a>Retrieving latest available Cassandra schema version</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/cassandra/version/latest
</pre></div></div>
<p>Will return:</p>
<div class="source">
<div class="source">
<pre>{&quot;version&quot;: 3}
</pre></div></div>
<p>Where the number corresponds to the latest available schema version of the database you are using. This means you can be migrating to this schema version.</p>
<p>Response codes:</p>
<ul>
<li>200: Success</li>
</ul></section><section>
<h3><a name="Upgrading_to_a_specific_version"></a>Upgrading to a specific version</h3>
<div class="source">
<div class="source">
<pre>curl -XPOST -H &quot;Content-Type: application/json http://ip:port/cassandra/version/upgrade -d '3'
</pre></div></div>
<p>Will schedule the run of the migrations you need to reach schema version 3.</p>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>Response codes:</p>
<ul>
<li>200: Success. The scheduled task taskId is returned.</li>
<li>400: The version is invalid. The version should be a strictly positive number.</li>
<li>410: Error while planning this migration. This resource is gone away. Reason is mentionned in the body.</li>
</ul>
<p>Note that several calls to this endpoint will be run in a sequential pattern.</p>
<p>If the server restarts during the migration, the migration is silently aborted.</p>
<p>The scheduled task will have the following type cassandra-migration and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{&quot;targetVersion&quot;:3}
</pre></div></div>
</section><section>
<h3><a name="Upgrading_to_the_latest_version"></a>Upgrading to the latest version</h3>
<div class="source">
<div class="source">
<pre>curl -XPOST http://ip:port/cassandra/version/upgrade/latest
</pre></div></div>
<p>Will schedule the run of the migrations you need to reach the latest schema version.</p>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>Response codes:</p>
<ul>
<li>200: Success. The scheduled task taskId is returned.</li>
<li>410: Error while planning this migration. This resource is gone away. Reason is mentionned in the body.</li>
</ul>
<p>Note that several calls to this endpoint will be run in a sequential pattern.</p>
<p>If the server restarts during the migration, the migration is silently aborted.</p>
<p>The scheduled task will have the following type cassandra-migration and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{&quot;toVersion&quot;:2}
</pre></div></div>
</section></section><section>
<h2><a name="Correcting_ghost_mailbox"></a>Correcting ghost mailbox</h2>
<p>This is a temporary workaround for the <b>Ghost mailbox</b> bug encountered using the Cassandra backend, as described in MAILBOX-322.</p>
<p>You can use the mailbox merging feature in order to merge the old &#x201c;ghosted&#x201d; mailbox with the new one.</p>
<div class="source">
<div class="source">
<pre>curl -XPOST http://ip:port/cassandra/mailbox/merging \
-d '{&quot;mergeOrigin&quot;:&quot;{id1}&quot;, &quot;mergeDestination&quot;:&quot;{id2}&quot;}' \
-H &quot;Content-Type: application/json&quot;
</pre></div></div>
<p>Will scedule a task for :</p>
<ul>
<li>Delete references to id1 mailbox</li>
<li>Move it&#x2019;s messages into id2 mailbox</li>
<li>Union the rights of both mailboxes</li>
</ul>
<p><a href="#Endpoints_returning_a_task">More details about endpoints returning a task</a>.</p>
<p>Response codes:</p>
<ul>
<li>201: Task generation succeeded. Corresponding task id is returned.</li>
<li>400: Unable to parse the body.</li>
</ul>
<p>The scheduled task will have the following type mailbox-merging and the following additionalInformation:</p>
<div class="source">
<div class="source">
<pre>{
&quot;oldMailboxId&quot;:&quot;5641376-02ed-47bd-bcc7-76ff6262d92a&quot;,
&quot;newMailboxId&quot;:&quot;4555159-52ae-895f-ccb7-586a4412fb50&quot;,
&quot;totalMessageCount&quot;: 1,
&quot;messageMovedCount&quot;: 1,
&quot;messageFailedCount&quot;: 0
}
</pre></div></div>
</section><section>
<h2><a name="Creating_address_group"></a>Creating address group</h2>
<p>You can use <b>webadmin</b> to define address groups.</p>
<p>When a specific email is sent to the group mail address, every group member will receive it.</p>
<p>Note that the group mail address is virtual: it does not correspond to an existing user.</p>
<p>This feature uses <a href="/server/config-recipientrewritetable.html">Recipients rewrite table</a> and requires the <a class="externalLink" href="https://github.com/apache/james-project/blob/master/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java">RecipientRewriteTable mailet</a> to be configured.</p>
<p>Note that email addresses are restricted to ASCII character set. Mail addresses not matching this criteria will be rejected.</p>
<ul>
<li><a href="#Listing_groups">Listing groups</a></li>
<li><a href="#Listing_members_of_a_group">Listing members of a group</a></li>
<li><a href="#Adding_a_group_member">Adding a group member</a></li>
<li><a href="#Removing_a_group_member">Removing a group member</a></li>
</ul><section>
<h3><a name="Listing_groups"></a>Listing groups</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/address/groups
</pre></div></div>
<p>Will return the groups as a list of JSON Strings representing mail addresses. For instance:</p>
<div class="source">
<div class="source">
<pre>[&quot;group1@domain.com&quot;, &quot;group2@domain.com&quot;]
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: Success</li>
</ul></section><section>
<h3><a name="Listing_members_of_a_group"></a>Listing members of a group</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/address/groups/group@domain.com
</pre></div></div>
<p>Will return the group members as a list of JSON Strings representing mail addresses. For instance:</p>
<div class="source">
<div class="source">
<pre>[&quot;member1@domain.com&quot;, &quot;member2@domain.com&quot;]
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: Success</li>
<li>400: Group structure is not valid</li>
<li>404: The group does not exist</li>
</ul></section><section>
<h3><a name="Adding_a_group_member"></a>Adding a group member</h3>
<div class="source">
<div class="source">
<pre>curl -XPUT http://ip:port/address/groups/group@domain.com/member@domain.com
</pre></div></div>
<p>Will add <a class="externalLink" href="mailto:member@domain.com">member@domain.com</a> to <a class="externalLink" href="mailto:group@domain.com">group@domain.com</a>, creating the group if needed</p>
<p>Response codes:</p>
<ul>
<li>204: Success</li>
<li>400: Group structure or member is not valid</li>
<li>400: Domain in the source is not managed by the DomainList</li>
<li>409: Requested group address is already used for another purpose</li>
<li>409: The addition of the group member would lead to a loop and thus cannot be performed</li>
</ul></section><section>
<h3><a name="Removing_a_group_member"></a>Removing a group member</h3>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/address/groups/group@domain.com/member@domain.com
</pre></div></div>
<p>Will remove <a class="externalLink" href="mailto:member@domain.com">member@domain.com</a> from <a class="externalLink" href="mailto:group@domain.com">group@domain.com</a>, removing the group if group is empty after deletion</p>
<p>Response codes:</p>
<ul>
<li>204: Success</li>
<li>400: Group structure or member is not valid</li>
</ul></section></section><section>
<h2><a name="Creating_address_forwards"></a>Creating address forwards</h2>
<p>You can use <b>webadmin</b> to define address forwards.</p>
<p>When a specific email is sent to the base mail address, every forward destination addresses will receive it.</p>
<p>Please note that the base address can be optionaly part of the forward destination. In that case, the base recipient also receive a copy of the mail. Otherwise he is ommitted.</p>
<p>Forwards can be defined for existing users. It then defers from &#x201c;groups&#x201d;.</p>
<p>This feature uses <a href="/server/config-recipientrewritetable.html">Recipients rewrite table</a> and requires the <a class="externalLink" href="https://github.com/apache/james-project/blob/master/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java">RecipientRewriteTable mailet</a> to be configured.</p>
<p>Note that email addresses are restricted to ASCII character set. Mail addresses not matching this criteria will be rejected.</p>
<ul>
<li><a href="#Listing_Forwards">Listing Forwards</a></li>
<li><a href="#Listing_destinations_in_a_forward">Listing destinations in a forward</a></li>
<li><a href="#Adding_a_new_destination_to_a_forward">Adding a new destination to a forward</a></li>
<li><a href="#Removing_a_destination_of_a_forward">Removing a destination of a forward</a></li>
</ul><section>
<h3><a name="Listing_Forwards"></a>Listing Forwards</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/address/forwards
</pre></div></div>
<p>Will return the users having forwards configured as a list of JSON Strings representing mail addresses. For instance:</p>
<div class="source">
<div class="source">
<pre>[&quot;user1@domain.com&quot;, &quot;user2@domain.com&quot;]
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: Success</li>
</ul></section><section>
<h3><a name="Listing_destinations_in_a_forward"></a>Listing destinations in a forward</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/address/forwards/user@domain.com
</pre></div></div>
<p>Will return the destination addresses of this forward as a list of JSON Strings representing mail addresses. For instance:</p>
<div class="source">
<div class="source">
<pre>[
{&quot;mailAddress&quot;:&quot;destination1@domain.com&quot;},
{&quot;mailAddress&quot;:&quot;destination2@domain.com&quot;}
]
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: Success</li>
<li>400: Forward structure is not valid</li>
<li>404: The given user don&#x2019;t have forwards or does not exist</li>
</ul></section><section>
<h3><a name="Adding_a_new_destination_to_a_forward"></a>Adding a new destination to a forward</h3>
<div class="source">
<div class="source">
<pre>curl -XPUT http://ip:port/address/forwards/user@domain.com/targets/destination@domain.com
</pre></div></div>
<p>Will add <a class="externalLink" href="mailto:destination@domain.com">destination@domain.com</a> to <a class="externalLink" href="mailto:user@domain.com">user@domain.com</a>, creating the forward if needed</p>
<p>Response codes:</p>
<ul>
<li>204: Success</li>
<li>400: Forward structure or member is not valid</li>
<li>400: Domain in the source is not managed by the DomainList</li>
<li>404: Requested forward address does not match an existing user</li>
<li>409: The creation of the forward would lead to a loop and thus cannot be performed</li>
</ul></section><section>
<h3><a name="Removing_a_destination_of_a_forward"></a>Removing a destination of a forward</h3>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/address/forwards/user@domain.com/targets/destination@domain.com
</pre></div></div>
<p>Will remove <a class="externalLink" href="mailto:destination@domain.com">destination@domain.com</a> from <a class="externalLink" href="mailto:user@domain.com">user@domain.com</a>, removing the forward if forward is empty after deletion</p>
<p>Response codes:</p>
<ul>
<li>204: Success</li>
<li>400: Forward structure or member is not valid</li>
</ul></section></section><section>
<h2><a name="Creating_address_aliases"></a>Creating address aliases</h2>
<p>You can use <b>webadmin</b> to define aliases for an user.</p>
<p>When a specific email is sent to the alias address, the destination address of the alias will receive it.</p>
<p>Aliases can be defined for existing users.</p>
<p>This feature uses <a href="/server/config-recipientrewritetable.html">Recipients rewrite table</a> and requires the <a class="externalLink" href="https://github.com/apache/james-project/blob/master/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java">RecipientRewriteTable mailet</a> to be configured.</p>
<p>Note that email addresses are restricted to ASCII character set. Mail addresses not matching this criteria will be rejected.</p>
<ul>
<li><a href="#Listing_users_with_aliases">Listing users with aliases</a></li>
<li><a href="#Listing_alias_sources_of_an_user">Listing alias sources of an user</a></li>
<li><a href="#Adding_a_new_alias_to_an_user">Adding a new alias to an user</a></li>
<li><a href="#Removing_an_alias_of_an_user">Removing an alias of an user</a></li>
</ul><section>
<h3><a name="Listing_users_with_aliases"></a>Listing users with aliases</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/address/aliases
</pre></div></div>
<p>Will return the users having aliases configured as a list of JSON Strings representing mail addresses. For instance:</p>
<div class="source">
<div class="source">
<pre>[&quot;user1@domain.com&quot;, &quot;user2@domain.com&quot;]
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: Success</li>
</ul></section><section>
<h3><a name="Listing_alias_sources_of_an_user"></a>Listing alias sources of an user</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/address/aliases/user@domain.com
</pre></div></div>
<p>Will return the aliases of this user as a list of JSON Strings representing mail addresses. For instance:</p>
<div class="source">
<div class="source">
<pre>[
{&quot;source&quot;:&quot;alias1@domain.com&quot;},
{&quot;source&quot;:&quot;alias2@domain.com&quot;}
]
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: Success</li>
<li>400: Alias structure is not valid</li>
</ul></section><section>
<h3><a name="Adding_a_new_alias_to_an_user"></a>Adding a new alias to an user</h3>
<div class="source">
<div class="source">
<pre>curl -XPUT http://ip:port/address/aliases/user@domain.com/sources/alias@domain.com
</pre></div></div>
<p>Will add <a class="externalLink" href="mailto:alias@domain.com">alias@domain.com</a> to <a class="externalLink" href="mailto:user@domain.com">user@domain.com</a>, creating the alias if needed</p>
<p>Response codes:</p>
<ul>
<li>204: OK</li>
<li>400: Alias structure or member is not valid</li>
<li>400: Source and destination can&#x2019;t be the same!</li>
<li>400: Domain in the destination or source is not managed by the DomainList</li>
<li>409: The alias source exists as an user already</li>
<li>409: The creation of the alias would lead to a loop and thus cannot be performed</li>
</ul></section><section>
<h3><a name="Removing_an_alias_of_an_user"></a>Removing an alias of an user</h3>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/address/aliases/user@domain.com/sources/alias@domain.com
</pre></div></div>
<p>Will remove <a class="externalLink" href="mailto:alias@domain.com">alias@domain.com</a> from <a class="externalLink" href="mailto:user@domain.com">user@domain.com</a>, removing the alias if needed</p>
<p>Response codes:</p>
<ul>
<li>204: OK</li>
<li>400: Alias structure or member is not valid</li>
</ul></section></section><section>
<h2><a name="Creating_domain_mappings"></a>Creating domain mappings</h2>
<p>You can use <b>webadmin</b> to define domain mappings.</p>
<p>Given a configured source (from) domain and a destination (to) domain, when an email is sent to an address belonging to the source domain, then the domain part of this address is overwritten, the destination domain is then used. A source (from) domain can have many destination (to) domains.</p>
<p>For example: with a source domain james.apache.org maps to two destination domains james.org and apache-james.org, when a mail is sent to admin@james.apache.org, then it will be routed to admin@james.org and admin@apache-james.org</p>
<p>This feature uses <a href="/server/config-recipientrewritetable.html">Recipients rewrite table</a> and requires the <a class="externalLink" href="https://github.com/apache/james-project/blob/master/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java">RecipientRewriteTable mailet</a> to be configured.</p>
<p>Note that email addresses are restricted to ASCII character set. Mail addresses not matching this criteria will be rejected.</p>
<ul>
<li><a href="#Listing_all_domain_mappings">Listing all domain mappings</a></li>
<li><a href="#Listing_all_destination_domains_for_a_source_domain">Listing all destination domains for a source domain</a></li>
<li><a href="#Adding_a_domain_mapping">Adding a domain mapping</a></li>
<li><a href="#Removing_a_domain_mapping">Removing a domain mapping</a></li>
</ul><section>
<h3><a name="Listing_all_domain_mappings"></a>Listing all domain mappings</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/domainMappings
</pre></div></div>
<p>Will return all configured domain mappings</p>
<div class="source">
<div class="source">
<pre>{
&quot;firstSource.org&quot; : [&quot;firstDestination.com&quot;, &quot;secondDestination.net&quot;],
&quot;secondSource.com&quot; : [&quot;thirdDestination.com&quot;, &quot;fourthDestination.net&quot;],
}
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: OK</li>
</ul></section><section>
<h3><a name="Listing_all_destination_domains_for_a_source_domain"></a>Listing all destination domains for a source domain</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/domainMappings/sourceDomain.tld
</pre></div></div>
<p>With sourceDomain.tld as the value passed to fromDomain resource name, the API will return all destination domains configured to that domain</p>
<div class="source">
<div class="source">
<pre>[&quot;firstDestination.com&quot;, &quot;secondDestination.com&quot;]
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: OK</li>
<li>400: The fromDomain resource name is invalid</li>
<li>404: The fromDomain resource name is not found</li>
</ul></section><section>
<h3><a name="Adding_a_domain_mapping"></a>Adding a domain mapping</h3>
<div class="source">
<div class="source">
<pre>curl -XPUT http://ip:port/domainMappings/sourceDomain.tld
</pre></div></div>
<p>Body:</p>
<div class="source">
<div class="source">
<pre>destination.tld
</pre></div></div>
<p>With sourceDomain.tld as the value passed to fromDomain resource name, the API will add a destination domain specified in the body to that domain</p>
<p>Response codes:</p>
<ul>
<li>204: OK</li>
<li>400: The fromDomain resource name is invalid</li>
<li>400: The destination domain specified in the body is invalid</li>
</ul>
<p>Be aware that no checks to find possible loops that would result of this creation will be performed.</p></section><section>
<h3><a name="Removing_a_domain_mapping"></a>Removing a domain mapping</h3>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/domainMappings/sourceDomain.tld
</pre></div></div>
<p>Body:</p>
<div class="source">
<div class="source">
<pre>destination.tld
</pre></div></div>
<p>With sourceDomain.tld as the value passed to fromDomain resource name, the API will remove a destination domain specified in the body mapped to that domain</p>
<p>Response codes:</p>
<ul>
<li>204: OK</li>
<li>400: The fromDomain resource name is invalid</li>
<li>400: The destination domain specified in the body is invalid</li>
</ul></section></section><section>
<h2><a name="Creating_regex_mapping"></a>Creating regex mapping</h2>
<p>You can use <b>webadmin</b> to create regex mappings.</p>
<p>A regex mapping contains a mapping source and a Java Regular Expression (regex) in String as the mapping value. Everytime, if a mail containing a recipient matched with the mapping source, then that mail will be re-routed to a new recipient address which is re written by the regex.</p>
<p>This feature uses <a href="/server/config-recipientrewritetable.html">Recipients rewrite table</a> and requires the <a class="externalLink" href="https://github.com/apache/james-project/blob/master/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java">RecipientRewriteTable API</a> to be configured.</p>
<ul>
<li><a href="#Adding_a_regex_mapping">Adding a regex mapping</a></li>
<li><a href="#Removing_a_regex_mapping">Removing a regex mapping</a></li>
</ul><section>
<h3><a name="Adding_a_regex_mapping"></a>Adding a regex mapping</h3>
<div class="source">
<div class="source">
<pre>POST /mappings/regex/mappingSource/targets/regex
</pre></div></div>
<p>Where:</p>
<ul>
<li>the mappingSource is the path parameter represents for the Regex Mapping mapping source</li>
<li>the regex is the path parameter represents for the Regex Mapping regex</li>
</ul>
<p>The route will add a regex mapping made from mappingSource and regex to RecipientRewriteTable.</p>
<p>Example:</p>
<div class="source">
<div class="source">
<pre>curl -XPOST http://ip:port/mappings/regex/james@domain.tld/targets/james@.*:james-intern@james.org
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>204: Mapping added successfully.</li>
<li>400: Invalid mappingSource path parameter.</li>
<li>400: Invalid regex path parameter.</li>
</ul>
<p>Be aware that no checks to find possible loops that would result of this creation will be performed.</p></section><section>
<h3><a name="Removing_a_regex_mapping"></a>Removing a regex mapping</h3>
<div class="source">
<div class="source">
<pre>DELETE /mappings/regex/{mappingSource}/targets/{regex}
</pre></div></div>
<p>Where:</p>
<ul>
<li>the mappingSource is the path parameter representing the Regex Mapping mapping source</li>
<li>the regex is the path parameter representing the Regex Mapping regex</li>
</ul>
<p>The route will remove the regex mapping made from regex from the mapping source mappingSource to RecipientRewriteTable.</p>
<p>Example:</p>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/mappings/regex/james@domain.tld/targets/[O_O]:james-intern@james.org
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>204: Mapping deleted successfully.</li>
<li>400: Invalid mappingSource path parameter.</li>
<li>400: Invalid regex path parameter.</li>
</ul></section></section><section>
<h2><a name="Address_Mappings"></a>Address Mappings</h2>
<p>You can use <b>webadmin</b> to define address mappings.</p>
<p>When a specific email is sent to the base mail address, every destination addresses will receive it.</p>
<p>This feature uses <a href="/server/config-recipientrewritetable.html">Recipients rewrite table</a> and requires the <a class="externalLink" href="https://github.com/apache/james-project/blob/master/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java">RecipientRewriteTable mailet</a> to be configured.</p>
<p>Note that email addresses are restricted to ASCII character set. Mail addresses not matching this criteria will be rejected.</p>
<p>Please use address mappings with caution, as it&#x2019;s not a typed address. If you know the type of your address (forward, alias, domain, group, etc), prefer using the corresponding routes to those types.</p>
<p>Here are the following actions available on address mappings:</p>
<ul>
<li><a href="#List_all_address_mappings">List all address mappings</a></li>
<li><a href="#Add_an_address_mapping">Add an address mapping</a></li>
<li><a href="#Remove_an_address_mapping">Remove an address mapping</a></li>
</ul><section>
<h3><a name="List_all_address_mappings"></a>List all address mappings</h3>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/mappings
</pre></div></div>
<p>Get all mappings from the <a href="/server/config-recipientrewritetable.html">Recipients rewrite table</a> Supported mapping types are the following:</p>
<ul>
<li><a href="#Creating_address_aliases">Alias</a></li>
<li><a href="#Address_Mappings">Address</a></li>
<li><a href="#Creating_address_domain">Domain</a></li>
<li>Error</li>
<li><a href="#Creating_address_forwards">Forward</a></li>
<li><a href="#Creating_address_group">Group</a></li>
<li>Regex</li>
</ul>
<p>Response body:</p>
<div class="source">
<div class="source">
<pre>{
&quot;alias@domain.tld&quot;: [
{
&quot;type&quot;: &quot;Alias&quot;,
&quot;mapping&quot;: &quot;user@domain.tld&quot;
},
{
&quot;type&quot;: &quot;Group&quot;,
&quot;mapping&quot;: &quot;group-user@domain.tld&quot;
}
],
&quot;aliasdomain.tld&quot;: [
{
&quot;type&quot;: &quot;Domain&quot;,
&quot;mapping&quot;: &quot;realdomain.tld&quot;
}
],
&quot;group@domain.tld&quot;: [
{
&quot;type&quot;: &quot;Address&quot;,
&quot;mapping&quot;: &quot;user@domain.tld&quot;
}
]
}
</pre></div></div>
<p>Response code:</p>
<ul>
<li>200: OK</li>
</ul></section><section>
<h3><a name="Add_an_address_mapping"></a>Add an address mapping</h3>
<div class="source">
<div class="source">
<pre>curl -XPOST http://ip:port/mappings/address/{mappingSource}/targets/{destinationAddress}
</pre></div></div>
<p>Add an address mapping to the <a href="/server/config-recipientrewritetable.html">Recipients rewrite table</a> Mapping source is the value of {mappingSource} Mapping destination is the value of {destinationAddress} Type of mapping destination is Address</p>
<p>Response codes:</p>
<ul>
<li>204: Action successfully performed</li>
<li>400: Invalid parameters</li>
<li>409: The creation of the address mapping would lead to a loop and thus cannot be performed</li>
</ul></section><section>
<h3><a name="Remove_an_address_mapping"></a>Remove an address mapping</h3>
<div class="source">
<div class="source">
<pre>curl -XDELETE http://ip:port/mappings/address/{mappingSource}/targets/{destinationAddress}
</pre></div></div>
<ul>
<li>Remove an address mapping from the <a href="/server/config-recipientrewritetable.html">Recipients rewrite table</a></li>
<li>Mapping source is the value of mappingSource</li>
<li>Mapping destination is the value of destinationAddress</li>
<li>Type of mapping destination is Address</li>
</ul>
<p>Response codes:</p>
<ul>
<li>204: Action successfully performed</li>
<li>400: Invalid parameters</li>
</ul></section></section><section>
<h2><a name="User_Mappings"></a>User Mappings</h2>
<ul>
<li><a href="#Listing_User_Mappings">Listing User Mappings</a></li>
</ul><section>
<h3><a name="Listing_User_Mappings"></a>Listing User Mappings</h3>
<p>This endpoint allows receiving all mappings of a corresponding user.</p>
<div class="source">
<div class="source">
<pre>curl -XGET http://ip:port/mappings/user/{userAddress}
</pre></div></div>
<p>Return all mappings of a user where:</p>
<ul>
<li>userAddress: is the selected user</li>
</ul>
<p>Response body:</p>
<div class="source">
<div class="source">
<pre>[
{
&quot;type&quot;: &quot;Address&quot;,
&quot;mapping&quot;: &quot;user123@domain.tld&quot;
},
{
&quot;type&quot;: &quot;Alias&quot;,
&quot;mapping&quot;: &quot;aliasuser123@domain.tld&quot;
},
{
&quot;type&quot;: &quot;Group&quot;,
&quot;mapping&quot;: &quot;group123@domain.tld&quot;
}
]
</pre></div></div>
<p>Response codes:</p>
<ul>
<li>200: OK</li>
<li>400: Invalid parameter value</li>
</ul></section></section><section>
<h2><a name="Administrating_mail_repositories"></a>Administrating mail repositories</h2>
<ul>
<li><a href="#Create_a_mail_repository">Create a mail repository</a></li>
<li><a href="#Listing_mail_repositories">Listing mail repositories</a></li>
<li><a href="#Getting_additional_information_for_a_mail_repository">Getting additional information for a mail repository</a></li>
<li><a href="#Listing_mails_contained_in_a_mail_repository">Listing mails contained in a mail repository</a></li>
<li><a href="#Reading.2Fdownloading_a_mail_details">Reading/downloading a mail details</a></li>
<li><a href="#Removing_a_mail_from_a_mail_repository">Removing a mail from a mail repository</a></li>
<li><a href="#Removing_all_mails_from_a_mail_repository">Removing all mails from a mail repository</a></li>
<li><a href="#Reprocessing_mails_from_a_mail_repository">Reprocessing mails from a mail repository</a></li>