diff --git a/libs/client/ArchiveJob.cpp b/libs/client/ArchiveJob.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..99f432fb27aada0bc14e36477cc3da0de8d7f43a
--- /dev/null
+++ b/libs/client/ArchiveJob.cpp
@@ -0,0 +1,50 @@
+#include "ArchiveJob.hpp"
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+cta::ArchiveJob::ArchiveJob():
+  m_creationTime(time(NULL)) {
+}
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+cta::ArchiveJob::ArchiveJob(
+  const std::string &id,
+  const UserIdentity &creator,
+  const std::string &comment):
+  m_id(id),
+  m_creationTime(time(NULL)),
+  m_creator(creator),
+  m_comment(comment) {
+}
+
+//------------------------------------------------------------------------------
+// getId
+//------------------------------------------------------------------------------
+const std::string &cta::ArchiveJob::getId() const throw() {
+  return m_id;
+}
+
+//------------------------------------------------------------------------------
+// getCreationTime
+//------------------------------------------------------------------------------
+time_t cta::ArchiveJob::getCreationTime() const throw() {
+  return m_creationTime;
+}
+
+//------------------------------------------------------------------------------
+// getCreator
+//------------------------------------------------------------------------------
+const cta::UserIdentity &cta::ArchiveJob::getCreator()
+  const throw() {
+  return m_creator;
+}
+
+//------------------------------------------------------------------------------
+// getComment
+//------------------------------------------------------------------------------
+const std::string &cta::ArchiveJob::getComment() const throw() {
+  return m_comment;
+}
diff --git a/libs/client/ArchiveJob.hpp b/libs/client/ArchiveJob.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..40cc06576539fff28a54fee3e039586752fafc74
--- /dev/null
+++ b/libs/client/ArchiveJob.hpp
@@ -0,0 +1,84 @@
+#pragma once
+
+#include "UserIdentity.hpp"
+
+#include <string>
+
+namespace cta {
+
+/**
+ * Class representing a  archive job.
+ */
+class ArchiveJob {
+public:
+
+  /**
+   * Constructor.
+   */
+  ArchiveJob();
+
+  /**
+   * Constructor.
+   *
+   * @param id The identification string of the archive job.
+   * @param creator The identity of the user that created the archive job.
+   * @param comment The comment describing the archive job.
+   */
+  ArchiveJob(
+    const std::string &id,
+    const UserIdentity &creator,
+    const std::string &comment);
+
+  /**
+   * Returns the identification string of the archive job.
+   *
+   * @return The identification string of the archive job.
+   */
+  const std::string &getId() const throw();
+
+  /**
+   * Returns the time when the archive job was created.
+   *
+   * @return The time when the archive job was created.
+   */
+  time_t getCreationTime() const throw();
+
+  /**
+   * Returns the identity of the user that created the archive job.
+   *
+   * @return The identity of the user that created the archive job.
+   */
+  const UserIdentity &getCreator() const throw();
+
+  /**
+   * Returns the comment describing the archive job.
+   *
+   * @return The comment describing the archive job.
+   */
+  const std::string &getComment() const throw();
+
+private:
+
+  /**
+   * The identification string of the archive job.
+   */
+  std::string m_id;
+
+  /**
+   * The time when the archive job was created.
+   */
+  time_t m_creationTime;
+
+  /**
+   * The identity of the user that created the archive job.
+   */
+  UserIdentity m_creator;
+
+  /**
+   * Comment describing the archive job.
+   */
+  std::string m_comment;
+
+}; // class ArchiveJob
+
+} // namespace cta
diff --git a/libs/client/CMakeLists.txt b/libs/client/CMakeLists.txt
index 549629a74ad35d8bdedb293469d502c399272898..92fbac3f94862aa583a3a819436b13fbfddefbc7 100644
--- a/libs/client/CMakeLists.txt
+++ b/libs/client/CMakeLists.txt
@@ -1,6 +1,7 @@
 cmake_minimum_required (VERSION 2.6)
 
 set (CLIENT_LIB_SRC_FILES
+  ArchiveJob.cpp
   ClientAPI.cpp
   DirectoryEntry.cpp
   DirectoryIterator.cpp
diff --git a/libs/client/ClientAPI.hpp b/libs/client/ClientAPI.hpp
index ee7b2cdc68dbd2700f6e1ca568a3001c5d1e1f22..ce6747b17302df948f299de5b6a988d9b3b741b9 100644
--- a/libs/client/ClientAPI.hpp
+++ b/libs/client/ClientAPI.hpp
@@ -264,7 +264,7 @@ public:
    */
   virtual std::string getDirectoryStorageClass(
     const SecurityIdentity &requester,
-    const std::string &dirPath) = 0;
+    const std::string &dirPath) const = 0;
 
   /**
    * Archives the specified list of source files to the specified destination
@@ -282,12 +282,12 @@ public:
    * @param requester The identity of the user requesting the archival.
    * @param srcUrls List of one or more source files.
    * @param dst Destination file or directory within the archive namespace.
-   * @return The identifier of the archive job.
+   * @return The string identifier of the archive job.
    */
