You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2020/07/17 02:24:37 UTC

[james-project] 23/31: JAMES-3302 Migrate WebAdmin documentation to Antora

This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 466973978fca0361fbc899b33a7abe1ed29fb9d4
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Jul 9 13:52:27 2020 +0700

    JAMES-3302 Migrate WebAdmin documentation to Antora
    
    Adapted to remove 'only on some products' mentions.
    
    Note that some liks needs to be fixed.
---
 .../pages/distributed/operate/webadmin.adoc        | 4047 +++++++++++++++++++-
 .../servers/pages/distributed/run-docker.adoc      |   38 +-
 2 files changed, 4079 insertions(+), 6 deletions(-)

diff --git a/docs/modules/servers/pages/distributed/operate/webadmin.adoc b/docs/modules/servers/pages/distributed/operate/webadmin.adoc
index ed1b75a..55c589d 100644
--- a/docs/modules/servers/pages/distributed/operate/webadmin.adoc
+++ b/docs/modules/servers/pages/distributed/operate/webadmin.adoc
@@ -1,4 +1,4047 @@
 = WebAdmin REST administration API
 
-(TODO migrate and adapt content from
-https://github.com/linagora/james-project/blob/master/src/site/markdown/server/manage-webadmin.md)
\ No newline at end of file
+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.
+
+*WARNING*: 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.
+
+By the way, some endpoints are not filtered by authentication. Those endpoints are not related to data stored in James,
+for example: Swagger documentation & James health checks.
+
+In case of any error, the system will return an error message which is
+json format like this:
+
+....
+{
+    statusCode: <error_code>,
+    type: <error_type>,
+    message: <the_error_message>
+    cause: <the_detail_message_from_throwable>
+}
+....
+
+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 omitted on endpoint specific
+documentation.
+
+Finally, please note that in case of a malformed URL the 400 bad request
+response will contain an HTML body.
+
+== HealthCheck
+
+=== Check all components
+
+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.
+
+....
+curl -XGET http://ip:port/healthcheck
+....
+
+Will return a list of healthChecks execution result, with an aggregated
+result:
+
+....
+{
+  "status": "healthy",
+  "checks": [
+    {
+      "componentName": "Cassandra backend",
+      "escapedComponentName": "Cassandra%20backend",
+      "status": "healthy"
+      "cause": null
+    }
+  ]
+}
+....
+
+*status* field can be:
+
+* *healthy*: Component works normally
+* *degraded*: Component works in degraded mode. Some non-critical
+services may not be working, or latencies are high, for example. Cause
+contains explanations.
+* *unhealthy*: The component is currently not working. Cause contains
+explanations.
+
+Supported health checks include:
+
+* *Cassandra backend*: Cassandra storage.
+* *ElasticSearch Backend*: ElasticSearch storage.
+* *EventDeadLettersHealthCheck*
+* *Guice application lifecycle*
+* *JPA Backend*: JPA storage.
+* *MessageFastViewProjection* 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`)
+* *RabbitMQ backend*: RabbitMQ messaging.
+
+Response codes:
+
+* 200: All checks have answered with a Healthy or Degraded status. James
+services can still be used.
+* 503: At least one check have answered with a Unhealthy status
+
+=== Check single component
+
+Performs a health check for the given component. The component is
+referenced by its URL encoded name.
+
+....
+curl -XGET http://ip:port/healthcheck/checks/Cassandra%20backend
+....
+
+Will return the component’s name, the component’s escaped name, the
+health status and a cause.
+
+....
+{
+  "componentName": "Cassandra backend",
+  "escapedComponentName": "Cassandra%20backend",
+  "status": "healthy"
+  "cause": null
+}
+....
+
+Response codes:
+
+* 200: The check has answered with a Healthy or Degraded status.
+* 404: A component with the given name was not found.
+* 503: The check has anwered with a Unhealthy status.
+
+=== List all health checks
+
+This endpoint lists all the available health checks.
+
+....
+curl -XGET http://ip:port/healthcheck/checks
+....
+
+Will return the list of all available health checks.
+
+....
+[
+    {
+        "componentName": "Cassandra backend",
+        "escapedComponentName": "Cassandra%20backend"
+    }
+]
+....
+
+Response codes:
+
+* 200: List of available health checks
+
+== Administrating domains
+
+=== Create a domain
+
+....
+curl -XPUT http://ip:port/domains/domainToBeCreated
+....
+
+Resource name domainToBeCreated:
+
+* can not be null or empty
+* can not contain `@'
+* can not be more than 255 characters
+* can not contain `/'
+
+Response codes:
+
+* 204: The domain was successfully added
+* 400: The domain name is invalid
+
+=== Delete a domain
+
+....
+curl -XDELETE http://ip:port/domains/{domainToBeDeleted}
+....
+
+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 https://james.apache.org/server/config-domainlist.html[domain list
+configuration].
+
+Response codes:
+
+* 204: The domain was successfully removed
+
+=== Test if a domain exists
+
+....
+curl -XGET http://ip:port/domains/{domainName}
+....
+
+Response codes:
+
+* 204: The domain exists
+* 404: The domain does not exist
+
+=== Get the list of domains
+
+....
+curl -XGET http://ip:port/domains
+....
+
+Possible response:
+
+....
+["domain1", "domain2"]
+....
+
+Response codes:
+
+* 200: The domain list was successfully retrieved
+
+=== Get the list of aliases for a domain
+
+....
+curl -XGET http://ip:port/domains/destination.domain.tld/aliases
+....
+
+Possible response:
+
+....
+[
+  {"source": "source1.domain.tld"},
+  {"source": "source2.domain.tld"}
+]
+....
+
+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`).
+
+Response codes:
+
+* 200: The domain aliases was successfully retrieved
+* 400: destination.domain.tld has an invalid syntax
+* 404: destination.domain.tld is not part of handled domains and does
+not have local domains as aliases.
+
+=== Create an alias for a domain
+
+To create a domain alias execute the following query:
+
+....
+curl -XPUT http://ip:port/domains/destination.domain.tld/aliases/source.domain.tld
+....
+
+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`).
+
+Response codes:
+
+* 204: The redirection now exists
+* 400: `source.domain.tld` or `destination.domain.tld` have an invalid
+syntax
+* 400: `source, domain` and `destination domain` are the same
+* 404: `source.domain.tld` are not part of handled domains.
+
+=== Delete an alias for a domain
+
+To delete a domain alias execute the following query:
+
+....
+curl -XDELETE http://ip:port/domains/destination.domain.tld/aliases/source.domain.tld
+....
+
+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`).
+
+Response codes:
+
+* 204: The redirection now no longer exists
+* 400: `source.domain.tld` or destination.domain.tld have an invalid
+syntax
+* 400: source, domain and destination domain are the same
+* 404: `source.domain.tld` are not part of handled domains.
+
+== Administrating users
+
+=== Create a user
+
+....
+curl -XPUT http://ip:port/users/usernameToBeUsed \
+  -d '{"password":"passwordToBeUsed"}' \
+  -H "Content-Type: application/json"
+....
+
+Resource name usernameToBeUsed representing valid users, hence it should
+match the criteria at (TODO) link:/server/config-users.html[User Repositories
+documentation]
+
+Response codes:
+
+* 204: The user was successfully created
+* 400: The user name or the payload is invalid
+
+Note: if the user exists already, its password will be updated.
+
+=== Testing a user existence
+
+....
+curl -XHEAD http://ip:port/users/usernameToBeUsed
+....
+
+Resource name ``usernameToBeUsed'' represents a valid user, hence it
+should match the criteria at (TODO) link:/server/config-users.html[User
+Repositories documentation]
+
+Response codes:
+
+* 200: The user exists
+* 400: The user name is invalid
+* 404: The user does not exist
+
+=== Updating a user password
+
+Same than Create, but a user need to exist.
+
+If the user do not exist, then it will be created.
+
+=== Deleting a user
+
+....
+curl -XDELETE http://ip:port/users/{userToBeDeleted}
+....
+
+Response codes:
+
+* 204: The user was successfully deleted
+
+=== Retrieving the user list
+
+....
+curl -XGET http://ip:port/users
+....
+
+The answer looks like:
+
+....
+[{"username":"username@domain-jmapauthentication.tld"},{"username":"username@domain.tld"}]
+....
+
+Response codes:
+
+* 200: The user name list was successfully retrieved
+
+=== Retrieving the list of allowed `From` headers for a given user
+
+This endpoint allows to know which From headers a given user is allowed to use when sending mails.
+
+....
+curl -XGET http://ip:port/users/givenUser/allowedFromHeaders
+....
+
+The answer looks like:
+
+....
+["user@domain.tld","alias@domain.tld"]
+....
+
+Response codes:
+
+* 200: The list was successfully retrieved
+* 400: The user is invalid
+* 404: The user is unknown
+
+== Administrating mailboxes
+
+=== All mailboxes
+
+Several actions can be performed on the server mailboxes.
+
+Request pattern is:
+
+....
+curl -XPOST /mailboxes?action={action1},...
+....
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+Response codes:
+
+* 201: Success. Corresponding task id is returned.
+* 400: Error in the request. Details can be found in the reported error.
+
+The kind of task scheduled depends on the action parameter. See below
+for details.
+
+==== Fixing mailboxes inconsistencies
+
+....
+curl -XPOST /mailboxes?task=SolveInconsistencies
+....
+
+Will schedule a task for fixing inconsistencies for the mailbox
+deduplicated object stored in Cassandra.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+The `I-KNOW-WHAT-I-M-DOING` header is mandatory (you can read more
+information about it in the warning section below).
+
+The scheduled task will have the following type
+`solve-mailbox-inconsistencies` and the following
+`additionalInformation`:
+
+....
+{
+  "type":"solve-mailbox-inconsistencies",
+  "processedMailboxEntries": 3,
+  "processedMailboxPathEntries": 3,
+  "fixedInconsistencies": 2,
+  "errors": 1,
+  "conflictingEntries":[{
+    "mailboxDaoEntry":{
+      "mailboxPath":"#private:user:mailboxName",
+      "mailboxId":"464765a0-e4e7-11e4-aba4-710c1de3782b"
+    }," +
+    "mailboxPathDaoEntry":{
+      "mailboxPath":"#private:user:mailboxName2",
+      "mailboxId":"464765a0-e4e7-11e4-aba4-710c1de3782b"
+    }
+  }]
+}
+....
+
+Note that conflicting entry inconsistencies will not be fixed and will
+require to explicitly use link:#correcting-ghost-mailbox[ghost mailbox]
+endpoint in order to merge the conflicting mailboxes and prevent any
+message loss.
+
+*WARNING*: this task can cancel concurrently running legitimate user
+operations upon dirty read. As such this task should be run offline.
+
+A dirty read is when data is read between the two writes of the
+denormalization operations (no isolation).
+
+In order to ensure being offline, stop the traffic on SMTP, JMAP and
+IMAP ports, for example via re-configuration or firewall rules.
+
+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.
+
+==== Recomputing mailbox counters
+
+....
+curl -XPOST /mailboxes?task=RecomputeMailboxCounters
+....
+
+Will recompute counters (unseen & total count) for the mailbox object
+stored in Cassandra.
+
+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.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+The scheduled task will have the following type
+`recompute-mailbox-counters` and the following `additionalInformation`:
+
+....
+{
+  "type":"recompute-mailbox-counters",
+  "processedMailboxes": 3,
+  "failedMailboxes": ["464765a0-e4e7-11e4-aba4-710c1de3782b"]
+}
+....
+
+Note that conflicting inconsistencies entries will not be fixed and will
+require to explicitly use link:#correcting-ghost-mailbox[ghost mailbox]
+endpoint in order to merge the conflicting mailboxes and prevent any
+message loss.
+
+*WARNING*: this task do not take into account concurrent modifications
+upon a single mailbox counter recomputation. Rerunning the task will
+_eventually_ provide the consistent result. As such we advise to run
+this task offline.
+
+In order to ensure being offline, stop the traffic on SMTP, JMAP and
+IMAP ports, for example via re-configuration or firewall rules.
+
+`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.
+
+Defaults to false, which generates additional checks. You can read
+https://github.com/apache/james-project/blob/master/src/adr/0022-cassandra-message-inconsistency.md[this
+ADR] to better understand the message projection and how it can become
+inconsistent.
+
+==== Recomputing Global JMAP fast message view projection
+
+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.
+
+These projection items are asynchronously computed on mailbox events.
+
+You can force the full projection recomputation by calling the following
+endpoint:
+
+....
+curl -XPOST /mailboxes?task=recomputeFastViewProjectionItems
+....
+
+Will schedule a task for recomputing the fast message view projection
+for all mailboxes.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+An admin can specify the concurrency that should be used when running
+the task:
+
+* `messagesPerSecond` rate at which messages should be processed, per
+second. Defaults to 10.
+
+This optional parameter must have a strictly positive integer as a value
+and be passed as query parameters.
+
+Example:
+
+....
+curl -XPOST /mailboxes?task=recomputeFastViewProjectionItems&messagesPerSecond=20
+....
+
+The scheduled task will have the following type
+`RecomputeAllFastViewProjectionItemsTask` and the following
+`additionalInformation`:
+
+....
+{
+  "type":"RecomputeAllPreviewsTask",
+  "processedUserCount": 3,
+  "processedMessageCount": 3,
+  "failedUserCount": 2,
+  "failedMessageCount": 1,
+  "runningOptions": {
+    "messagesPerSecond":20
+  }
+}
+....
+
+Response codes:
+
+* 201: Success. Corresponding task id is returned.
+* 400: Error in the request. Details can be found in the reported error.
+
+==== ReIndexing action
+
+Be also aware of the limits of this API:
+
+Warning: During the re-indexing, the result of search operations might
+be altered.
+
+Warning: Canceling this task should be considered unsafe as it will
+leave the currently reIndexed mailbox as partially indexed.
+
+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.
+
+===== ReIndexing all mails
+
+....
+curl -XPOST http://ip:port/mailboxes?task=reIndex
+....
+
+Will schedule a task for reIndexing all the mails stored on this James
+server.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+An admin can specify the concurrency that should be used when running
+the task:
+
+* `messagesPerSecond` rate at which messages should be processed per
+second. Default is 50.
+
+This optional parameter must have a strictly positive integer as a value
+and be passed as query parameter.
+
+An admin can also specify the reindexing mode it wants to use when
+running the task:
+
+* `mode` the reindexing mode used. There are 2 modes for the moment:
+** `rebuildAll` allows to rebuild all indexes. This is the default mode.
+** `fixOutdated` will check for outdated indexed document and reindex
+only those.
+
+This optional parameter must be passed as query parameter.
+
+It’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).
+
+Example:
+
+    curl -XPOST http://ip:port/mailboxes?task=reIndex&messagesPerSecond=200&mode=rebuildAll
+
+The scheduled task will have the following type `full-reindexing` and
+the following `additionalInformation`:
+
+....
+{
+  "type":"full-reindexing",
+  "runningOptions":{
+    "messagesPerSecond":200,
+    "mode":"REBUILD_ALL"
+  },
+  "successfullyReprocessedMailCount":18,
+  "failedReprocessedMailCount": 3,
+  "mailboxFailures": ["12", "23" ],
+  "messageFailures": [
+   {
+     "mailboxId": "1",
+      "uids": [1, 36]
+   }]
+}
+....
+
+===== Fixing previously failed ReIndexing
+
+Will schedule a task for reIndexing all the mails which had failed to be
+indexed from the ReIndexingAllMails task.
+
+Given `bbdb69c9-082a-44b0-a85a-6e33e74287a5` being a `taskId` generated
+for a reIndexing tasks
+
+....
+curl -XPOST 'http://ip:port/mailboxes?task=reIndex&reIndexFailedMessagesOf=bbdb69c9-082a-44b0-a85a-6e33e74287a5'
+....
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+An admin can specify the concurrency that should be used when running
+the task:
+
+* `messagesPerSecond` rate at which messages should be processed per
+second. Default is 50.
+
+This optional parameter must have a strictly positive integer as a value
+and be passed as query parameter.
+
+An admin can also specify the reindexing mode it wants to use when
+running the task:
+
+* `mode` the reindexing mode used. There are 2 modes for the moment:
+** `rebuildAll` allows to rebuild all indexes. This is the default mode.
+** `fixOutdated` will check for outdated indexed document and reindex
+only those.
+
+This optional parameter must be passed as query parameter.
+
+It’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).
+
+Example:
+
+....
+curl -XPOST http://ip:port/mailboxes?task=reIndex&reIndexFailedMessagesOf=bbdb69c9-082a-44b0-a85a-6e33e74287a5&messagesPerSecond=200&mode=rebuildAll
+....
+
+The scheduled task will have the following type
+`error-recovery-indexation` and the following `additionalInformation`:
+
+....
+{
+  "type":"error-recovery-indexation"
+  "runningOptions":{
+    "messagesPerSecond":200,
+    "mode":"REBUILD_ALL"
+  },
+  "successfullyReprocessedMailCount":18,
+  "failedReprocessedMailCount": 3,
+  "mailboxFailures": ["12", "23" ],
+  "messageFailures": [{
+     "mailboxId": "1",
+      "uids": [1, 36]
+   }]
+}
+....
+
+== Single mailbox
+
+=== ReIndexing a mailbox mails
+
+....
+curl -XPOST http://ip:port/mailboxes/{mailboxId}?task=reIndex
+....
+
+Will schedule a task for reIndexing all the mails in one mailbox.
+
+Note that `mailboxId' path parameter needs to be a (implementation
+dependent) valid mailboxId.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+An admin can specify the concurrency that should be used when running
+the task:
+
+* `messagesPerSecond` rate at which messages should be processed per
+second. Default is 50.
+
+This optional parameter must have a strictly positive integer as a value
+and be passed as query parameter.
+
+An admin can also specify the reindexing mode it wants to use when
+running the task:
+
+* `mode` the reindexing mode used. There are 2 modes for the moment:
+** `rebuildAll` allows to rebuild all indexes. This is the default mode.
+** `fixOutdated` will check for outdated indexed document and reindex
+only those.
+
+This optional parameter must be passed as query parameter.
+
+It’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).
+
+Example:
+
+....
+curl -XPOST http://ip:port/mailboxes/{mailboxId}?task=reIndex&messagesPerSecond=200&mode=fixOutdated
+....
+
+Response codes:
+
+* 201: Success. Corresponding task id is returned.
+* 400: Error in the request. Details can be found in the reported error.
+
+The scheduled task will have the following type `mailbox-reindexing` and
+the following `additionalInformation`:
+
+....
+{
+  "type":"mailbox-reindexing",
+  "runningOptions":{
+    "messagesPerSecond":200,
+    "mode":"FIX_OUTDATED"
+  },
+  "mailboxId":"{mailboxId}",
+  "successfullyReprocessedMailCount":18,
+  "failedReprocessedMailCount": 3,
+  "mailboxFailures": ["12"],
+  "messageFailures": [
+   {
+     "mailboxId": "1",
+      "uids": [1, 36]
+   }]
+}
+....
+
+Warning: During the re-indexing, the result of search operations might
+be altered.
+
+Warning: Canceling this task should be considered unsafe as it will
+leave the currently reIndexed mailbox as partially indexed.
+
+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.
+
+== Administrating Messages
+
+=== ReIndexing a single mail by messageId
+
+....
+curl -XPOST http://ip:port/messages/{messageId}?task=reIndex
+....
+
+Will schedule a task for reIndexing a single email in all the mailboxes
+containing it.
+
+Note that `messageId' path parameter needs to be a (implementation
+dependent) valid messageId.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+Response codes:
+
+* 201: Success. Corresponding task id is returned.
+* 400: Error in the request. Details can be found in the reported error.
+
+The scheduled task will have the following type `messageId-reindexing`
+and the following `additionalInformation`:
+
+....
+{
+  "messageId":"18"
+}
+....
+
+Warning: During the re-indexing, the result of search operations might
+be altered.
+
+=== Fixing message inconsistencies
+
+This task is only available on top of Guice Cassandra products.
+
+....
+curl -XPOST /messages?task=SolveInconsistencies
+....
+
+Will schedule a task for fixing message inconsistencies created by the
+message denormalization process.
+
+Messages are denormalized and stored in separated data tables in
+Cassandra, so they can be accessed by their unique identifier or mailbox
+identifier & local mailbox identifier through different protocols.
+
+Failure in the denormalization process will lead to inconsistencies, for
+example:
+
+....
+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
+....
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+An admin can specify the concurrency that should be used when running
+the task:
+
+* `messagesPerSecond` rate of messages to be processed per second.
+Default is 100.
+
+This optional parameter must have a strictly positive integer as a value
+and be passed as query parameter.
+
+An admin can also specify the reindexing mode it wants to use when
+running the task:
+
+* `mode` the reindexing mode used. There are 2 modes for the moment:
+** `rebuildAll` allows to rebuild all indexes. This is the default mode.
+** `fixOutdated` will check for outdated indexed document and reindex
+only those.
+
+This optional parameter must be passed as query parameter.
+
+It’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).
+
+Example:
+
+....
+curl -XPOST /messages?task=SolveInconsistencies&messagesPerSecond=200&mode=rebuildAll
+....
+
+Response codes:
+
+* 201: Success. Corresponding task id is returned.
+* 400: Error in the request. Details can be found in the reported error.
+
+The scheduled task will have the following type
+`solve-message-inconsistencies` and the following
+`additionalInformation`:
+
+....
+{
+  "type":"solve-message-inconsistencies",
+  "timestamp":"2007-12-03T10:15:30Z",
+  "processedImapUidEntries": 2,
+  "processedMessageIdEntries": 1,
+  "addedMessageIdEntries": 1,
+  "updatedMessageIdEntries": 0,
+  "removedMessageIdEntries": 1,
+  "runningOptions":{
+    "messagesPerSecond": 200,
+    "mode":"REBUILD_ALL"
+  },
+  "fixedInconsistencies": [
+    {
+      "mailboxId": "551f0580-82fb-11ea-970e-f9c83d4cf8c2",
+      "messageId": "d2bee791-7e63-11ea-883c-95b84008f979",
+      "uid": 1
+    },
+    {
+      "mailboxId": "551f0580-82fb-11ea-970e-f9c83d4cf8c2",
+      "messageId": "d2bee792-7e63-11ea-883c-95b84008f979",
+      "uid": 2
+    }
+  ],
+  "errors": [
+    {
+      "mailboxId": "551f0580-82fb-11ea-970e-f9c83d4cf8c2",
+      "messageId": "ffffffff-7e63-11ea-883c-95b84008f979",
+      "uid": 3
+    }
+  ]
+}
+....
+
+User actions concurrent to the inconsistency fixing task could result in
+concurrency issues. New inconsistencies could be created.
+
+However the source of truth will not be impacted, hence rerunning the
+task will eventually fix all issues.
+
+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.
+
+== Administrating user mailboxes
+
+=== Creating a mailbox
+
+....
+curl -XPUT http://ip:port/users/{usernameToBeUsed}/mailboxes/{mailboxNameToBeCreated}
+....
+
+Resource name `usernameToBeUsed` should be an existing user Resource
+name `mailboxNameToBeCreated` should not be empty, nor contain # & % *
+characters.
+
+Response codes:
+
+* 204: The mailbox now exists on the server
+* 400: Invalid mailbox name
+* 404: The user name does not exist
+
+To create nested mailboxes, for instance a work mailbox inside the INBOX
+mailbox, people should use the . separator. The sample query is:
+
+....
+curl -XDELETE http://ip:port/users/{usernameToBeUsed}/mailboxes/INBOX.work
+....
+
+=== Deleting a mailbox and its children
+
+....
+curl -XDELETE http://ip:port/users/{usernameToBeUsed}/mailboxes/{mailboxNameToBeDeleted}
+....
+
+Resource name `usernameToBeUsed` should be an existing user Resource
+name `mailboxNameToBeDeleted` should not be empty
+
+Response codes:
+
+* 204: The mailbox now does not exist on the server
+* 400: Invalid mailbox name
+* 404: The user name does not exist
+
+=== Testing existence of a mailbox
+
+....
+curl -XGET http://ip:port/users/{usernameToBeUsed}/mailboxes/{mailboxNameToBeTested}
+....
+
+Resource name `usernameToBeUsed` should be an existing user Resource
+name `mailboxNameToBeTested` should not be empty
+
+Response codes:
+
+* 204: The mailbox exists
+* 400: Invalid mailbox name
+* 404: The user name does not exist, the mailbox does not exist
+
+=== Listing user mailboxes
+
+....
+curl -XGET http://ip:port/users/{usernameToBeUsed}/mailboxes
+....
+
+The answer looks like:
+
+....
+[{"mailboxName":"INBOX"},{"mailboxName":"outbox"}]
+....
+
+Resource name `usernameToBeUsed` should be an existing user
+
+Response codes:
+
+* 200: The mailboxes list was successfully retrieved
+* 404: The user name does not exist
+
+=== Deleting user mailboxes
+
+....
+curl -XDELETE http://ip:port/users/{usernameToBeUsed}/mailboxes
+....
+
+Resource name `usernameToBeUsed` should be an existing user
+
+Response codes:
+
+* 204: The user do not have mailboxes anymore
+* 404: The user name does not exist
+
+=== Exporting user mailboxes
+
+....
+curl -XPOST http://ip:port/users/{usernameToBeUsed}/mailboxes?action=export
+....
+
+Resource name `usernameToBeUsed` should be an existing user
+
+Response codes:
+
+* 201: Success. Corresponding task id is returned
+* 404: The user name does not exist
+
+The scheduled task will have the following type `MailboxesExportTask`
+and the following `additionalInformation`:
+
+....
+{
+  "type":"MailboxesExportTask",
+  "timestamp":"2007-12-03T10:15:30Z",
+  "username": "user",
+  "stage": "STARTING"
+}
+....
+
+=== ReIndexing a user mails
+
+....
+curl -XPOST http://ip:port/users/{usernameToBeUsed}/mailboxes?task=reIndex
+....
+
+Will schedule a task for reIndexing all the mails in ``user@domain.com''
+mailboxes (encoded above).
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+An admin can specify the concurrency that should be used when running
+the task:
+
+* `messagesPerSecond` rate at which messages should be processed per
+second. Default is 50.
+
+This optional parameter must have a strictly positive integer as a value
+and be passed as query parameter.
+
+An admin can also specify the reindexing mode it wants to use when
+running the task:
+
+* `mode` the reindexing mode used. There are 2 modes for the moment:
+** `rebuildAll` allows to rebuild all indexes. This is the default mode.
+** `fixOutdated` will check for outdated indexed document and reindex
+only those.
+
+This optional parameter must be passed as query parameter.
+
+It’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).
+
+Example:
+
+....
+curl -XPOST http://ip:port/users/{usernameToBeUsed}/mailboxes?task=reIndex&messagesPerSecond=200&mode=fixOutdated
+....
+
+Response codes:
+
+* 201: Success. Corresponding task id is returned.
+* 400: Error in the request. Details can be found in the reported error.
+
+The scheduled task will have the following type `user-reindexing` and
+the following `additionalInformation`:
+
+....
+{
+  "type":"user-reindexing",
+  "runningOptions":{
+    "messagesPerSecond":200,
+    "mode":"FIX_OUTDATED"
+  },
+  "user":"user@domain.com",
+  "successfullyReprocessedMailCount":18,
+  "failedReprocessedMailCount": 3,
+  "mailboxFailures": ["12", "23" ],
+  "messageFailures": [
+   {
+     "mailboxId": "1",
+      "uids": [1, 36]
+   }]
+}
+....
+
+Warning: During the re-indexing, the result of search operations might
+be altered.
+
+Warning: Canceling this task should be considered unsafe as it will
+leave the currently reIndexed mailbox as partially indexed.
+
+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.
+
+=== Recomputing User JMAP fast message view projection
+
+This action is only available for backends supporting JMAP protocol.
+
+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.
+
+These projection items are asynchronously computed on mailbox events.
+
+You can force the full projection recomputation by calling the following
+endpoint:
+
+....
+curl -XPOST /users/{usernameToBeUsed}/mailboxes?task=recomputeFastViewProjectionItems
+....
+
+Will schedule a task for recomputing the fast message view projection
+for all mailboxes of `usernameToBeUsed`.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+An admin can specify the concurrency that should be used when running
+the task:
+
+* `messagesPerSecond` rate at which messages should be processed, per
+second. Defaults to 10.
+
+This optional parameter must have a strictly positive integer as a value
+and be passed as query parameters.
+
+Example:
+
+....
+curl -XPOST /mailboxes?task=recomputeFastViewProjectionItems&messagesPerSecond=20
+....
+
+The scheduled task will have the following type
+`RecomputeUserFastViewProjectionItemsTask` and the following
+`additionalInformation`:
+
+....
+{
+  "type":"RecomputeUserFastViewProjectionItemsTask",
+  "username": "{usernameToBeUsed}",
+  "processedMessageCount": 3,
+  "failedMessageCount": 1,
+  "runningOptions": {
+    "messagesPerSecond":20
+  }
+}
+....
+
+Response codes:
+
+* 201: Success. Corresponding task id is returned.
+* 400: Error in the request. Details can be found in the reported error.
+* 404: User not found.
+
+== Administrating quotas by users
+
+=== Getting the quota for a user
+
+....
+curl -XGET http://ip:port/quota/users/{usernameToBeUsed}
+....
+
+Resource name `usernameToBeUsed` should be an existing user
+
+The answer is the details of the quota of that user.
+
+....
+{
+  "global": {
+    "count":252,
+    "size":242
+  },
+  "domain": {
+    "count":152,
+    "size":142
+  },
+  "user": {
+    "count":52,
+    "size":42
+  },
+  "computed": {
+    "count":52,
+    "size":42
+  },
+  "occupation": {
+    "size":13,
+    "count":21,
+    "ratio": {
+      "size":0.25,
+      "count":0.5,
+      "max":0.5
+    }
+  }
+}
+....
+
+* The `global` entry represent the quota limit allowed on this James
+server.
+* The `domain` entry represent the quota limit allowed for the user of
+that domain.
+* The `user` entry represent the quota limit allowed for this specific
+user.
+* The `computed` entry represent the quota limit applied for this user,
+resolved from the upper values.
+* 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).
+
+Note that `quota` object can contain a fixed value, an empty value
+(null) or an unlimited value (-1):
+
+....
+{"count":52,"size":42}
+
+{"count":null,"size":null}
+
+{"count":52,"size":-1}
+....
+
+Response codes:
+
+* 200: The user’s quota was successfully retrieved
+* 404: The user does not exist
+
+=== Updating the quota for a user
+
+....
+curl -XPUT http://ip:port/quota/users/{usernameToBeUsed}
+....
+
+Resource name `usernameToBeUsed` should be an existing user
+
+The body can contain a fixed value, an empty value (null) or an
+unlimited value (-1):
+
+....
+{"count":52,"size":42}
+
+{"count":null,"size":null}
+
+{"count":52,"size":-1}
+....
+
+Response codes:
+
+* 204: The quota has been updated
+* 400: The body is not a positive integer neither an unlimited value
+(-1).
+* 404: The user does not exist
+
+=== Getting the quota count for a user
+
+....
+curl -XGET http://ip:port/quota/users/{usernameToBeUsed}/count
+....
+
+Resource name `usernameToBeUsed` should be an existing user
+
+The answer looks like:
+
+....
+52
+....
+
+Response codes:
+
+* 200: The user’s quota was successfully retrieved
+* 204: No quota count limit is defined at the user level for this user
+* 404: The user does not exist
+
+=== Updating the quota count for a user
+
+....
+curl -XPUT http://ip:port/quota/users/{usernameToBeUsed}/count
+....
+
+Resource name `usernameToBeUsed` should be an existing user
+
+The body can contain a fixed value or an unlimited value (-1):
+
+....
+52
+....
+
+Response codes:
+
+* 204: The quota has been updated
+* 400: The body is not a positive integer neither an unlimited value
+(-1).
+* 404: The user does not exist
+
+=== Deleting the quota count for a user
+
+....
+curl -XDELETE http://ip:port/quota/users/{usernameToBeUsed}/count
+....
+
+Resource name `usernameToBeUsed` should be an existing user
+
+Response codes:
+
+* 204: The quota has been updated to unlimited value.
+* 404: The user does not exist
+
+=== Getting the quota size for a user
+
+....
+curl -XGET http://ip:port/quota/users/{usernameToBeUsed}/size
+....
+
+Resource name `usernameToBeUsed` should be an existing user
+
+The answer looks like:
+
+....
+52
+....
+
+Response codes:
+
+* 200: The user’s quota was successfully retrieved
+* 204: No quota size limit is defined at the user level for this user
+* 404: The user does not exist
+
+=== Updating the quota size for a user
+
+....
+curl -XPUT http://ip:port/quota/users/{usernameToBeUsed}/size
+....
+
+Resource name `usernameToBeUsed` should be an existing user
+
+The body can contain a fixed value or an unlimited value (-1):
+
+....
+52
+....
+
+Response codes:
+
+* 204: The quota has been updated
+* 400: The body is not a positive integer neither an unlimited value
+(-1).
+* 404: The user does not exist
+
+=== Deleting the quota size for a user
+
+....
+curl -XDELETE http://ip:port/quota/users/{usernameToBeUsed}/size
+....
+
+Resource name `usernameToBeUsed` should be an existing user
+
+Response codes:
+
+* 204: The quota has been updated to unlimited value.
+* 404: The user does not exist
+
+=== Searching user by quota ratio
+
+....
+curl -XGET 'http://ip:port/quota/users?minOccupationRatio=0.8&maxOccupationRatio=0.99&limit=100&offset=200&domain=domain.com'
+....
+
+Will return:
+
+....
+[
+  {
+    "username":"user@domain.com",
+    "detail": {
+      "global": {
+        "count":252,
+        "size":242
+      },
+      "domain": {
+        "count":152,
+        "size":142
+      },
+      "user": {
+        "count":52,
+        "size":42
+      },
+      "computed": {
+        "count":52,
+        "size":42
+      },
+      "occupation": {
+        "size":48,
+        "count":21,
+        "ratio": {
+          "size":0.9230,
+          "count":0.5,
+          "max":0.9230
+        }
+      }
+    }
+  },
+  ...
+]
+....
+
+Where:
+
+* *minOccupationRatio* is a query parameter determining the minimum
+occupation ratio of users to be returned.
+* *maxOccupationRatio* is a query parameter determining the maximum
+occupation ratio of users to be returned.
+* *domain* is a query parameter determining the domain of users to be
+returned.
+* *limit* is a query parameter determining the maximum number of users
+to be returned.
+* *offset* is a query parameter determining the number of users to skip.
+
+Please note that users are alphabetically ordered on username.
+
+The response is a list of usernames, with attached quota details as
+defined link:#getting-the-quota-for-a-user[here].
+
+Response codes:
+
+* 200: List of users had successfully been returned.
+* 400: Validation issues with parameters
+
+=== Recomputing current quotas for users
+
+....
+curl -XPOST /quota/users?task=RecomputeCurrentQuotas
+....
+
+Will recompute current quotas (count and size) for all users stored in
+James.
+
+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.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+An admin can specify the concurrency that should be used when running
+the task:
+
+* `usersPerSecond` rate at which users quotas should be reprocessed, per
+second. Defaults to 1.
+
+This optional parameter must have a strictly positive integer as a value
+and be passed as query parameters.
+
+Example:
+
+....
+curl -XPOST /quota/users?task=RecomputeCurrentQuotas&usersPerSecond=20
+....
+
+The scheduled task will have the following type
+`recompute-current-quotas` and the following `additionalInformation`:
+
+....
+{
+  "type":"recompute-current-quotas",
+  "processedQuotaRoots": 3,
+  "failedQuotaRoots": ["#private&bob@localhost"],
+  "runningOptions": {
+    "usersPerSecond":20
+  }
+}
+....
+
+*WARNING*: this task do not take into account concurrent modifications
+upon a single current quota re-computation. Rerunning the task will
+_eventually_ provide the consistent result.
+
+== Administrating quotas by domains
+
+=== Getting the quota for a domain
+
+....
+curl -XGET http://ip:port/quota/domains/{domainToBeUsed}
+....
+
+Resource name `domainToBeUsed` should be an existing domain. For
+example:
+
+....
+curl -XGET http://ip:port/quota/domains/james.org
+....
+
+The answer will detail the default quota applied to users belonging to
+that domain:
+
+....
+{
+  "global": {
+    "count":252,
+    "size":null
+  },
+  "domain": {
+    "count":null,
+    "size":142
+  },
+  "computed": {
+    "count":252,
+    "size":142
+  }
+}
+....
+
+* The `global` entry represents the quota limit defined on this James
+server by default.
+* The `domain` entry represents the quota limit allowed for the user of
+that domain by default.
+* The `computed` entry represents the quota limit applied for the users
+of that domain, by default, resolved from the upper values.
+
+Note that `quota` object can contain a fixed value, an empty value
+(null) or an unlimited value (-1):
+
+....
+{"count":52,"size":42}
+
+{"count":null,"size":null}
+
+{"count":52,"size":-1}
+....
+
+Response codes:
+
+* 200: The domain’s quota was successfully retrieved
+* 404: The domain does not exist
+* 405: Domain Quota configuration not supported when virtual hosting is
+desactivated.
+
+=== Updating the quota for a domain
+
+....
+curl -XPUT http://ip:port/quota/domains/{domainToBeUsed}
+....
+
+Resource name `domainToBeUsed` should be an existing domain.
+
+The body can contain a fixed value, an empty value (null) or an
+unlimited value (-1):
+
+....
+{"count":52,"size":42}
+
+{"count":null,"size":null}
+
+{"count":52,"size":-1}
+....
+
+Response codes:
+
+* 204: The quota has been updated
+* 400: The body is not a positive integer neither an unlimited value
+(-1).
+* 404: The domain does not exist
+* 405: Domain Quota configuration not supported when virtual hosting is
+desactivated.
+
+=== Getting the quota count for a domain
+
+....
+curl -XGET http://ip:port/quota/domains/{domainToBeUsed}/count
+....
+
+Resource name `domainToBeUsed` should be an existing domain.
+
+The answer looks like:
+
+....
+52
+....
+
+Response codes:
+
+* 200: The domain’s quota was successfully retrieved
+* 204: No quota count limit is defined at the domain level for this
+domain
+* 404: The domain does not exist
+* 405: Domain Quota configuration not supported when virtual hosting is
+desactivated.
+
+=== Updating the quota count for a domain
+
+....
+curl -XPUT http://ip:port/quota/domains/{domainToBeUsed}/count
+....
+
+Resource name `domainToBeUsed` should be an existing domain.
+
+The body can contain a fixed value or an unlimited value (-1):
+
+....
+52
+....
+
+Response codes:
+
+* 204: The quota has been updated
+* 400: The body is not a positive integer neither an unlimited value
+(-1).
+* 404: The domain does not exist
+* 405: Domain Quota configuration not supported when virtual hosting is
+desactivated.
+
+=== Deleting the quota count for a domain
+
+....
+curl -XDELETE http://ip:port/quota/domains/{domainToBeUsed}/count
+....
+
+Resource name `domainToBeUsed` should be an existing domain.
+
+Response codes:
+
+* 204: The quota has been updated to unlimited value.
+* 404: The domain does not exist
+* 405: Domain Quota configuration not supported when virtual hosting is
+desactivated.
+
+=== Getting the quota size for a domain
+
+....
+curl -XGET http://ip:port/quota/domains/{domainToBeUsed}/size
+....
+
+Resource name `domainToBeUsed` should be an existing domain.
+
+The answer looks like:
+
+....
+52
+....
+
+Response codes:
+
+* 200: The domain’s quota was successfully retrieved
+* 204: No quota size limit is defined at the domain level for this
+domain
+* 404: The domain does not exist
+* 405: Domain Quota configuration not supported when virtual hosting is
+desactivated.
+
+=== Updating the quota size for a domain
+
+....
+curl -XPUT http://ip:port/quota/domains/{domainToBeUsed}/size
+....
+
+Resource name `domainToBeUsed` should be an existing domain.
+
+The body can contain a fixed value or an unlimited value (-1):
+
+....
+52
+....
+
+Response codes:
+
+* 204: The quota has been updated
+* 400: The body is not a positive integer neither an unlimited value
+(-1).
+* 404: The domain does not exist
+* 405: Domain Quota configuration not supported when virtual hosting is
+desactivated.
+
+=== Deleting the quota size for a domain
+
+....
+curl -XDELETE http://ip:port/quota/domains/{domainToBeUsed}/size
+....
+
+Resource name `domainToBeUsed` should be an existing domain.
+
+Response codes:
+
+* 204: The quota has been updated to unlimited value.
+* 404: The domain does not exist
+
+== Administrating global quotas
+
+=== Getting the global quota
+
+....
+curl -XGET http://ip:port/quota
+....
+
+The answer is the details of the global quota.
+
+....
+{
+  "count":252,
+  "size":242
+}
+....
+
+Note that `quota` object can contain a fixed value, an empty value
+(null) or an unlimited value (-1):
+
+....
+{"count":52,"size":42}
+
+{"count":null,"size":null}
+
+{"count":52,"size":-1}
+....
+
+Response codes:
+
+* 200: The quota was successfully retrieved
+
+=== Updating global quota
+
+....
+curl -XPUT http://ip:port/quota
+....
+
+The body can contain a fixed value, an empty value (null) or an
+unlimited value (-1):
+
+....
+{"count":52,"size":42}
+
+{"count":null,"size":null}
+
+{"count":52,"size":-1}
+....
+
+Response codes:
+
+* 204: The quota has been updated
+* 400: The body is not a positive integer neither an unlimited value
+(-1).
+
+=== Getting the global quota count
+
+....
+curl -XGET http://ip:port/quota/count
+....
+
+Resource name usernameToBeUsed should be an existing user
+
+The answer looks like:
+
+....
+52
+....
+
+Response codes:
+
+* 200: The quota was successfully retrieved
+* 204: No quota count limit is defined at the global level
+
+=== Updating the global quota count
+
+....
+curl -XPUT http://ip:port/quota/count
+....
+
+The body can contain a fixed value or an unlimited value (-1):
+
+....
+52
+....
+
+Response codes:
+
+* 204: The quota has been updated
+* 400: The body is not a positive integer neither an unlimited value
+(-1).
+
+=== Deleting the global quota count
+
+....
+curl -XDELETE http://ip:port/quota/count
+....
+
+Response codes:
+
+* 204: The quota has been updated to unlimited value.
+
+=== Getting the global quota size
+
+....
+curl -XGET http://ip:port/quota/size
+....
+
+The answer looks like:
+
+....
+52
+....
+
+Response codes:
+
+* 200: The quota was successfully retrieved
+* 204: No quota size limit is defined at the global level
+
+=== Updating the global quota size
+
+....
+curl -XPUT http://ip:port/quota/size
+....
+
+The body can contain a fixed value or an unlimited value (-1):
+
+....
+52
+....
+
+Response codes:
+
+* 204: The quota has been updated
+* 400: The body is not a positive integer neither an unlimited value
+(-1).
+
+=== Deleting the global quota size
+
+....
+curl -XDELETE http://ip:port/quota/size
+....
+
+Response codes:
+
+* 204: The quota has been updated to unlimited value.
+
+== Cassandra Schema upgrades
+
+Cassandra upgrades implies the creation of a new table. Thus restarting
+James is needed, as new tables are created on restart.
+
+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.
+
+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…
+
+These schema updates can be triggered by webadmin using the Cassandra
+backend.
+
+Note that currently the progress can be tracked by logs.
+
+* link:#Retrieving_current_Cassandra_schema_version[Retrieving current
+Cassandra schema version]
+* link:#Retrieving_latest_available_Cassandra_schema_version[Retrieving
+latest available Cassandra schema version]
+* link:#Upgrading_to_a_specific_version[Upgrading to a specific version]
+* link:#Upgrading_to_the_latest_version[Upgrading to the latest version]
+
+=== Retrieving current Cassandra schema version
+
+....
+curl -XGET http://ip:port/cassandra/version
+....
+
+Will return:
+
+....
+{"version": 2}
+....
+
+Where the number corresponds to the current schema version of the
+database you are using.
+
+Response codes:
+
+* 200: Success
+
+=== Retrieving latest available Cassandra schema version
+
+....
+curl -XGET http://ip:port/cassandra/version/latest
+....
+
+Will return:
+
+....
+{"version": 3}
+....
+
+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.
+
+Response codes:
+
+* 200: Success
+
+=== Upgrading to a specific version
+
+....
+curl -XPOST http://ip:port/cassandra/version/upgrade -d '3'
+....
+
+Will schedule the run of the migrations you need to reach schema version
+3.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+Response codes:
+
+* 200: Success. The scheduled task `taskId` is returned.
+* 400: The version is invalid. The version should be a strictly positive
+number.
+* 410: Error while planning this migration. This resource is gone away.
+Reason is mentionned in the body.
+
+Note that several calls to this endpoint will be run in a sequential
+pattern.
+
+If the server restarts during the migration, the migration is silently
+aborted.
+
+The scheduled task will have the following type `cassandra-migration`
+and the following `additionalInformation`:
+
+....
+{"targetVersion":3}
+....
+
+=== Upgrading to the latest version
+
+....
+curl -XPOST http://ip:port/cassandra/version/upgrade/latest
+....
+
+Will schedule the run of the migrations you need to reach the latest
+schema version.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+Response codes:
+
+* 200: Success. The scheduled task `taskId` is returned.
+* 410: Error while planning this migration. This resource is gone away.
+Reason is mentionned in the body.
+
+Note that several calls to this endpoint will be run in a sequential
+pattern.
+
+If the server restarts during the migration, the migration is silently
+aborted.
+
+The scheduled task will have the following type `cassandra-migration`
+and the following `additionalInformation`:
+
+....
+{"toVersion":2}
+....
+
+== Correcting ghost mailbox
+
+This is a temporary workaround for the *Ghost mailbox* bug encountered
+using the Cassandra backend, as described in MAILBOX-322.
+
+You can use the mailbox merging feature in order to merge the old
+``ghosted'' mailbox with the new one.
+
+....
+curl -XPOST http://ip:port/cassandra/mailbox/merging \
+  -d '{"mergeOrigin":"{id1}", "mergeDestination":"{id2}"}' \
+  -H "Content-Type: application/json"
+....
+
+Will scedule a task for :
+
+* Delete references to `id1` mailbox
+* Move it’s messages into `id2` mailbox
+* Union the rights of both mailboxes
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+Response codes:
+
+* 201: Task generation succeeded. Corresponding task id is returned.
+* 400: Unable to parse the body.
+
+The scheduled task will have the following type `mailbox-merging` and
+the following `additionalInformation`:
+
+....
+{
+  "oldMailboxId":"5641376-02ed-47bd-bcc7-76ff6262d92a",
+  "newMailboxId":"4555159-52ae-895f-ccb7-586a4412fb50",
+  "totalMessageCount": 1,
+  "messageMovedCount": 1,
+  "messageFailedCount": 0
+}
+....
+
+== Creating address group
+
+You can use *webadmin* to define address groups.
+
+When a specific email is sent to the group mail address, every group
+member will receive it.
+
+Note that the group mail address is virtual: it does not correspond to
+an existing user.
+
+This feature uses (TODO)
+link:/server/config-recipientrewritetable.html[Recipients rewrite table]
+and requires the
+https://github.com/apache/james-project/blob/master/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java[RecipientRewriteTable
+mailet] to be configured.
+
+Note that email addresses are restricted to ASCII character set. Mail
+addresses not matching this criteria will be rejected.
+
+=== Listing groups
+
+....
+curl -XGET http://ip:port/address/groups
+....
+
+Will return the groups as a list of JSON Strings representing mail
+addresses. For instance:
+
+....
+["group1@domain.com", "group2@domain.com"]
+....
+
+Response codes:
+
+* 200: Success
+
+=== Listing members of a group
+
+....
+curl -XGET http://ip:port/address/groups/group@domain.com
+....
+
+Will return the group members as a list of JSON Strings representing
+mail addresses. For instance:
+
+....
+["member1@domain.com", "member2@domain.com"]
+....
+
+Response codes:
+
+* 200: Success
+* 400: Group structure is not valid
+* 404: The group does not exist
+
+=== Adding a group member
+
+....
+curl -XPUT http://ip:port/address/groups/group@domain.com/member@domain.com
+....
+
+Will add member@domain.com to group@domain.com, creating the group if
+needed
+
+Response codes:
+
+* 204: Success
+* 400: Group structure or member is not valid
+* 400: Domain in the source is not managed by the DomainList
+* 409: Requested group address is already used for another purpose
+
+=== Removing a group member
+
+....
+curl -XDELETE http://ip:port/address/groups/group@domain.com/member@domain.com
+....
+
+Will remove member@domain.com from group@domain.com, removing the group
+if group is empty after deletion
+
+Response codes:
+
+* 204: Success
+* 400: Group structure or member is not valid
+
+== Creating address forwards
+
+You can use *webadmin* to define address forwards.
+
+When a specific email is sent to the base mail address, every forward
+destination addresses will receive it.
+
+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.
+
+Forwards can be defined for existing users. It then defers from
+``groups''.
+
+This feature uses (TODO)
+link:/server/config-recipientrewritetable.html[Recipients rewrite table]
+and requires the
+https://github.com/apache/james-project/blob/master/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java[RecipientRewriteTable
+mailet] to be configured.
+
+Note that email addresses are restricted to ASCII character set. Mail
+addresses not matching this criteria will be rejected.
+
+=== Listing Forwards
+
+....
+curl -XGET http://ip:port/address/forwards
+....
+
+Will return the users having forwards configured as a list of JSON
+Strings representing mail addresses. For instance:
+
+....
+["user1@domain.com", "user2@domain.com"]
+....
+
+Response codes:
+
+* 200: Success
+
+=== Listing destinations in a forward
+
+....
+curl -XGET http://ip:port/address/forwards/user@domain.com
+....
+
+Will return the destination addresses of this forward as a list of JSON
+Strings representing mail addresses. For instance:
+
+....
+[
+  {"mailAddress":"destination1@domain.com"},
+  {"mailAddress":"destination2@domain.com"}
+]
+....
+
+Response codes:
+
+* 200: Success
+* 400: Forward structure is not valid
+* 404: The given user don’t have forwards or does not exist
+
+=== Adding a new destination to a forward
+
+....
+curl -XPUT http://ip:port/address/forwards/user@domain.com/targets/destination@domain.com
+....
+
+Will add destination@domain.com to user@domain.com, creating the forward
+if needed
+
+Response codes:
+
+* 204: Success
+* 400: Forward structure or member is not valid
+* 400: Domain in the source is not managed by the DomainList
+* 404: Requested forward address does not match an existing user
+
+=== Removing a destination of a forward
+
+....
+curl -XDELETE http://ip:port/address/forwards/user@domain.com/targets/destination@domain.com
+....
+
+Will remove destination@domain.com from user@domain.com, removing the
+forward if forward is empty after deletion
+
+Response codes:
+
+* 204: Success
+* 400: Forward structure or member is not valid
+
+== Creating address aliases
+
+You can use *webadmin* to define aliases for an user.
+
+When a specific email is sent to the alias address, the destination
+address of the alias will receive it.
+
+Aliases can be defined for existing users.
+
+This feature uses (TODO)
+link:/server/config-recipientrewritetable.html[Recipients rewrite table]
+and requires the
+https://github.com/apache/james-project/blob/master/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java[RecipientRewriteTable
+mailet] to be configured.
+
+Note that email addresses are restricted to ASCII character set. Mail
+addresses not matching this criteria will be rejected.
+
+=== Listing users with aliases
+
+....
+curl -XGET http://ip:port/address/aliases
+....
+
+Will return the users having aliases configured as a list of JSON
+Strings representing mail addresses. For instance:
+
+....
+["user1@domain.com", "user2@domain.com"]
+....
+
+Response codes:
+
+* 200: Success
+
+=== Listing alias sources of an user
+
+....
+curl -XGET http://ip:port/address/aliases/user@domain.com
+....
+
+Will return the aliases of this user as a list of JSON Strings
+representing mail addresses. For instance:
+
+....
+[
+  {"source":"alias1@domain.com"},
+  {"source":"alias2@domain.com"}
+]
+....
+
+Response codes:
+
+* 200: Success
+* 400: Alias structure is not valid
+
+=== Adding a new alias to an user
+
+....
+curl -XPUT http://ip:port/address/aliases/user@domain.com/sources/alias@domain.com
+....
+
+Will add alias@domain.com to user@domain.com, creating the alias if
+needed
+
+Response codes:
+
+* 204: OK
+* 400: Alias structure or member is not valid
+* 400: The alias source exists as an user already
+* 400: Source and destination can’t be the same!
+* 400: Domain in the destination or source is not managed by the
+DomainList
+
+=== Removing an alias of an user
+
+....
+curl -XDELETE http://ip:port/address/aliases/user@domain.com/sources/alias@domain.com
+....
+
+Will remove alias@domain.com from user@domain.com, removing the alias if
+needed
+
+Response codes:
+
+* 204: OK
+* 400: Alias structure or member is not valid
+
+== Creating domain mappings
+
+You can use *webadmin* to define domain mappings.
+
+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.
+
+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`
+
+This feature uses (TODO)
+link:/server/config-recipientrewritetable.html[Recipients rewrite table]
+and requires the
+https://github.com/apache/james-project/blob/master/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java[RecipientRewriteTable
+mailet] to be configured.
+
+Note that email addresses are restricted to ASCII character set. Mail
+addresses not matching this criteria will be rejected.
+
+=== Listing all domain mappings
+
+....
+curl -XGET http://ip:port/domainMappings
+....
+
+Will return all configured domain mappings
+
+....
+{
+  "firstSource.org" : ["firstDestination.com", "secondDestination.net"],
+  "secondSource.com" : ["thirdDestination.com", "fourthDestination.net"],
+}
+....
+
+Response codes:
+
+* 200: OK
+
+=== Listing all destination domains for a source domain
+
+....
+curl -XGET http://ip:port/domainMappings/sourceDomain.tld
+....
+
+With `sourceDomain.tld` as the value passed to `fromDomain` resource
+name, the API will return all destination domains configured to that
+domain
+
+....
+["firstDestination.com", "secondDestination.com"]
+....
+
+Response codes:
+
+* 200: OK
+* 400: The `fromDomain` resource name is invalid
+* 404: The `fromDomain` resource name is not found
+
+=== Adding a domain mapping
+
+....
+curl -XPUT http://ip:port/domainMappings/sourceDomain.tld
+....
+
+Body:
+
+....
+destination.tld
+....
+
+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
+
+Response codes:
+
+* 204: OK
+* 400: The `fromDomain` resource name is invalid
+* 400: The destination domain specified in the body is invalid
+
+=== Removing a domain mapping
+
+....
+curl -XDELETE http://ip:port/domainMappings/sourceDomain.tld
+....
+
+Body:
+
+....
+destination.tld
+....
+
+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
+
+Response codes:
+
+* 204: OK
+* 400: The `fromDomain` resource name is invalid
+* 400: The destination domain specified in the body is invalid
+
+== Creating regex mapping
+
+You can use *webadmin* to create regex mappings.
+
+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.
+
+This feature uses (TODO)
+link:/server/config-recipientrewritetable.html[Recipients rewrite table]
+and requires the
+https://github.com/apache/james-project/blob/master/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java[RecipientRewriteTable
+API] to be configured.
+
+=== Adding a regex mapping
+
+....
+POST /mappings/regex/mappingSource/targets/regex
+....
+
+Where:
+
+* the `mappingSource` is the path parameter represents for the Regex
+Mapping mapping source
+* the `regex` is the path parameter represents for the Regex Mapping
+regex
+
+The route will add a regex mapping made from `mappingSource` and `regex`
+to RecipientRewriteTable.
+
+Example:
+
+....
+curl -XPOST http://ip:port/mappings/regex/james@domain.tld/targets/james@.*:james-intern@james.org
+....
+
+Response codes:
+
+* 204: Mapping added successfully.
+* 400: Invalid `mappingSource` path parameter.
+* 400: Invalid `regex` path parameter.
+
+=== Removing a regex mapping
+
+....
+DELETE /mappings/regex/{mappingSource}/targets/{regex}
+....
+
+Where:
+
+* the `mappingSource` is the path parameter representing the Regex
+Mapping mapping source
+* the `regex` is the path parameter representing the Regex Mapping regex
+
+The route will remove the regex mapping made from `regex` from the
+mapping source `mappingSource` to RecipientRewriteTable.
+
+Example:
+
+....
+curl -XDELETE http://ip:port/mappings/regex/james@domain.tld/targets/[O_O]:james-intern@james.org
+....
+
+Response codes:
+
+* 204: Mapping deleted successfully.
+* 400: Invalid `mappingSource` path parameter.
+* 400: Invalid `regex` path parameter.
+
+== Address Mappings
+
+You can use *webadmin* to define address mappings.
+
+When a specific email is sent to the base mail address, every
+destination addresses will receive it.
+
+This feature uses (TODO)
+link:/server/config-recipientrewritetable.html[Recipients rewrite table]
+and requires the
+https://github.com/apache/james-project/blob/master/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java[RecipientRewriteTable
+mailet] to be configured.
+
+Note that email addresses are restricted to ASCII character set. Mail
+addresses not matching this criteria will be rejected.
+
+Please use address mappings with caution, as it’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.
+
+Here are the following actions available on address mappings:
+
+=== Add an address mapping
+
+....
+curl -XPOST http://ip:port/mappings/address/{mappingSource}/targets/{destinationAddress}
+....
+
+Add an address mapping to the Recipients rewrite table
+Mapping source is the value of \{mappingSource} Mapping destination is
+the value of \{destinationAddress} Type of mapping destination is
+Address
+
+Response codes:
+
+* 204: Action successfully performed
+* 400: Invalid parameters
+
+=== Remove an address mapping
+
+....
+curl -XDELETE http://ip:port/mappings/address/{mappingSource}/targets/{destinationAddress}
+....
+
+* Remove an address mapping from the Recipients rewrite table
+* Mapping source is the value of `mappingSource`
+* Mapping destination is the value of `destinationAddress`
+* Type of mapping destination is Address
+
+Response codes:
+
+* 204: Action successfully performed
+* 400: Invalid parameters
+
+== List all mappings
+
+....
+curl -XGET http://ip:port/mappings
+....
+
+Get all mappings from the
+link:/server/config-recipientrewritetable.html[Recipients rewrite table]
+Supported mapping types are the following:
+
+(TODO)
+
+* link:#Creating_address_aliases[Alias]
+* link:#Address_Mappings[Address]
+* link:#Creating_address_domain[Domain]
+* Error
+* link:#Creating_address_forwards[Forward]
+* link:#Creating_address_group[Group]
+* Regex
+
+Response body:
+
+....
+{
+  "alias@domain.tld": [
+    {
+      "type": "Alias",
+      "mapping": "user@domain.tld"
+    },
+    {
+      "type": "Group",
+      "mapping": "group-user@domain.tld"
+    }
+  ],
+  "aliasdomain.tld": [
+    {
+      "type": "Domain",
+      "mapping": "realdomain.tld"
+    }
+  ],
+  "group@domain.tld": [
+    {
+      "type": "Address",
+      "mapping": "user@domain.tld"
+    }
+  ]
+}
+....
+
+Response code:
+
+* 200: OK
+
+== User Mappings
+
+=== Listing User Mappings
+
+This endpoint allows receiving all mappings of a corresponding user.
+
+....
+curl -XGET http://ip:port/mappings/user/{userAddress}
+....
+
+Return all mappings of a user where:
+
+* `userAddress`: is the selected user
+
+Response body:
+
+....
+[
+  {
+    "type": "Address",
+    "mapping": "user123@domain.tld"
+  },
+  {
+    "type": "Alias",
+    "mapping": "aliasuser123@domain.tld"
+  },
+  {
+    "type": "Group",
+    "mapping": "group123@domain.tld"
+  }
+]
+....
+
+Response codes:
+
+* 200: OK
+* 400: Invalid parameter value
+
+== Administrating mail repositories
+
+=== Create a mail repository
+
+....
+curl -XPUT http://ip:port/mailRepositories/{encodedPathOfTheRepository}?protocol={someProtocol}
+....
+
+Resource name `encodedPathOfTheRepository` should be the resource path
+of the created mail repository. Example:
+
+....
+curl -XPUT http://ip:port/mailRepositories/mailRepo?protocol=file
+....
+
+Response codes:
+
+* 204: The repository is created
+
+=== Listing mail repositories
+
+....
+curl -XGET http://ip:port/mailRepositories
+....
+
+The answer looks like:
+
+....
+[
+    {
+        "repository": "var/mail/error/",
+        "path": "var%2Fmail%2Ferror%2F"
+    },
+    {
+        "repository": "var/mail/relay-denied/",
+        "path": "var%2Fmail%2Frelay-denied%2F"
+    },
+    {
+        "repository": "var/mail/spam/",
+        "path": "var%2Fmail%2Fspam%2F"
+    },
+    {
+        "repository": "var/mail/address-error/",
+        "path": "var%2Fmail%2Faddress-error%2F"
+    }
+]
+....
+
+You can use `id`, the encoded URL of the repository, to access it in
+later requests.
+
+Response codes:
+
+* 200: The list of mail repositories
+
+=== Getting additional information for a mail repository
+
+....
+curl -XGET http://ip:port/mailRepositories/{encodedPathOfTheRepository}
+....
+
+Resource name `encodedPathOfTheRepository` should be the resource path
+of an existing mail repository. Example:
+
+....
+curl -XGET http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F
+....
+
+The answer looks like:
+
+....
+{
+   "repository": "var/mail/error/",
+   "path": "mail%2Ferror%2F",
+   "size": 243
+}
+....
+
+Response codes:
+
+* 200: Additonnal information for that repository
+* 404: This repository can not be found
+
+=== Listing mails contained in a mail repository
+
+....
+curl -XGET http://ip:port/mailRepositories/{encodedPathOfTheRepository}/mails
+....
+
+Resource name `encodedPathOfTheRepository` should be the resource path
+of an existing mail repository. Example:
+
+....
+curl -XGET http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails
+....
+
+The answer will contains all mailKey contained in that repository.
+
+....
+[
+    "mail-key-1",
+    "mail-key-2",
+    "mail-key-3"
+]
+....
+
+Note that this can be used to read mail details.
+
+You can pass additional URL parameters to this call in order to limit
+the output: - A limit: no more elements than the specified limit will be
+returned. This needs to be strictly positive. If no value is specified,
+no limit will be applied. - An offset: allow to skip elements. This
+needs to be positive. Default value is zero.
+
+Example:
+
+....
+curl -XGET 'http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails?limit=100&offset=500'
+....
+
+Response codes:
+
+* 200: The list of mail keys contained in that mail repository
+* 400: Invalid parameters
+* 404: This repository can not be found
+
+=== Reading/downloading a mail details
+
+....
+curl -XGET http://ip:port/mailRepositories/{encodedPathOfTheRepository}/mails/mailKey
+....
+
+Resource name `encodedPathOfTheRepository` should be the resource path
+of an existing mail repository. Resource name `mailKey` should be the
+key of a mail stored in that repository. Example:
+
+....
+curl -XGET http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails/mail-key-1
+....
+
+If the Accept header in the request is ``application/json'', then the
+response looks like:
+
+....
+{
+    "name": "mail-key-1",
+    "sender": "sender@domain.com",
+    "recipients": ["recipient1@domain.com", "recipient2@domain.com"],
+    "state": "address-error",
+    "error": "A small message explaining what happened to that mail...",
+    "remoteHost": "111.222.333.444",
+    "remoteAddr": "127.0.0.1",
+    "lastUpdated": null
+}
+....
+
+If the Accept header in the request is ``message/rfc822'', then the
+response will be the _eml_ file itself.
+
+Additional query parameter `additionalFields` add the existing
+information to the response for the supported values (only work with
+``application/json'' Accept header):
+
+* attributes
+* headers
+* textBody
+* htmlBody
+* messageSize
+* perRecipientsHeaders
+
+....
+curl -XGET http://ip:port/mailRepositories/file%3A%2F%2Fvar%2Fmail%2Ferror%2F/mails/mail-key-1?additionalFields=attributes,headers,textBody,htmlBody,messageSize,perRecipientsHeaders
+....
+
+Give the following kind of response:
+
+....
+{
+    "name": "mail-key-1",
+    "sender": "sender@domain.com",
+    "recipients": ["recipient1@domain.com", "recipient2@domain.com"],
+    "state": "address-error",
+    "error": "A small message explaining what happened to that mail...",
+    "remoteHost": "111.222.333.444",
+    "remoteAddr": "127.0.0.1",
+    "lastUpdated": null,
+    "attributes": {
+      "name2": "value2",
+      "name1": "value1"
+    },
+    "perRecipientsHeaders": {
+      "third@party": {
+        "headerName1": [
+          "value1",
+          "value2"
+        ],
+        "headerName2": [
+          "value3",
+          "value4"
+        ]
+      }
+    },
+    "headers": {
+      "headerName4": [
+        "value6",
+        "value7"
+      ],
+      "headerName3": [
+        "value5",
+        "value8"
+      ]
+    },
+    "textBody": "My body!!",
+    "htmlBody": "My <em>body</em>!!",
+    "messageSize": 42424242
+}
+....
+
+Response codes:
+
+* 200: Details of the mail
+* 404: This repository or mail can not be found
+
+=== Removing a mail from a mail repository
+
+....
+curl -XDELETE http://ip:port/mailRepositories/{encodedPathOfTheRepository}/mails/mailKey
+....
+
+Resource name `encodedPathOfTheRepository` should be the resource path
+of an existing mail repository. Resource name `mailKey` should be the
+key of a mail stored in that repository. Example:
+
+....
+curl -XDELETE http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails/mail-key-1
+....
+
+Response codes:
+
+* 204: This mail no longer exists in this repository
+* 404: This repository can not be found
+
+=== Removing all mails from a mail repository
+
+....
+curl -XDELETE http://ip:port/mailRepositories/{encodedPathOfTheRepository}/mails
+....
+
+Resource name `encodedPathOfTheRepository` should be the resource path
+of an existing mail repository. Example:
+
+....
+curl -XDELETE http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails
+....
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+Response codes:
+
+* 201: Task generation succeeded. Corresponding task id is returned.
+* 404: Could not find that mail repository
+
+The scheduled task will have the following type `clear-mail-repository`
+and the following `additionalInformation`:
+
+....
+{
+  "mailRepositoryPath":"var/mail/error/",
+  "initialCount": 243,
+  "remainingCount": 17
+}
+....
+
+=== Reprocessing mails from a mail repository
+
+Sometime, you want to re-process emails stored in a mail repository. For
+instance, you can make a configuration error, or there can be a James
+bug that makes processing of some mails fail. Those mail will be stored
+in a mail repository. Once you solved the problem, you can reprocess
+them.
+
+To reprocess mails from a repository:
+
+....
+curl -XPATCH http://ip:port/mailRepositories/{encodedPathOfTheRepository}/mails?action=reprocess
+....
+
+Resource name `encodedPathOfTheRepository` should be the resource path
+of an existing mail repository. Example:
+
+For instance:
+
+....
+curl -XPATCH http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails?action=reprocess
+....
+
+Additional query parameters are supported: - `queue` allows you to
+target the mail queue you want to enqueue the mails in. Defaults to
+`spool`. - `processor` allows you to overwrite the state of the
+reprocessing mails, and thus select the processors they will start their
+processing in. Defaults to the `state` field of each processed email.
+
+For instance:
+
+....
+curl -XPATCH 'http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails?action=reprocess&processor=transport&queue=spool'
+....
+
+Note that the `action` query parameter is compulsary and can only take
+value `reprocess`.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+Response codes:
+
+* 201: Task generation succeeded. Corresponding task id is returned.
+* 404: Could not find that mail repository
+
+The scheduled task will have the following type `reprocessing-all` and
+the following `additionalInformation`:
+
+....
+{
+  "mailRepositoryPath":"var/mail/error/",
+  "targetQueue":"spool",
+  "targetProcessor":"transport",
+  "initialCount": 243,
+  "remainingCount": 17
+}
+....
+
+=== Reprocessing a specific mail from a mail repository
+
+To reprocess a specific mail from a mail repository:
+
+....
+curl -XPATCH http://ip:port/mailRepositories/{encodedPathOfTheRepository}/mails/mailKey?action=reprocess
+....
+
+Resource name `encodedPathOfTheRepository` should be the resource id of
+an existing mail repository. Resource name `mailKey` should be the key
+of a mail stored in that repository. Example:
+
+For instance:
+
+....
+curl -XPATCH http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails/name1?action=reprocess
+....
+
+Additional query parameters are supported: - `queue` allows you to
+target the mail queue you want to enqueue the mails in. Defaults to
+`spool`. - `processor` allows you to overwrite the state of the
+reprocessing mails, and thus select the processors they will start their
+processing in. Defaults to the `state` field of each processed email.
+
+While `processor` being an optional parameter, not specifying it will
+result reprocessing the mails in their current state
+(https://james.apache.org/server/feature-mailetcontainer.html#Processors[see
+documentation about processors and state]). Consequently, only few cases
+will give a different result, definitively storing them out of the mail
+repository.
+
+For instance:
+
+....
+curl -XPATCH 'http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails/name1?action=reprocess&processor=transport&queue=spool'
+....
+
+Note that the `action` query parameter is compulsary and can only take
+value `reprocess`.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+Response codes:
+
+* 201: Task generation succeeded. Corresponding task id is returned.
+* 404: Could not find that mail repository
+
+The scheduled task will have the following type `reprocessing-one` and
+the following `additionalInformation`:
+
+....
+{
+  "mailRepositoryPath":"var/mail/error/",
+  "targetQueue":"spool",
+  "targetProcessor":"transport",
+  "mailKey":"name1"
+}
+....
+
+== Administrating mail queues
+
+=== Listing mail queues
+
+....
+curl -XGET http://ip:port/mailQueues
+....
+
+The answer looks like:
+
+....
+["outgoing","spool"]
+....
+
+Response codes:
+
+* 200: The list of mail queues
+
+=== Getting a mail queue details
+
+....
+curl -XGET http://ip:port/mailQueues/{mailQueueName}
+....
+
+Resource name `mailQueueName` is the name of a mail queue, this command
+will return the details of the given mail queue. For instance:
+
+....
+{"name":"outgoing","size":0}
+....
+
+Response codes:
+
+* 200: Success
+* 400: Mail queue is not valid
+* 404: The mail queue does not exist
+
+=== Listing the mails of a mail queue
+
+....
+curl -XGET http://ip:port/mailQueues/{mailQueueName}/mails
+....
+
+Additional URL query parameters:
+
+* `limit`: Maximum number of mails returned in a single call. Only
+strictly positive integer values are accepted. Example:
+
+....
+curl -XGET http://ip:port/mailQueues/{mailQueueName}/mails?limit=100
+....
+
+The answer looks like:
+
+....
+[{
+  "name": "Mail1516976156284-8b3093b9-eebf-4c40-9c26-1450f4fcdc3c-to-test.com",
+  "sender": "user@james.linagora.com",
+  "recipients": ["someone@test.com"],
+  "nextDelivery": "1969-12-31T23:59:59.999Z"
+}]
+....
+
+Response codes:
+
+* 200: Success
+* 400: Mail queue is not valid or limit is invalid
+* 404: The mail queue does not exist
+
+=== Deleting mails from a mail queue
+
+....
+curl -XDELETE http://ip:port/mailQueues/{mailQueueName}/mails?sender=senderMailAddress
+....
+
+This request should have exactly one query parameter from the following
+list:
+
+* sender: which is a mail address (i.e. sender@james.org)
+* name: which is a string
+* recipient: which is a mail address (i.e. recipient@james.org)
+
+The mails from the given mail queue matching the query parameter will be
+deleted.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+Response codes:
+
+* 201: Task generation succeeded. Corresponding task id is returned.
+* 400: Invalid request
+* 404: The mail queue does not exist
+
+The scheduled task will have the following type
+`delete-mails-from-mail-queue` and the following
+`additionalInformation`:
+
+....
+{
+  "queue":"outgoing",
+  "initialCount":10,
+  "remainingCount": 5,
+  "sender": "sender@james.org",
+  "name": "Java Developer",
+  "recipient: "recipient@james.org"
+}
+....
+
+=== Clearing a mail queue
+
+....
+curl -XDELETE http://ip:port/mailQueues/{mailQueueName}/mails
+....
+
+All mails from the given mail queue will be deleted.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+Response codes:
+
+* 201: Task generation succeeded. Corresponding task id is returned.
+* 400: Invalid request
+* 404: The mail queue does not exist
+
+The scheduled task will have the following type `clear-mail-queue` and
+the following `additionalInformation`:
+
+....
+{
+  "queue":"outgoing",
+  "initialCount":10,
+  "remainingCount": 0
+}
+....
+
+=== Flushing mails from a mail queue
+
+....
+curl -XPATCH http://ip:port/mailQueues/{mailQueueName}?delayed=true \
+  -d '{"delayed": false}' \
+  -H "Content-Type: application/json"
+....
+
+This request should have the query parameter _delayed_ set to _true_, in
+order to indicate only delayed mails are affected. The payload should
+set the `delayed` field to false inorder to remove the delay. This is
+the only supported combination, and it performs a flush.
+
+The mails delayed in the given mail queue will be flushed.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+Response codes:
+
+* 204: Success (No content)
+* 400: Invalid request
+* 404: The mail queue does not exist
+
+== Administrating DLP Configuration
+
+DLP (stands for Data Leak Prevention) is supported by James. A DLP
+matcher will, on incoming emails, execute regular expressions on email
+sender, recipients or content, in order to report suspicious emails to
+an administrator. WebAdmin can be used to manage these DLP rules on a
+per `senderDomain` basis.
+
+`senderDomain` is domain of the sender of incoming emails, for example:
+`apache.org`, `james.org`,… Each `senderDomain` correspond to a distinct
+DLP configuration.
+
+=== List DLP configuration by sender domain
+
+Retrieve a DLP configuration for corresponding `senderDomain`, a
+configuration contains list of configuration items
+
+....
+curl -XGET http://ip:port/dlp/rules/{senderDomain}
+....
+
+Response codes:
+
+* 200: A list of dlp configuration items is returned
+* 400: Invalid `senderDomain` or payload in request
+* 404: The domain does not exist.
+
+This is an example of returned body. The rules field is a list of rules
+as described below.
+
+....
+{"rules : [
+  {
+    "id": "1",
+    "expression": "james.org",
+    "explanation": "Find senders or recipients containing james[any char]org",
+    "targetsSender": true,
+    "targetsRecipients": true,
+    "targetsContent": false
+  },
+  {
+    "id": "2",
+    "expression": "Find senders containing apache[any char]org",
+    "explanation": "apache.org",
+    "targetsSender": true,
+    "targetsRecipients": false,
+    "targetsContent": false
+  }
+]}
+....
+
+=== Store DLP configuration by sender domain
+
+Store a DLP configuration for corresponding `senderDomain`, if any item
+of DLP configuration in the request is stored before, it will not be
+stored anymore
+
+....
+curl -XPUT http://ip:port/dlp/rules/{senderDomain}
+....
+
+The body can contain a list of DLP configuration items formed by those
+fields: - `id`(String) is mandatory, unique identifier of the
+configuration item - `expression`(String) is mandatory, regular
+expression to match contents of targets - `explanation`(String) is
+optional, description of the configuration item -
+`targetsSender`(boolean) is optional and defaults to false. If true,
+`expression` will be applied to Sender and to From headers of the mail -
+`targetsContent`(boolean) is optional and defaults to false. If true,
+`expression` will be applied to Subject headers and textual bodies
+(text/plain and text/html) of the mail - `targetsRecipients`(boolean) is
+optional and defaults to false. If true, `expression` will be applied to
+recipients of the mail
+
+This is an example of returned body. The rules field is a list of rules
+as described below.
+
+....
+{"rules": [
+  {
+    "id": "1",
+    "expression": "james.org",
+    "explanation": "Find senders or recipients containing james[any char]org",
+    "targetsSender": true,
+    "targetsRecipients": true,
+    "targetsContent": false
+  },
+  {
+    "id": "2",
+    "expression": "Find senders containing apache[any char]org",
+    "explanation": "apache.org",
+    "targetsSender": true,
+    "targetsRecipients": false,
+    "targetsContent": false
+  }
+]}
+....
+
+Response codes:
+
+* 204: List of dlp configuration items is stored
+* 400: Invalid `senderDomain` or payload in request
+* 404: The domain does not exist.
+
+=== Remove DLP configuration by sender domain
+
+Remove a DLP configuration for corresponding `senderDomain`
+
+....
+curl -XDELETE http://ip:port/dlp/rules/{senderDomain}
+....
+
+Response codes:
+
+* 204: DLP configuration is removed
+* 400: Invalid `senderDomain` or payload in request
+* 404: The domain does not exist.
+
+=== Fetch a DLP configuration item by sender domain and rule id
+
+Retrieve a DLP configuration rule for corresponding `senderDomain` and a
+`ruleId`
+
+....
+curl -XGET http://ip:port/dlp/rules/{senderDomain}/rules/{ruleId}
+....
+
+Response codes:
+
+* 200: A dlp configuration item is returned
+* 400: Invalid `senderDomain` or payload in request
+* 404: The domain and/or the rule does not exist.
+
+This is an example of returned body.
+
+....
+{
+  "id": "1",
+  "expression": "james.org",
+  "explanation": "Find senders or recipients containing james[any char]org",
+  "targetsSender": true,
+  "targetsRecipients": true,
+  "targetsContent": false
+}
+....
+
+== Administrating Sieve quotas
+
+Some limitations on space Users Sieve script can occupy can be
+configured by default, and overridden by user.
+
+=== Retrieving global sieve quota
+
+This endpoints allows to retrieve the global Sieve quota, which will be
+users default:
+
+....
+curl -XGET http://ip:port/sieve/quota/default
+....
+
+Will return the bytes count allowed by user per default on this server.
+
+....
+102400
+....
+
+Response codes:
+
+* 200: Request is a success and the value is returned
+* 204: No default quota is being configured
+
+=== Updating global sieve quota
+
+This endpoints allows to update the global Sieve quota, which will be
+users default:
+
+....
+curl -XPUT http://ip:port/sieve/quota/default
+....
+
+With the body being the bytes count allowed by user per default on this
+server.
+
+....
+102400
+....
+
+Response codes:
+
+* 204: Operation succeeded
+* 400: Invalid payload
+
+=== Removing global sieve quota
+
+This endpoints allows to remove the global Sieve quota. There will no
+more be users default:
+
+....
+curl -XDELETE http://ip:port/sieve/quota/default
+....
+
+Response codes:
+
+* 204: Operation succeeded
+
+=== Retrieving user sieve quota
+
+This endpoints allows to retrieve the Sieve quota of a user, which will
+be this users quota:
+
+....
+curl -XGET http://ip:port/sieve/quota/users/user@domain.com
+....
+
+Will return the bytes count allowed for this user.
+
+....
+102400
+....
+
+Response codes:
+
+* 200: Request is a success and the value is returned
+* 204: No quota is being configured for this user
+
+=== Updating user sieve quota
+
+This endpoints allows to update the Sieve quota of a user, which will be
+users default:
+
+....
+curl -XPUT http://ip:port/sieve/quota/users/user@domain.com
+....
+
+With the body being the bytes count allowed for this user on this
+server.
+
+....
+102400
+....
+
+Response codes:
+
+* 204: Operation succeeded
+* 400: Invalid payload
+
+=== Removing user sieve quota
+
+This endpoints allows to remove the Sieve quota of a user. There will no
+more quota for this user:
+
+....
+curl -XDELETE http://ip:port/sieve/quota/users/user@domain.com
+....
+
+Response codes:
+
+* 204: Operation succeeded
+
+== Event Dead Letter
+
+The EventBus allows to register `group listeners' that are called in a
+distributed fashion. These group listeners enable the implementation of
+some advanced mailbox manager feature like indexing, spam reporting,
+quota management and the like.
+
+Upon exceptions, a bounded number of retries are performed (with
+exponential backoff delays). If after those retries the listener is
+still failing, then the event will be stored in the ``Event Dead
+Letter''. This API allows diagnosing issues, as well as performing event
+replay.
+
+=== Listing mailbox listener groups
+
+This endpoint allows discovering the list of mailbox listener groups.
+
+....
+curl -XGET http://ip:port/events/deadLetter/groups
+....
+
+Will return a list of group names that can be further used to interact
+with the dead letter API:
+
+....
+["org.apache.james.mailbox.events.EventBusTestFixture$GroupA", "org.apache.james.mailbox.events.GenericGroup-abc"]
+....
+
+Response codes:
+
+* 200: Success. A list of group names is returned.
+
+=== Listing failed events
+
+This endpoint allows listing failed events for a given group:
+
+....
+curl -XGET http://ip:port/events/deadLetter/groups/org.apache.james.mailbox.events.EventBusTestFixture$GroupA
+....
+
+Will return a list of insertionIds:
+
+....
+["6e0dd59d-660e-4d9b-b22f-0354479f47b4", "58a8f59d-660e-4d9b-b22f-0354486322a2"]
+....
+
+Response codes:
+
+* 200: Success. A list of insertion ids is returned.
+* 400: Invalid group name
+
+=== Getting event details
+
+....
+curl -XGET http://ip:port/events/deadLetter/groups/org.apache.james.mailbox.events.EventBusTestFixture$GroupA/6e0dd59d-660e-4d9b-b22f-0354479f47b4
+....
+
+Will return the full JSON associated with this event.
+
+Response codes:
+
+* 200: Success. A JSON representing this event is returned.
+* 400: Invalid group name or `insertionId`
+* 404: No event with this `insertionId`
+
+=== Deleting an event
+
+....
+curl -XDELETE http://ip:port/events/deadLetter/groups/org.apache.james.mailbox.events.EventBusTestFixture$GroupA/6e0dd59d-660e-4d9b-b22f-0354479f47b4
+....
+
+Will delete this event.
+
+Response codes:
+
+* 204: Success
+* 400: Invalid group name or `insertionId`
+
+=== Redeliver all events
+
+....
+curl -XPOST http://ip:port/events/deadLetter?action=redeliver
+....
+
+Will create a task that will attempt to redeliver all events stored in
+``Event Dead Letter''. If successful, redelivered events will then be
+removed from ``Dead Letter''.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+Response codes:
+
+* 201: the taskId of the created task
+* 400: Invalid action argument
+
+=== Redeliver group events
+
+....
+curl -XPOST http://ip:port/events/deadLetter/groups/org.apache.james.mailbox.events.EventBusTestFixture$GroupA
+....
+
+Will create a task that will attempt to redeliver all events of a
+particular group stored in ``Event Dead Letter''. If successful,
+redelivered events will then be removed from ``Dead Letter''.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+Response codes:
+
+* 201: the taskId of the created task
+* 400: Invalid group name or action argument
+
+=== Redeliver a single event
+
+....
+curl -XPOST http://ip:port/events/deadLetter/groups/org.apache.james.mailbox.events.EventBusTestFixture$GroupA/6e0dd59d-660e-4d9b-b22f-0354479f47b4?action=reDeliver
+....
+
+Will create a task that will attempt to redeliver a single event of a
+particular group stored in ``Event Dead Letter''. If successful,
+redelivered event will then be removed from ``Dead Letter''.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+Response codes:
+
+* 201: the taskId of the created task
+* 400: Invalid group name, insertion id or action argument
+* 404: No event with this insertionId
+
+== Deleted Messages Vault
+
+The `Deleted Message Vault plugin' allows you to keep users deleted
+messages during a given retention time. This set of routes allow you to
+_restore_ users deleted messages or export them in an archive.
+
+To move deleted messages in the vault, you need to specifically
+configure the DeletedMessageVault PreDeletionHook.
+
+=== Restore Deleted Messages
+
+Deleted messages of a specific user can be restored by calling the
+following endpoint:
+
+....
+curl -XPOST http://ip:port/deletedMessages/users/userToRestore@domain.ext?action=restore
+
+{
+  "combinator": "and",
+  "criteria": [
+    {
+      "fieldName": "subject",
+      "operator": "containsIgnoreCase",
+      "value": "Apache James"
+    },
+    {
+      "fieldName": "deliveryDate",
+      "operator": "beforeOrEquals",
+      "value": "2014-10-30T14:12:00Z"
+    },
+    {
+      "fieldName": "deletionDate",
+      "operator": "afterOrEquals",
+      "value": "2015-10-20T09:08:00Z"
+    },
+    {
+      "fieldName": "recipients","
+      "operator": "contains","
+      "value": "recipient@james.org"
+    },
+    {
+      "fieldName": "hasAttachment",
+      "operator": "equals",
+      "value": "false"
+    },
+    {
+      "fieldName": "sender",
+      "operator": "equals",
+      "value": "sender@apache.org"
+    },
+    {
+      "fieldName": "originMailboxes",
+      "operator": "contains",
+      "value":  "02874f7c-d10e-102f-acda-0015176f7922"
+    }
+  ]
+};
+....
+
+The requested Json body is made from a list of criterion objects which
+have the following structure:
+
+....
+{
+  "fieldName": "supportedFieldName",
+  "operator": "supportedOperator",
+  "value": "A plain string representing the matching value of the corresponding field"
+}
+....
+
+Deleted Messages which are matched with the *all* criterion in the query
+body will be restored. Here are a list of supported fieldName for the
+restoring:
+
+* subject: represents for deleted message `subject` field matching.
+Supports below string operators:
+** contains
+** containsIgnoreCase
+** equals
+** equalsIgnoreCase
+* deliveryDate: represents for deleted message `deliveryDate` field
+matching. Tested value should follow the right date time with zone
+offset format (ISO-8601) like `2008-09-15T15:53:00+05:00` or
+`2008-09-15T15:53:00Z` Supports below date time operators:
+** beforeOrEquals: is the deleted message’s `deliveryDate` before or
+equals the time of tested value.
+** afterOrEquals: is the deleted message’s `deliveryDate` after or
+equals the time of tested value
+* deletionDate: represents for deleted message `deletionDate` field
+matching. Tested value & Supports operators: similar to `deliveryDate`
+* sender: represents for deleted message `sender` field matching. Tested
+value should be a valid mail address. Supports mail address operator:
+** equals: does the tested sender equal to the sender of the tested
+deleted message ? +
+* recipients: represents for deleted message `recipients` field
+matching. Tested value should be a valid mail address. Supports list
+mail address operator:
+** contains: does the tested deleted message’s recipients contain tested
+recipient ?
+* hasAttachment: represents for deleted message `hasAttachment` field
+matching. Tested value could be `false` or `true`. Supports boolean
+operator:
+** equals: does the tested deleted message’s hasAttachment property
+equal to the tested hasAttachment value?
+* originMailboxes: represents for deleted message `originMailboxes`
+field matching. Tested value is a string serialized of mailbox id.
+Supports list mailbox id operators:
+** contains: does the tested deleted message’s originMailbox ids contain
+tested mailbox id ?
+
+Messages in the Deleted Messages Vault of a specified user that are
+matched with Query Json Object in the body will be appended to his
+`Restored-Messages' mailbox, which will be created if needed.
+
+*Note*:
+
+* Query parameter `action` is required and should have the value
+`restore` to represent the restoring feature. Otherwise, a bad request
+response will be returned
+* Query parameter `action` is case sensitive
+* fieldName & operator passed to the routes are case sensitive
+* Currently, we only support query combinator `and` value, otherwise,
+requests will be rejected
+* If you only want to restore by only one criterion, the json body could
+be simplified to a single criterion:
+
+....
+{
+  "fieldName": "subject",
+  "operator": "containsIgnoreCase",
+  "value": "Apache James"
+}
+....
+
+* For restoring all deleted messages, passing a query json with an empty
+criterion list to represent `matching all deleted messages`:
+
+....
+{
+  "combinator": "and",
+  "criteria": []
+}
+....
+
+*Warning*: Current web-admin uses `US` locale as the default. Therefore,
+there might be some conflicts when using String `containsIgnoreCase`
+comparators to apply on the String data of other special locales stored
+in the Vault. More details at
+https://issues.apache.org/jira/browse/MAILBOX-384[JIRA]
+
+Response code:
+
+* 201: Task for restoring deleted has been created
+* 400: Bad request:
+** action query param is not present
+** action query param is not a valid action
+** user parameter is invalid
+** can not parse the JSON body
+** Json query object contains unsupported operator, fieldName
+** Json query object values violate parsing rules
+* 404: User not found
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+The scheduled task will have the following type
+`deleted-messages-restore` and the following `additionalInformation`:
+
+....
+{
+  "successfulRestoreCount": 47,
+  "errorRestoreCount": 0,
+  "user": "userToRestore@domain.ext"
+}
+....
+
+while:
+
+* successfulRestoreCount: number of restored messages
+* errorRestoreCount: number of messages that failed to restore
+* user: owner of deleted messages need to restore
+
+=== Export Deleted Messages
+
+Retrieve deleted messages matched with requested query from an user then
+share the content to a targeted mail address (exportTo)
+
+....
+curl -XPOST 'http://ip:port/deletedMessages/users/userExportFrom@domain.ext?action=export&exportTo=userReceiving@domain.ext'
+
+BODY: is the json query has the same structure with Restore Deleted Messages section
+....
+
+*Note*: Json query passing into the body follows the same rules &
+restrictions like in link:#Restore_deleted_messages[Restore Deleted
+Messages]
+
+Response code:
+
+* 201: Task for exporting has been created
+* 400: Bad request:
+** exportTo query param is not present
+** exportTo query param is not a valid mail address
+** action query param is not present
+** action query param is not a valid action
+** user parameter is invalid
+** can not parse the JSON body
+** Json query object contains unsupported operator, fieldName
+** Json query object values violate parsing rules
+* 404: User not found
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+The scheduled task will have the following type
+`deleted-messages-export` and the following `additionalInformation`:
+
+....
+{
+  "userExportFrom": "userToRestore@domain.ext",
+  "exportTo": "userReceiving@domain.ext",
+  "totalExportedMessages": 1432
+}
+....
+
+while:
+
+ * userExportFrom: export deleted messages from this user
+ * exportTo: content of deleted messages have been shared to this mail
+address
+ * totalExportedMessages: number of deleted messages match with
+json query, then being shared to sharee.
+
+=== Purge Deleted Messages
+
+You can overwrite `retentionPeriod' configuration in
+`deletedMessageVault' configuration file or use the default value of 1
+year.
+
+Purge all deleted messages older than the configured `retentionPeriod'
+
+....
+curl -XDELETE http://ip:port/deletedMessages?scope=expired
+....
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+Response code:
+
+* 201: Task for purging has been created
+* 400: Bad request:
+** action query param is not present
+** action query param is not a valid action
+
+You may want to call this endpoint on a regular basis.
+
+=== Permanently Remove Deleted Message
+
+Delete a Deleted Message with `MessageId`
+
+....
+curl -XDELETE http://ip:port/deletedMessages/users/user@domain.ext/messages/3294a976-ce63-491e-bd52-1b6f465ed7a2
+....
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+Response code:
+
+* 201: Task for deleting message has been created
+* 400: Bad request:
+** user parameter is invalid
+** messageId parameter is invalid
+* 404: User not found
+
+The scheduled task will have the following type
+`deleted-messages-delete` and the following `additionalInformation`:
+
+....
+ {
+   "userName": "user@domain.ext",
+   "messageId": "3294a976-ce63-491e-bd52-1b6f465ed7a2"
+ }
+....
+
+while: - user: delete deleted messages from this user - deleteMessageId:
+messageId of deleted messages will be delete
+
+== Task management
+
+Some webadmin features schedules tasks. The task management API allow to
+monitor and manage the execution of the following tasks.
+
+Note that the `taskId` used in the following APIs is returned by other
+WebAdmin APIs scheduling tasks.
+
+=== Getting a task details
+
+....
+curl -XGET http://ip:port/tasks/3294a976-ce63-491e-bd52-1b6f465ed7a2
+....
+
+An Execution Report will be returned:
+
+....
+{
+    "submitDate": "2017-12-27T15:15:24.805+0700",
+    "startedDate": "2017-12-27T15:15:24.809+0700",
+    "completedDate": "2017-12-27T15:15:24.815+0700",
+    "cancelledDate": null,
+    "failedDate": null,
+    "taskId": "3294a976-ce63-491e-bd52-1b6f465ed7a2",
+    "additionalInformation": {},
+    "status": "completed",
+    "type": "type-of-the-task"
+}
+....
+
+Note that:
+
+* `status` can have the value:
+** `waiting`: The task is scheduled but its execution did not start yet
+** `inProgress`: The task is currently executed
+** `cancelled`: The task had been cancelled
+** `completed`: The task execution is finished, and this execution is a
+success
+** `failed`: The task execution is finished, and this execution is a
+failure
+* `additionalInformation` is a task specific object giving additional
+information and context about that task. The structure of this
+`additionalInformation` field is provided along the specific task
+submission endpoint.
+
+Response codes:
+
+* 200: The specific task was found and the execution report exposed
+above is returned
+* 400: Invalid task ID
+* 404: Task ID was not found
+
+=== Awaiting a task
+
+One can await the end of a task, then receive it’s final execution
+report.
+
+That feature is especially usefull for testing purpose but still can
+serve real-life scenari.
+
+....
+curl -XGET http://ip:port/tasks/3294a976-ce63-491e-bd52-1b6f465ed7a2/await?timeout=duration
+....
+
+An Execution Report will be returned.
+
+`timeout` is optional. By default it is set to 365 days (the maximum
+value). The expected value is expressed in the following format:
+`Nunit`. `N` should be strictly positive. `unit` could be either in the
+short form (`s`, `m`, `h`, etc.), or in the long form (`day`, `week`,
+`month`, etc.).
+
+Examples:
+
+* `30s`
+* `5m`
+* `7d`
+* `1y`
+
+Response codes:
+
+* 200: The specific task was found and the execution report exposed
+above is returned
+* 400: Invalid task ID or invalid timeout
+* 404: Task ID was not found
+* 408: The timeout has been reached
+
+=== Cancelling a task
+
+You can cancel a task by calling:
+
+....
+curl -XDELETE http://ip:port/tasks/3294a976-ce63-491e-bd52-1b6f465ed7a2
+....
+
+Response codes:
+
+* 204: Task had been cancelled
+* 400: Invalid task ID
+
+=== Listing tasks
+
+A list of all tasks can be retrieved:
+
+....
+curl -XGET http://ip:port/tasks
+....
+
+Will return a list of Execution reports
+
+One can filter the above results by status. For example:
+
+....
+curl -XGET http://ip:port/tasks?status=inProgress
+....
+
+Will return a list of Execution reports that are currently in progress.
+
+Response codes:
+
+* 200: A list of corresponding tasks is returned
+* 400: Invalid status value
+
+=== Endpoints returning a task
+
+Many endpoints do generate a task.
+
+Example:
+
+....
+curl -XPOST /endpoint?action={action}
+....
+
+The response to these requests will be the scheduled `taskId` :
+
+....
+{"taskId":"5641376-02ed-47bd-bcc7-76ff6262d92a"}
+....
+
+Positionned headers:
+
+* Location header indicates the location of the resource associated with
+the scheduled task. Example:
+
+....
+Location: /tasks/3294a976-ce63-491e-bd52-1b6f465ed7a2
+....
+
+Response codes:
+
+* 201: Task generation succeeded. Corresponding task id is returned.
+* Other response codes might be returned depending on the endpoint
+
+The additional information returned depends on the scheduled task type
+and is documented in the endpoint documentation.
+
+== Cassandra extra operations
+
+Some webadmin features to manage some extra operations on Cassandra
+tables, like solving inconsistencies on projection tables. Such
+inconsistencies can be for example created by a fail of the DAO to add a
+mapping into
+’mappings_sources`, while it was successful regarding the`rrt` table.
+
+=== Operations on mappings sources
+
+You can do a series of action on `mappings_sources` projection table :
+
+....
+curl -XPOST /cassandra/mappings?action={action}
+....
+
+Will return the taskId corresponding to the related task. Actions
+supported so far are :
+
+* SolveInconsistencies : cleans up first all the mappings in
+`mappings_sources` index and then repopulate it correctly. In the
+meantime, listing sources of a mapping might create temporary
+inconsistencies during the process.
+
+For example :
+
+....
+curl -XPOST /cassandra/mappings?action=SolveInconsistencies
+....
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+Response codes :
+
+* 201: the taskId of the created task
+* 400: Invalid action argument for performing operation on mappings data
\ No newline at end of file
diff --git a/docs/modules/servers/pages/distributed/run-docker.adoc b/docs/modules/servers/pages/distributed/run-docker.adoc
index d3fd897..2646ae1 100644
--- a/docs/modules/servers/pages/distributed/run-docker.adoc
+++ b/docs/modules/servers/pages/distributed/run-docker.adoc
@@ -1,6 +1,36 @@
 = Run with docker
 
-== Requirements
+== Running via docker-compose
+
+
+Requirements: docker & docker-compose installed.
+
+When you try James this way, you will use the most current state of James.
+It will be configured to run with Cassandra & ElasticSearch.
+All those three components will be started with a single command.
+
+You can retrieve the docker-compose file :
+
+    $ wget https://raw.githubusercontent.com/apache/james-project/master/dockerfiles/run/docker-compose.yml
+
+Then, you just have to start the services:
+
+    $ docker-compose up
+
+Wait a few seconds in order to have all those services start up. You will see the following log when James is available:
+james           | Started : true
+
+A default domain, james.local, has been created. You can see this by running:
+
+    $ docker exec james java -jar /root/james-cli.jar -h 127.0.0.1 -p 9999 listdomains
+
+James will respond to IMAP port 143 and SMTP port 25.
+You have to create users before playing with james. You may also want to create other domains.
+Follow the 'Useful commands' section for more information about James CLI.
+
+== Run with docker
+
+=== Requirements
 
 Built artifacts should be in ./dockerfiles/run/guice/cassandra-rabbitmq/destination folder for cassandra.
 If you haven't already:
@@ -10,7 +40,7 @@ If you haven't already:
   -v $PWD/dockerfiles/run/guice/cassandra-rabbitmq/destination:/cassandra-rabbitmq/destination \
   -t james/project -s HEAD
 
-== Running
+=== Running
 
 You need a running *cassandra* in docker. To achieve this run:
 
@@ -62,7 +92,7 @@ If you want to pass additional options to the underlying java command, you can c
 
 To have log file accessible on a volume, add *-v  $PWD/logs:/logs* option to the above command line, where *$PWD/logs* is your local directory to put files in.
 
-== Instrumentation
+=== Instrumentation
 You can use Glowroot to instrumentalize James. The provided guice docker files allow a simple way to do it.
 In order to activate Glowroot you need to run the container with the environment variable _GLOWROOT_ACTIVATED_ set to _true_
 and to expose the glowroot instrumentation ui port.
@@ -76,7 +106,7 @@ See the https://github.com/glowroot/glowroot/wiki/Agent-Installation-(with-Embed
 Or by mapping the 4000 port to the IP of the desired network interface, for example `-p 127.0.0.1:4000:4000`.
 
 
-== Handling attachment indexing
+=== Handling attachment indexing
 
 You can handle attachment text extraction before indexing in ElasticSearch. This makes attachments searchable. To enable this:
 


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org