diff --git a/src/main/java/it/inaf/oats/vospace/JobService.java b/src/main/java/it/inaf/oats/vospace/JobService.java
index 9e90f41454dee187c805cf87cfbbdf65676978a3..749a4bac903298c81b599cff3fb7369b8ffd0a69 100644
--- a/src/main/java/it/inaf/oats/vospace/JobService.java
+++ b/src/main/java/it/inaf/oats/vospace/JobService.java
@@ -19,7 +19,7 @@ public class JobService {
     @Autowired
     private TapeService tapeService;
 
-    enum JobType {
+    public enum JobType {
         pullToVoSpace,
         pullFromVoSpace,
         pushToVoSpace,
diff --git a/src/main/java/it/inaf/oats/vospace/TransferController.java b/src/main/java/it/inaf/oats/vospace/TransferController.java
index 0ceaf4219f7516b9b3c730fdd1556dfd69519bad..c525baa345112de2b89623230fa7af1da296933f 100644
--- a/src/main/java/it/inaf/oats/vospace/TransferController.java
+++ b/src/main/java/it/inaf/oats/vospace/TransferController.java
@@ -7,6 +7,8 @@ import java.util.UUID;
 import javax.servlet.http.HttpServletRequest;
 import net.ivoa.xml.uws.v1.ExecutionPhase;
 import net.ivoa.xml.uws.v1.JobSummary;
+import net.ivoa.xml.uws.v1.Jobs;
+import net.ivoa.xml.uws.v1.ShortJobDescription;
 import net.ivoa.xml.vospace.v2.Transfer;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpHeaders;
@@ -19,6 +21,9 @@ import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+import java.util.List;
 
 @RestController
 public class TransferController {
@@ -103,6 +108,41 @@ public class TransferController {
         return ResponseEntity.ok(jobDAO.getJob(jobId).get().getPhase().toString());
     }
 
+    @GetMapping(value = "/transfers", produces = {MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
+    public ResponseEntity<?> getTransfers(
+            @RequestParam(value = "PHASE", required = false) Optional<List<ExecutionPhase>> phase,
+            @RequestParam(value = "AFTER", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Optional<LocalDateTime> after,
+            @RequestParam(value = "LAST", required = false) Optional<Integer> last,
+            @RequestParam(value = "direction", required = false) Optional<List<JobService.JobType>> direction,
+            User principal) {        
+        
+        if(last.isPresent())
+        {
+            if(last.get() <= 0) 
+                return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
+        }
+
+        String userId = principal.getName();
+
+        List<ExecutionPhase> phaseList;
+        if (phase.isPresent()) {
+            phaseList = phase.get();
+        } else {
+            phaseList = List.of();
+        }
+
+        List<JobService.JobType> directionList;
+        if (direction.isPresent()) {
+            directionList = direction.get();
+        } else {
+            directionList = List.of();
+        }       
+
+        Jobs jobs = jobDAO.getJobs(userId, phaseList, directionList, after, last);
+
+        return ResponseEntity.ok(jobs);
+    }
+
     private JobSummary newJobSummary(Transfer transfer, User principal) {
         String jobId = UUID.randomUUID().toString().replace("-", "");
 
diff --git a/src/main/java/it/inaf/oats/vospace/persistence/JobDAO.java b/src/main/java/it/inaf/oats/vospace/persistence/JobDAO.java
index b3781a8d1be2456da9c4ba5669c34558df5de307..58a47e33ecfc732f9d8e049cd47ff822a969902a 100644
--- a/src/main/java/it/inaf/oats/vospace/persistence/JobDAO.java
+++ b/src/main/java/it/inaf/oats/vospace/persistence/JobDAO.java
@@ -3,19 +3,29 @@ package it.inaf.oats.vospace.persistence;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import java.sql.Timestamp;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Types;
 import java.util.List;
 import java.util.Optional;
 import javax.sql.DataSource;
+import javax.xml.datatype.DatatypeFactory;
+import javax.xml.datatype.XMLGregorianCalendar;
 import net.ivoa.xml.uws.v1.ExecutionPhase;
 import net.ivoa.xml.uws.v1.JobSummary;
+import net.ivoa.xml.uws.v1.ShortJobDescription;
 import net.ivoa.xml.uws.v1.ResultReference;
+import net.ivoa.xml.uws.v1.Jobs;
+import it.inaf.oats.vospace.JobService;
 import net.ivoa.xml.vospace.v2.Transfer;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.stereotype.Repository;
+import java.util.ArrayList;
+import java.time.LocalDateTime;
+import java.math.BigDecimal;
+
 
 @Repository
 public class JobDAO {
@@ -105,6 +115,106 @@ public class JobDAO {
         return jobSummary;
     }
 
+    public Jobs getJobs(String userId,
+            List<ExecutionPhase> phaseList,
+            List<JobService.JobType> directionList,
+            Optional<LocalDateTime> after,
+            Optional<Integer> last
+    ) {
+        Jobs jobs = new Jobs();
+        jobs.setVersion("1.1");
+
+        List<ShortJobDescription> sjdList = jobs.getJobref();
+        // Query db to fill ShortJobDescription list
+        ArrayList<Object> queryParams = new ArrayList<>();
+        ArrayList<Integer> queryParamTypes = new ArrayList<>();
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("SELECT * FROM job");
+
+        sb.append(" WHERE owner_id = ?");
+        queryParams.add(userId);
+        queryParamTypes.add(Types.VARCHAR);
+
+        // Fill conditions on execution phase
+        if (phaseList.isEmpty()) {
+            sb.append(" AND phase NOT IN (?)");
+            queryParams.add(ExecutionPhase.ARCHIVED);
+            queryParamTypes.add(Types.OTHER);
+        } else {
+            sb.append(" AND phase IN (");            
+            for (int i = 0; i < phaseList.size(); i++) {
+                sb.append("?");                
+                queryParams.add(phaseList.get(i));
+                queryParamTypes.add(Types.OTHER);
+                if (i < phaseList.size() - 1) {
+                    sb.append(",");
+                }
+            }
+            sb.append(")");
+        }
+
+        // Fill conditions on type list
+        if (!directionList.isEmpty()) {
+            sb.append(" AND job_type IN (");
+            for (int i = 0; i < directionList.size(); i++) {
+                sb.append("?");
+                queryParams.add(directionList.get(i));
+                queryParamTypes.add(Types.OTHER);
+                if (i < directionList.size() - 1) {
+                    sb.append(",");
+                }
+            }
+            sb.append(")");
+        }
+
+        // Fill conditions on creation date
+        if (after.isPresent()) {
+            sb.append(" AND creation_time > ?");
+            queryParams.add(after.get());
+            queryParamTypes.add(Types.TIMESTAMP);
+        }
+
+        sb.append(" ORDER BY creation_time DESC");
+
+        if (last.isPresent()) {
+            sb.append(" LIMIT ?");
+            //sb.append(last.get().toString());
+            queryParams.add(last.get());
+            queryParamTypes.add(Types.INTEGER);
+        }
+
+        String sql = sb.toString();
+
+        // Perform query
+        jdbcTemplate.query(sql,
+                (ps) -> {                    
+                    for (int i = 0; i < queryParams.size(); i++) {
+                        ps.setObject(i + 1, queryParams.get(i),
+                                queryParamTypes.get(i));
+                    }
+                },
+                (rs) -> {                    
+                    sjdList.add(getShortJobDescriptionFromCurrentRow(rs));                    
+                }
+        );
+
+        return jobs;
+    }
+
+    private ShortJobDescription getShortJobDescriptionFromCurrentRow(ResultSet rs)
+            throws SQLException {
+        ShortJobDescription sjd = new ShortJobDescription();
+        sjd.setId(rs.getString("job_id"));
+        sjd.setOwnerId(rs.getString("owner_id"));
+        sjd.setType(rs.getString("job_type"));
+        sjd.setPhase(ExecutionPhase.fromValue(rs.getString("phase")));
+        sjd.setCreationTime(
+                toXMLGregorianCalendar(rs.getTimestamp("creation_time")));        
+        
+        return sjd;
+    }
+
     private Object getJobPayload(String jobType, String json) {
         try {
             // TODO: switch on jobType
@@ -145,4 +255,27 @@ public class JobDAO {
             throw new RuntimeException(ex);
         }
     }
+    
+    public static XMLGregorianCalendar toXMLGregorianCalendar(Timestamp t)
+    {
+        XMLGregorianCalendar cal = null;
+        try{         
+            cal = DatatypeFactory.newInstance().newXMLGregorianCalendar();
+        
+        LocalDateTime ldt = t.toLocalDateTime();
+        
+        cal.setYear(ldt.getYear());
+        cal.setMonth(ldt.getMonthValue());
+        cal.setDay(ldt.getDayOfMonth());
+        cal.setHour(ldt.getHour());
+        cal.setMinute(ldt.getMinute());
+        cal.setSecond(ldt.getSecond());
+        cal.setFractionalSecond(new BigDecimal("0." + ldt.getNano()));
+                
+        } catch(Exception e) {
+           e.printStackTrace();
+        }
+        
+        return cal;       
+    }
 }
diff --git a/src/test/java/it/inaf/oats/vospace/TransferControllerTest.java b/src/test/java/it/inaf/oats/vospace/TransferControllerTest.java
index 5b2e92b57852dbeb31a29d6dda1c3c32a5bdc51b..2645cb36e98f8d17ce2c5f2b12cfc794755d0789 100644
--- a/src/test/java/it/inaf/oats/vospace/TransferControllerTest.java
+++ b/src/test/java/it/inaf/oats/vospace/TransferControllerTest.java
@@ -6,9 +6,13 @@ import it.inaf.oats.vospace.persistence.JobDAO;
 import it.inaf.oats.vospace.persistence.NodeDAO;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
 import java.util.Optional;
 import net.ivoa.xml.uws.v1.ExecutionPhase;
 import net.ivoa.xml.uws.v1.JobSummary;
+import net.ivoa.xml.uws.v1.Jobs;
+import net.ivoa.xml.uws.v1.ShortJobDescription;
 import net.ivoa.xml.vospace.v2.DataNode;
 import net.ivoa.xml.vospace.v2.Node;
 import net.ivoa.xml.vospace.v2.Property;
@@ -36,6 +40,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
 import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 import org.w3c.dom.Document;
+import java.util.List;
 
 @SpringBootTest
 @AutoConfigureMockMvc
@@ -191,6 +196,51 @@ public class TransferControllerTest {
 
         verify(jobDao, times(1)).getJob(eq("123"));
     }
+    
+    @Test
+    public void testGetJobs() throws Exception {
+       
+        when(jobDao.getJobs(eq("user1"), any(), any(), any(), any()))
+                .thenReturn(this.getFakeJobs());
+        
+        mockMvc.perform(get("/transfers")
+                .header("Authorization", "Bearer user1_token")
+                .param("LAST", "-3")
+                .accept(MediaType.APPLICATION_XML))                
+                .andDo(print())
+                .andExpect(status().is4xxClientError());
+                                
+        
+        String xml2 = mockMvc.perform(get("/transfers")
+                .header("Authorization", "Bearer user1_token")
+                .accept(MediaType.APPLICATION_XML))                
+                .andDo(print())
+                .andExpect(status().isOk())
+                .andReturn().getResponse().getContentAsString();
+    }
+
+    private Jobs getFakeJobs() {
+        Jobs jobs = new Jobs();
+        jobs.setVersion("1.1");
+        List<ShortJobDescription> sjdList = jobs.getJobref();
+        sjdList.add(getFakeSJD1());
+
+        return jobs;
+    }
+
+    private ShortJobDescription getFakeSJD1() {
+        ShortJobDescription sjd = new ShortJobDescription();
+        sjd.setId("pippo1");
+        sjd.setPhase(ExecutionPhase.QUEUED);
+        sjd.setOwnerId("user1");
+        sjd.setType(JobService.JobType.pullFromVoSpace.toString());
+
+        LocalDateTime now = LocalDateTime.now();
+        Timestamp ts = Timestamp.valueOf(now);
+        sjd.setCreationTime(JobDAO.toXMLGregorianCalendar(ts));
+
+        return sjd;
+    }
 
     private JobSummary getFakePendingJob() {
         JobSummary job = new JobSummary();
@@ -212,7 +262,7 @@ public class TransferControllerTest {
     }
 
     protected static String getResourceFileContent(String fileName) throws Exception {
-        try ( InputStream in = TransferControllerTest.class.getClassLoader().getResourceAsStream(fileName)) {
+        try (InputStream in = TransferControllerTest.class.getClassLoader().getResourceAsStream(fileName)) {
             return new String(in.readAllBytes(), StandardCharsets.UTF_8);
         }
     }
diff --git a/src/test/java/it/inaf/oats/vospace/persistence/JobDAOTest.java b/src/test/java/it/inaf/oats/vospace/persistence/JobDAOTest.java
index 4134c206bde4c8658e6dc21fb36e3619bb38a5ea..0173ba286f830a175b272f1aced105abc616599f 100644
--- a/src/test/java/it/inaf/oats/vospace/persistence/JobDAOTest.java
+++ b/src/test/java/it/inaf/oats/vospace/persistence/JobDAOTest.java
@@ -1,9 +1,11 @@
 package it.inaf.oats.vospace.persistence;
 
+import it.inaf.oats.vospace.JobService;
+import java.util.List;
 import javax.sql.DataSource;
 import net.ivoa.xml.uws.v1.ExecutionPhase;
 import net.ivoa.xml.uws.v1.JobSummary;
-import net.ivoa.xml.vospace.v2.ContainerNode;
+import net.ivoa.xml.uws.v1.ShortJobDescription;
 import net.ivoa.xml.vospace.v2.Transfer;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -14,6 +16,10 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.TestPropertySource;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
+import java.util.Optional;
+import java.time.LocalDateTime;
+import java.time.Month;
+import net.ivoa.xml.uws.v1.Jobs;
 
 @ExtendWith(SpringExtension.class)
 @ContextConfiguration(classes = {DataSourceConfig.class})
@@ -54,4 +60,119 @@ public class JobDAOTest {
 
         assertEquals(ExecutionPhase.EXECUTING, dao.getJob("123").get().getPhase());
     }
+
+    @Test
+    public void testJobsList() {
+        // Check no arguments
+        String user = "user1";
+        List<ExecutionPhase> phaseList = List.of();
+        List<JobService.JobType> directionList = List.of();
+        Optional<LocalDateTime> after = Optional.ofNullable(null);
+        Optional<Integer> last = Optional.ofNullable(null);
+
+        Jobs jobs = dao.getJobs(user, phaseList, directionList, after, last);
+
+        assertTrue(jobs != null);
+        List<ShortJobDescription> sjdList = jobs.getJobref();
+        assertTrue(sjdList != null);
+        assertTrue(!sjdList.isEmpty());
+        assertTrue(
+                sjdList.stream().noneMatch(
+                        (i) -> {
+                            return i.getPhase().equals(ExecutionPhase.ARCHIVED);
+                        }
+                )
+        );
+        assertEquals(3, sjdList.size());
+    }
+
+    @Test
+    public void testJobsListLimit() {
+        // Check no arguments
+        String user = "user1";
+        List<ExecutionPhase> phaseList = List.of();
+        List<JobService.JobType> directionList = List.of();
+        Optional<LocalDateTime> after = Optional.ofNullable(null);
+        Optional<Integer> last = Optional.of(2);
+
+        Jobs jobs = dao.getJobs(user, phaseList, directionList, after, last);
+        List<ShortJobDescription> sjdList = jobs.getJobref();
+        assertEquals(2, sjdList.size());
+
+    }
+
+    @Test
+    public void testJobsPhase() {
+        // Check no arguments
+        String user = "user1";
+        List<ExecutionPhase> phaseList
+                = List.of(ExecutionPhase.PENDING, ExecutionPhase.EXECUTING);
+        List<JobService.JobType> directionList = List.of();
+        Optional<LocalDateTime> after = Optional.ofNullable(null);
+        Optional<Integer> last = Optional.ofNullable(null);
+
+        Jobs jobs = dao.getJobs(user, phaseList, directionList, after, last);
+        List<ShortJobDescription> sjdList = jobs.getJobref();
+        assertEquals(sjdList.size(), 2);
+        assertEquals("pippo5", sjdList.get(0).getId());
+        assertEquals("pippo2", sjdList.get(1).getId());
+    }
+
+    @Test
+    public void testJobsDirection() {
+        // Check no arguments
+        String user = "user1";
+        List<ExecutionPhase> phaseList = List.of();
+        List<JobService.JobType> directionList
+                = List.of(JobService.JobType.pullFromVoSpace,
+                        JobService.JobType.pullToVoSpace);
+
+        Optional<LocalDateTime> after = Optional.ofNullable(null);
+        Optional<Integer> last = Optional.ofNullable(null);
+        Jobs jobs = dao.getJobs(user, phaseList, directionList, after, last);
+        List<ShortJobDescription> sjdList = jobs.getJobref();
+        assertEquals(2, sjdList.size());
+        assertEquals("pippo3", sjdList.get(0).getId());
+        assertEquals("pippo2", sjdList.get(1).getId());
+    }
+
+    @Test
+    public void testJobsAfter() {
+        // Check no arguments
+        String user = "user1";
+        List<ExecutionPhase> phaseList = List.of();
+        List<JobService.JobType> directionList = List.of();
+
+        LocalDateTime ldt
+                = LocalDateTime.of(2013, Month.FEBRUARY, 7, 18, 15);
+        Optional<LocalDateTime> after = Optional.of(ldt);
+
+        Optional<Integer> last = Optional.ofNullable(null);
+        Jobs jobs = dao.getJobs(user, phaseList, directionList, after, last);
+        List<ShortJobDescription> sjdList = jobs.getJobref();
+        assertEquals(2, sjdList.size());
+        assertEquals("pippo5", sjdList.get(0).getId());
+        assertEquals("pippo3", sjdList.get(1).getId());
+    }
+
+    @Test
+    public void testJobsAllchecks() {
+        // Check no arguments
+        String user = "user1";
+        List<ExecutionPhase> phaseList = List.of(ExecutionPhase.QUEUED, 
+                ExecutionPhase.PENDING);
+        List<JobService.JobType> directionList =
+                List.of(JobService.JobType.pullFromVoSpace,
+                        JobService.JobType.pullToVoSpace);
+
+        LocalDateTime ldt
+                = LocalDateTime.of(2013, Month.FEBRUARY, 7, 18, 15);
+        Optional<LocalDateTime> after = Optional.of(ldt);
+
+        Optional<Integer> last = Optional.of(2);
+        Jobs jobs = dao.getJobs(user, phaseList, directionList, after, last);
+        List<ShortJobDescription> sjdList = jobs.getJobref();
+        assertEquals(1, sjdList.size());
+        assertEquals("pippo3", sjdList.get(0).getId());
+    }
 }
diff --git a/src/test/resources/test-data.sql b/src/test/resources/test-data.sql
index 2a0c10bc91be31372de426fc50c14c550911a448..67aa878e0f8597cc445c87f9eb1fe332ad47ae54 100644
--- a/src/test/resources/test-data.sql
+++ b/src/test/resources/test-data.sql
@@ -11,3 +11,12 @@ INSERT INTO node (parent_path, parent_relative_path, name, type, owner_id, creat
 INSERT INTO node (parent_path, parent_relative_path, name, type, owner_id, creator_id, is_public) VALUES ('', NULL, 'test2', 'container', 'user2', 'user2', true);      -- /test2
 INSERT INTO node (parent_path, parent_relative_path, name, type, owner_id, creator_id, is_public) VALUES ('5', '', 'f4', 'container', 'user2', 'user2', true);    -- /test2/f4 (rel: /f4)
 INSERT INTO node (parent_path, parent_relative_path, name, type, owner_id, creator_id, is_public) VALUES ('5', '', 'f5', 'container', 'user2', 'user2', true);    -- /test2/f5 (rel: /f5)
+
+DELETE FROM job;
+
+INSERT INTO job (job_id, owner_id, job_type, phase, start_time, end_time, creation_time, job_info, results) VALUES ('pippo1', 'user1', 'pullFromVoSpace', 'ARCHIVED', NULL, NULL, '2011-06-22 19:10:25', NULL, NULL);
+INSERT INTO job (job_id, owner_id, job_type, phase, start_time, end_time, creation_time, job_info, results) VALUES ('pippo2', 'user1', 'pullToVoSpace', 'PENDING', NULL, NULL, '2012-06-22 19:10:25', NULL, NULL);
+INSERT INTO job (job_id, owner_id, job_type, phase, start_time, end_time, creation_time, job_info, results) VALUES ('pippo3', 'user1', 'pullFromVoSpace', 'QUEUED', NULL, NULL, '2013-06-22 19:10:25', NULL, NULL);
+INSERT INTO job (job_id, owner_id, job_type, phase, start_time, end_time, creation_time, job_info, results) VALUES ('pippo4', 'user2', 'copyNode', 'PENDING', NULL, NULL, '2014-06-22 19:10:25', NULL, NULL);
+INSERT INTO job (job_id, owner_id, job_type, phase, start_time, end_time, creation_time, job_info, results) VALUES ('pippo5', 'user1', 'pushToVoSpace', 'EXECUTING', NULL, NULL, '2015-06-22 19:10:25', NULL, NULL);
+INSERT INTO job (job_id, owner_id, job_type, phase, start_time, end_time, creation_time, job_info, results) VALUES ('pippo6', 'user2', 'pullFromVoSpace', 'PENDING', NULL, NULL, '2015-06-22 19:10:25', NULL, NULL);
\ No newline at end of file