-  virtual std::string archiveToTape(
+  virtual std::string archive(
     const SecurityIdentity &requester,
     const std::list<std::string> &srcUrls,
-    std::string dst) = 0;
+    const std::string &dst) = 0;
 
 }; // class ClientAPI
 
diff --git a/libs/client/DirectoryEntry.cpp b/libs/client/DirectoryEntry.cpp
index b3f646e8955d9ff88e864f19fcc22c1b9c859e84..bc9ac0d6127181fec2e23046b1532435f0778ea3 100644
--- a/libs/client/DirectoryEntry.cpp
+++ b/libs/client/DirectoryEntry.cpp
@@ -35,9 +35,9 @@ cta::DirectoryEntry::DirectoryEntry(const EntryType entryType,
 }
 
 //------------------------------------------------------------------------------
-// getEntryType
+// getType
 //------------------------------------------------------------------------------
-cta::DirectoryEntry::EntryType cta::DirectoryEntry::getEntryType()
+cta::DirectoryEntry::EntryType cta::DirectoryEntry::getType()
   const throw() {
   return m_entryType;
 }
diff --git a/libs/client/DirectoryEntry.hpp b/libs/client/DirectoryEntry.hpp
index b45463e42a8f92268c2c3174f52fbf257057f027..4854cdcf3196bfb04b2941516f7a4a1b5ff0d16d 100644
--- a/libs/client/DirectoryEntry.hpp
+++ b/libs/client/DirectoryEntry.hpp
@@ -51,7 +51,7 @@ public:
    *
    * @return The type of the directory entry.
    */
-  EntryType getEntryType() const throw();
+  EntryType getType() const throw();
 
   /**
    * Returns the name of the directory entry.
diff --git a/libs/client/MockClientAPI.cpp b/libs/client/MockClientAPI.cpp
index b99f4895d04191b596433da6ca294acdb3bcc311..14795cc73344bd9361ec145d862a1ca1f85e4053 100644
--- a/libs/client/MockClientAPI.cpp
+++ b/libs/client/MockClientAPI.cpp
@@ -278,7 +278,7 @@ void cta::MockClientAPI::createDirectory(const SecurityIdentity &requester,
 
   FileSystemNode &enclosingNode = getFileSystemNode(enclosingPath);
   if(DirectoryEntry::ENTRYTYPE_DIRECTORY !=
-    enclosingNode.getFileSystemEntry().getEntry().getEntryType()) {
+    enclosingNode.getFileSystemEntry().getEntry().getType()) {
     std::ostringstream message;
     message << enclosingPath << " is not a directory";
     throw Exception(message.str());
@@ -468,7 +468,7 @@ void cta::MockClientAPI::deleteDirectory(const SecurityIdentity &requester,
   FileSystemNode &dirNode = getFileSystemNode(dirPath);
 
   if(DirectoryEntry::ENTRYTYPE_DIRECTORY !=
-    dirNode.getFileSystemEntry().getEntry().getEntryType()) {
+    dirNode.getFileSystemEntry().getEntry().getType()) {
     std::ostringstream message;
     message << "The absolute path " << dirPath << " is not a directory";
     throw(message.str());
@@ -496,7 +496,7 @@ cta::DirectoryIterator cta::MockClientAPI::getDirectoryContents(
   const FileSystemNode &dirNode = getFileSystemNode(dirPath);
 
   if(DirectoryEntry::ENTRYTYPE_DIRECTORY !=
-    dirNode.getFileSystemEntry().getEntry().getEntryType()) {
+    dirNode.getFileSystemEntry().getEntry().getType()) {
     std::ostringstream message;
     message << "The absolute path " << dirPath << " is not a directory";
     throw(message.str());
@@ -568,7 +568,7 @@ void cta::MockClientAPI::setDirectoryStorageClass(
 
   FileSystemNode &dirNode = getFileSystemNode(dirPath);
   if(DirectoryEntry::ENTRYTYPE_DIRECTORY !=
-    dirNode.getFileSystemEntry().getEntry().getEntryType()) {
+    dirNode.getFileSystemEntry().getEntry().getType()) {
     std::ostringstream message;
     message << dirPath << " is not a directory";
     throw Exception(message.str());
@@ -585,7 +585,7 @@ void cta::MockClientAPI::clearDirectoryStorageClass(
   const std::string &dirPath) {
   FileSystemNode &dirNode = getFileSystemNode(dirPath);
   if(DirectoryEntry::ENTRYTYPE_DIRECTORY !=
-    dirNode.getFileSystemEntry().getEntry().getEntryType()) {
+    dirNode.getFileSystemEntry().getEntry().getType()) {
     std::ostringstream message;
     message << dirPath << " is not a directory";
     throw Exception(message.str());
@@ -599,10 +599,10 @@ void cta::MockClientAPI::clearDirectoryStorageClass(
 //------------------------------------------------------------------------------
 std::string cta::MockClientAPI::getDirectoryStorageClass(
   const SecurityIdentity &requester,
-  const std::string &dirPath) {
-  FileSystemNode &dirNode = getFileSystemNode(dirPath);
+  const std::string &dirPath) const {
+  const FileSystemNode &dirNode = getFileSystemNode(dirPath);
   if(DirectoryEntry::ENTRYTYPE_DIRECTORY !=
-    dirNode.getFileSystemEntry().getEntry().getEntryType()) {
+    dirNode.getFileSystemEntry().getEntry().getType()) {
     std::ostringstream message;
     message << dirPath << " is not a directory";
     throw Exception(message.str());
@@ -612,9 +612,72 @@ std::string cta::MockClientAPI::getDirectoryStorageClass(
 }
 
 //------------------------------------------------------------------------------
-// archiveToTape
+// archive
+//------------------------------------------------------------------------------
+std::string cta::MockClientAPI::archive(const SecurityIdentity &requester,
+  const std::list<std::string> &srcUrls, const std::string &dst) {
+  checkAbsolutePathSyntax(dst);
+  if(isAnExistingDirectory(dst)) {
+    return archiveToDirectory(requester, srcUrls, dst);
+  } else {
+    return archiveToFile(requester, srcUrls, dst);
+  }
+}
+
+//------------------------------------------------------------------------------
+// isAnExistingDirectory
+//------------------------------------------------------------------------------
+bool cta::MockClientAPI::isAnExistingDirectory(const std::string &path)
+  const throw() {
+  try {
+    const FileSystemNode &node = getFileSystemNode(path);
+    const DirectoryEntry &entry = node.getFileSystemEntry().getEntry();
+    if(DirectoryEntry::ENTRYTYPE_DIRECTORY == entry.getType()) {
+      return true;
+    }
+  } catch(...) {
+  }
+  return false;
+}
+
+//------------------------------------------------------------------------------
+// archiveToDirectory
+//------------------------------------------------------------------------------
+std::string cta::MockClientAPI::archiveToDirectory(
+  const SecurityIdentity &requester,
+  const std::list<std::string> &srcUrls,
+  const std::string &dstDir) {
+  if(0 == srcUrls.size()) {
+    throw Exception("At least one source file should be provided");
+  }
+
+  FileSystemNode &dstDirNode = getFileSystemNode(dstDir);
+  checkUserIsAuthorisedToArchive(requester, dstDirNode);
+  return "Funny_archive_to_dir_ID";
+}
+
+//------------------------------------------------------------------------------
+// archiveToFile
+//------------------------------------------------------------------------------
+std::string cta::MockClientAPI::archiveToFile(
+  const SecurityIdentity &requester,
+  const std::list<std::string> &srcUrls,
+  const std::string &dstFile) {
+  if(1 != srcUrls.size()) {
+    throw Exception("One and only one source file must be provided when "
+      "archiving to a single destination file");
+  }
+
+  FileSystemNode &enclosingDirNode = getFileSystemNode(dstFile);
+  checkUserIsAuthorisedToArchive(requester, enclosingDirNode);
+  return "Funny_archive_to_file_ID";
+}
+
+//------------------------------------------------------------------------------
+// checkUserIsAuthorisedToArchive
 //------------------------------------------------------------------------------
-std::string cta::MockClientAPI::archiveToTape(const SecurityIdentity &requester,
-  const std::list<std::string> &srcUrls, std::string dst) {
-  return "Funny_Job_ID";
+void cta::MockClientAPI::checkUserIsAuthorisedToArchive(
+  const SecurityIdentity &user,
+  const FileSystemNode &dstDir) {
+  // TO BE DONE
 }
diff --git a/libs/client/MockClientAPI.hpp b/libs/client/MockClientAPI.hpp
index 7bc792deeda9025af283fed95fd3477e5d887e0e..2a7689ce9f71b626e36ace92e586d10c0e1856c0 100644
--- a/libs/client/MockClientAPI.hpp
+++ b/libs/client/MockClientAPI.hpp
@@ -266,7 +266,7 @@ public:
    */
   std::string getDirectoryStorageClass(
     const SecurityIdentity &requester,
-    const std::string &dirPath);
+    const std::string &dirPath) const;
 
   /**
    * Archives the specified list of source files to the specified destination
@@ -284,12 +284,12 @@ public:
    * @param requester The identity of the user requesting the archival.
    * @param srcUrls List of one or more source files.
    * @param dst Destination file or directory within the archive namespace.
-   * @return The identifier of the archive job.
+   * @return The string identifier of the archive job.
    */
-  std::string archiveToTape(
+  std::string archive(
     const SecurityIdentity &requester,
     const std::list<std::string> &srcUrls,
-    std::string dst);
+    const std::string &dst);
 
 protected:
 
@@ -465,6 +465,65 @@ protected:
    */
   void checkStorageClassIsNotInAMigrationRoute(const std::string &name) const;
 
+  /**
+   * Returns true if the specified absolute path is that of an existing
+   * directory within the archive namepsace.
+   *
+   * @param path The specified absolute path.
+   * @return True if the specified absolute path is that of an existing
+   * directory within the archive namepsace.
+   */
+  bool isAnExistingDirectory(const std::string &path) const throw();
+
+  /**
+   * Archives the specified list of source files to the specified destination
+   * directory within the archive namespace.
+   *
+   * The list of source files should contain at least one file.
+   *
+   * The storage class of the archived file will be inherited from its
+   * destination directory.
+   *
+   * @param requester The identity of the user requesting the archival.
+   * @param srcUrls List of one or more source files.
+   * @param dstDir Destination directory within the archive namespace.
+   * @return The string identifier of the archive job.
+   */
+  std::string archiveToDirectory(
+    const SecurityIdentity &requester,
+    const std::list<std::string> &srcUrls,
+    const std::string &dstDir);
+
+  /**
+   * Archives the specified list of source files to the specified destination
+   * file within the archive namespace.
+   *
+   * The list of source files should contain one and only one file.
+   *
+   * The storage class of the archived file will be inherited from its
+   * destination directory.
+   *
+   * @param requester The identity of the user requesting the archival.
+   * @param srcUrls List of one or more source files.
+   * @param dstFile Destination file within the archive namespace.
+   * @return The string identifier of the archive job.
+   */
+  std::string archiveToFile(
+    const SecurityIdentity &requester,
+    const std::list<std::string> &srcUrls,
+    const std::string &dstFile);
+
+  /**
+   * Throws an exception if the specified requester is not authorised to archive
+   * a file to the specified destination directory within the archive namespace.
+   *
+   * @param requester The identity of the user requesting the archival.
+   * @param dstDir Destination directory within the archive namespace.
+   */
+  void checkUserIsAuthorisedToArchive(
+    const SecurityIdentity &user,
+    const FileSystemNode &dstDir);
+
 }; // class MockClientAPI
 
 } // namespace cta
diff --git a/xroot_plugins/XrdProFilesystem.cpp b/xroot_plugins/XrdProFilesystem.cpp
index 17c53ed8be986e7860b6e11979a2edf53ac8a07e..e744392eded35cae3e661dc178f70c3a1e75976f 100644
--- a/xroot_plugins/XrdProFilesystem.cpp
+++ b/xroot_plugins/XrdProFilesystem.cpp
@@ -108,7 +108,7 @@ int XrdProFilesystem::executeArchiveCommand(const ParsedRequest &req, XrdOucErrI
     for(size_t i=0; i<req.args.size()-1; i++) {
       sourceFiles.push_back(req.args.at(i));
     }
-    std::string jobID = m_clientAPI->archiveToTape(requester, sourceFiles, destinationPath);
+    std::string jobID = m_clientAPI->archive(requester, sourceFiles, destinationPath);
     std::ostringstream responseSS;
     responseSS << "[OK] Requested archival of the following files:\n";
     for(std::list<std::string>::iterator it = sourceFiles.begin(); it != sourceFiles.end(); it++) {
@@ -421,7 +421,7 @@ int XrdProFilesystem::executeLsCommand(const ParsedRequest &req, XrdOucErrInfo &
       const cta::DirectoryEntry &entry = itor.next();
       
       responseSS << "\n";
-      responseSS << ((entry.getEntryType() == cta::DirectoryEntry::ENTRYTYPE_DIRECTORY) ? "d" : "-");
+      responseSS << ((entry.getType() == cta::DirectoryEntry::ENTRYTYPE_DIRECTORY) ? "d" : "-");
       responseSS << ((entry.getMode() & S_IRUSR) ? "r" : "-");
       responseSS << ((entry.getMode() & S_IWUSR) ? "w" : "-");
       responseSS << ((entry.getMode() & S_IXUSR) ? "x" : "-");