killbill-aplcache

Merge pull request #972 from wwjbatista/work-for-release-0.19.x New

5/7/2018 5:34:10 PM

Changes

Details

diff --git a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java
index 46a3ea0..be52cb0 100644
--- a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java
+++ b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java
@@ -239,4 +239,9 @@ public class DefaultAccountUserApi extends DefaultAccountApiBase implements Acco
     public List<AuditLogWithHistory> getAuditLogsWithHistoryForId(final UUID accountId, final AuditLevel auditLevel, final TenantContext tenantContext) throws AccountApiException {
         return accountDao.getAuditLogsWithHistoryForId(accountId, auditLevel, internalCallContextFactory.createInternalTenantContext(accountId, tenantContext));
     }
+
+    @Override
+    public List<AuditLogWithHistory> getEmailAuditLogsWithHistoryForId(final UUID accountEmailId, final AuditLevel auditLevel, final TenantContext tenantContext) throws AccountApiException {
+        return accountDao.getEmailAuditLogsWithHistoryForId(accountEmailId, auditLevel, internalCallContextFactory.createInternalTenantContext(tenantContext.getAccountId(), tenantContext));
+    }
 }
diff --git a/account/src/main/java/org/killbill/billing/account/dao/AccountDao.java b/account/src/main/java/org/killbill/billing/account/dao/AccountDao.java
index 1e0e94c..81de578 100644
--- a/account/src/main/java/org/killbill/billing/account/dao/AccountDao.java
+++ b/account/src/main/java/org/killbill/billing/account/dao/AccountDao.java
@@ -58,4 +58,7 @@ public interface AccountDao extends EntityDao<AccountModelDao, Account, AccountA
     List<AccountModelDao> getAccountsByParentId(UUID parentAccountId, InternalTenantContext context);
 
     List<AuditLogWithHistory> getAuditLogsWithHistoryForId(UUID accountId, AuditLevel auditLevel, InternalTenantContext context) throws AccountApiException;
+
+    List<AuditLogWithHistory> getEmailAuditLogsWithHistoryForId(UUID accountEmailId, AuditLevel auditLevel, InternalTenantContext context) throws AccountApiException;
+
 }
diff --git a/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java b/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
index dddab19..909db49 100644
--- a/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
+++ b/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
@@ -334,4 +334,15 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
             }
         });
     }
+
+    @Override
+    public List<AuditLogWithHistory> getEmailAuditLogsWithHistoryForId(final UUID accountEmailId, final AuditLevel auditLevel, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(true, new EntitySqlDaoTransactionWrapper<List<AuditLogWithHistory>>() {
+            @Override
+            public List<AuditLogWithHistory> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) {
+                final AccountEmailSqlDao transactional = entitySqlDaoWrapperFactory.become(AccountEmailSqlDao.class);
+                return auditDao.getAuditLogsWithHistoryForId(transactional, TableName.ACCOUNT_EMAIL, accountEmailId, auditLevel, context);
+            }
+        });
+    }
 }
diff --git a/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java b/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
index 077ca8d..69e8199 100644
--- a/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
+++ b/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
@@ -193,4 +193,9 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account, 
     public List<AuditLogWithHistory> getAuditLogsWithHistoryForId(final UUID accountId, final AuditLevel auditLevel, final InternalTenantContext context) throws AccountApiException {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public List<AuditLogWithHistory> getEmailAuditLogsWithHistoryForId(final UUID accountEmailId, final AuditLevel auditLevel, final InternalTenantContext context) throws AccountApiException {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index bcb9d1d..ecf1c9d 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -1434,6 +1434,20 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
+    @TimedResource
+    @GET
+    @Path("/{accountId:" + UUID_PATTERN + "}/" + EMAILS + "/{accountEmailId:" + UUID_PATTERN + "}/" + AUDIT_LOG_WITH_HISTORY)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Retrieve account email audit logs with history by id", response = AuditLogJson.class, responseContainer = "List")
+    @ApiResponses(value = {@ApiResponse(code = 404, message = "Account not found")})
+    public Response getAccountEmailAuditLogsWithHistory(@PathParam("accountId") final UUID accountId,
+                                                        @PathParam("accountEmailId") final UUID accountEmailId,
+                                                        @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException {
+        final TenantContext tenantContext = context.createTenantContextWithAccountId(accountId, request);
+        final List<AuditLogWithHistory> auditLogWithHistory = accountUserApi.getEmailAuditLogsWithHistoryForId(accountEmailId, AuditLevel.FULL, tenantContext);
+        return Response.status(Status.OK).entity(getAuditLogsWithHistory(auditLogWithHistory)).build();
+    }
+
     @Override
     protected ObjectType getObjectType() {
         return ObjectType.ACCOUNT;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java
index 69fcd94..92a8f76 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java
@@ -18,6 +18,7 @@ package org.killbill.billing.jaxrs.resources;
 
 import java.net.URI;
 import java.util.List;
+import java.util.UUID;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.DefaultValue;
@@ -27,18 +28,23 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
 
 import org.killbill.billing.ObjectType;
+import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.jaxrs.json.AuditLogJson;
 import org.killbill.billing.jaxrs.json.CustomFieldJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.payment.api.PaymentApi;
+import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldApiException;
 import org.killbill.billing.util.api.CustomFieldUserApi;
 import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.customfield.CustomField;
 import org.killbill.billing.util.entity.Pagination;
@@ -51,6 +57,7 @@ import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
@@ -124,4 +131,17 @@ public class CustomFieldResource extends JaxRsResourceBase {
                                                 },
                                                 nextPageUri);
     }
+
+    @TimedResource
+    @GET
+    @Path("/{customFieldId:" + UUID_PATTERN + "}/" + AUDIT_LOG_WITH_HISTORY)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Retrieve custom field audit logs with history by id", response = AuditLogJson.class, responseContainer = "List")
+    @ApiResponses(value = {@ApiResponse(code = 404, message = "Account not found")})
+    public Response getCustomFieldAuditLogsWithHistory(@PathParam("customFieldId") final UUID customFieldId,
+                                                   @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException {
+        final TenantContext tenantContext = context.createTenantContextNoAccountId(request);
+        final List<AuditLogWithHistory> auditLogWithHistory = customFieldUserApi.getCustomFieldAuditLogsWithHistoryForId(customFieldId, AuditLevel.FULL, tenantContext);
+        return Response.status(Status.OK).entity(getAuditLogsWithHistory(auditLogWithHistory)).build();
+    }
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index c95abc4..409b58b 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -18,9 +18,6 @@
 
 package org.killbill.billing.jaxrs.resources;
 
-import javax.ws.rs.DefaultValue;
-import javax.ws.rs.QueryParam;
-
 public interface JaxrsResource {
 
     public static final String API_PREFIX = "";
@@ -196,6 +193,7 @@ public interface JaxrsResource {
     public static final String CHARGES = "charges";
     public static final String CHARGES_PATH = PREFIX + "/" + INVOICES + "/" + CHARGES;
 
+    public static final String ATTEMPTS = "attempts";
     public static final String PAYMENTS = "payments";
     public static final String PAYMENTS_PATH = PREFIX + "/" + PAYMENTS;
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java
index 89e3ac5..3445337 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java
@@ -45,6 +45,7 @@ import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.jaxrs.json.AuditLogJson;
 import org.killbill.billing.jaxrs.json.CustomFieldJson;
 import org.killbill.billing.jaxrs.json.PaymentMethodJson;
 import org.killbill.billing.jaxrs.util.Context;
@@ -53,11 +54,13 @@ import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PaymentMethod;
 import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldApiException;
 import org.killbill.billing.util.api.CustomFieldUserApi;
 import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.audit.AccountAuditLogs;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.entity.Pagination;
@@ -346,6 +349,19 @@ public class PaymentMethodResource extends JaxRsResourceBase {
                                         context.createCallContextNoAccountId(createdBy, reason, comment, request));
     }
 
+    @TimedResource
+    @GET
+    @Path("/{paymentMethodId:" + UUID_PATTERN + "}/" + AUDIT_LOG_WITH_HISTORY)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Retrieve payment method audit logs with history by id", response = AuditLogJson.class, responseContainer = "List")
+    @ApiResponses(value = {@ApiResponse(code = 404, message = "Account not found")})
+    public Response getPaymentMethodAuditLogsWithHistory(@PathParam("paymentMethodId") final UUID paymentMethodId,
+                                                   @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException {
+        final TenantContext tenantContext = context.createTenantContextNoAccountId(request);
+        final List<AuditLogWithHistory> auditLogWithHistory = paymentApi.getPaymentMethodAuditLogsWithHistoryForId(paymentMethodId, AuditLevel.FULL, tenantContext);
+        return Response.status(Status.OK).entity(getAuditLogsWithHistory(auditLogWithHistory)).build();
+    }
+
     @Override
     protected ObjectType getObjectType() {
         return ObjectType.PAYMENT_METHOD;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
index 6c7d14c..9ceeda6 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
@@ -47,6 +47,7 @@ import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.jaxrs.json.AuditLogJson;
 import org.killbill.billing.jaxrs.json.ComboPaymentTransactionJson;
 import org.killbill.billing.jaxrs.json.CustomFieldJson;
 import org.killbill.billing.jaxrs.json.PaymentJson;
@@ -60,6 +61,7 @@ import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PaymentOptions;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldApiException;
 import org.killbill.billing.util.api.CustomFieldUserApi;
@@ -67,6 +69,7 @@ import org.killbill.billing.util.api.TagApiException;
 import org.killbill.billing.util.api.TagDefinitionApiException;
 import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.audit.AccountAuditLogs;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.entity.Pagination;
@@ -896,6 +899,32 @@ public class PaymentResource extends ComboPaymentResource {
                                 context.createCallContextNoAccountId(createdBy, reason, comment, request));
     }
 
+    @TimedResource
+    @GET
+    @Path("/{paymentId:" + UUID_PATTERN + "}/" + AUDIT_LOG_WITH_HISTORY)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Retrieve payment audit logs with history by id", response = AuditLogJson.class, responseContainer = "List")
+    @ApiResponses(value = {@ApiResponse(code = 404, message = "Account not found")})
+    public Response getPaymentAuditLogsWithHistory(@PathParam("paymentId") final UUID paymentId,
+                                                   @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException {
+        final TenantContext tenantContext = context.createTenantContextNoAccountId(request);
+        final List<AuditLogWithHistory> auditLogWithHistory = paymentApi.getPaymentAuditLogsWithHistoryForId(paymentId, AuditLevel.FULL, tenantContext);
+        return Response.status(Status.OK).entity(getAuditLogsWithHistory(auditLogWithHistory)).build();
+    }
+
+    @TimedResource
+    @GET
+    @Path("/" + ATTEMPTS + "/{paymentAttemptId:" + UUID_PATTERN + "}/" + AUDIT_LOG_WITH_HISTORY)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Retrieve payment attempt audit logs with history by id", response = AuditLogJson.class, responseContainer = "List")
+    @ApiResponses(value = {@ApiResponse(code = 404, message = "Account not found")})
+    public Response getPaymentAttemptAuditLogsWithHistory(@PathParam("paymentAttemptId") final UUID paymentAttemptId,
+                                                   @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException {
+        final TenantContext tenantContext = context.createTenantContextNoAccountId(request);
+        final List<AuditLogWithHistory> auditLogWithHistory = paymentApi.getPaymentAttemptAuditLogsWithHistoryForId(paymentAttemptId, AuditLevel.FULL, tenantContext);
+        return Response.status(Status.OK).entity(getAuditLogsWithHistory(auditLogWithHistory)).build();
+    }
+
     @Override
     protected ObjectType getObjectType() {
         return ObjectType.PAYMENT;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java
index 7f5d997..cf9c557 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java
@@ -37,16 +37,20 @@ import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 
 import org.killbill.billing.ObjectType;
+import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.jaxrs.json.AuditLogJson;
 import org.killbill.billing.jaxrs.json.TagDefinitionJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.payment.api.PaymentApi;
+import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldUserApi;
 import org.killbill.billing.util.api.TagDefinitionApiException;
 import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.tag.TagDefinition;
 import org.killbill.clock.Clock;
@@ -153,6 +157,19 @@ public class TagDefinitionResource extends JaxRsResourceBase {
         return Response.status(Status.NO_CONTENT).build();
     }
 
+    @TimedResource
+    @GET
+    @Path("/{tagDefinitionId:" + UUID_PATTERN + "}/" + AUDIT_LOG_WITH_HISTORY)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Retrieve tag definition audit logs with history by id", response = AuditLogJson.class, responseContainer = "List")
+    @ApiResponses(value = {@ApiResponse(code = 404, message = "Account not found")})
+    public Response getTagDefinitionAuditLogsWithHistory(@PathParam("tagDefinitionId") final UUID tagDefinitionId,
+                                                   @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException {
+        final TenantContext tenantContext = context.createTenantContextNoAccountId(request);
+        final List<AuditLogWithHistory> auditLogWithHistory = tagUserApi.getTagDefinitionAuditLogsWithHistoryForId(tagDefinitionId, AuditLevel.FULL, tenantContext);
+        return Response.status(Status.OK).entity(getAuditLogsWithHistory(auditLogWithHistory)).build();
+    }
+
     @Override
     protected ObjectType getObjectType() {
         return ObjectType.TAG_DEFINITION;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java
index e194d3c..96114a0 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java
@@ -30,10 +30,15 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
 
 import org.killbill.billing.ObjectType;
+import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.jaxrs.json.AuditLogJson;
 import org.killbill.billing.payment.api.PaymentApi;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.clock.Clock;
 import org.killbill.billing.jaxrs.json.TagJson;
 import org.killbill.billing.jaxrs.util.Context;
@@ -55,6 +60,7 @@ import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
@@ -141,4 +147,17 @@ public class TagResource extends JaxRsResourceBase {
                                                 },
                                                 nextPageUri);
     }
+
+    @TimedResource
+    @GET
+    @Path("/{tagId:" + UUID_PATTERN + "}/" + AUDIT_LOG_WITH_HISTORY)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Retrieve tag audit logs with history by id", response = AuditLogJson.class, responseContainer = "List")
+    @ApiResponses(value = {@ApiResponse(code = 404, message = "Account not found")})
+    public Response getTagAuditLogsWithHistory(@PathParam("tagId") final UUID tagId,
+                                               @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException {
+        final TenantContext tenantContext = context.createTenantContextNoAccountId(request);
+        final List<AuditLogWithHistory> auditLogWithHistory = tagUserApi.getTagAuditLogsWithHistoryForId(tagId, AuditLevel.FULL, tenantContext);
+        return Response.status(Status.OK).entity(getAuditLogsWithHistory(auditLogWithHistory)).build();
+    }
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java
index 31f6522..da16de5 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java
@@ -34,12 +34,14 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.jaxrs.json.AuditLogJson;
 import org.killbill.billing.jaxrs.json.CustomFieldJson;
 import org.killbill.billing.jaxrs.json.PaymentJson;
 import org.killbill.billing.jaxrs.json.PaymentTransactionJson;
@@ -52,6 +54,7 @@ import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PaymentOptions;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionStatus;
+import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldApiException;
 import org.killbill.billing.util.api.CustomFieldUserApi;
@@ -59,6 +62,7 @@ import org.killbill.billing.util.api.TagApiException;
 import org.killbill.billing.util.api.TagDefinitionApiException;
 import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.audit.AccountAuditLogs;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.clock.Clock;
@@ -261,6 +265,19 @@ public class TransactionResource extends JaxRsResourceBase {
                                 context.createCallContextNoAccountId(createdBy, reason, comment, request));
     }
 
+    @TimedResource
+    @GET
+    @Path("/{transactionId:" + UUID_PATTERN + "}/" + AUDIT_LOG_WITH_HISTORY)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Retrieve payment transaction audit logs with history by id", response = AuditLogJson.class, responseContainer = "List")
+    @ApiResponses(value = {@ApiResponse(code = 404, message = "Account not found")})
+    public Response getTransactionAuditLogsWithHistory(@PathParam("transactionId") final UUID transactionId,
+                                                   @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException {
+        final TenantContext tenantContext = context.createTenantContextNoAccountId(request);
+        final List<AuditLogWithHistory> auditLogWithHistory = paymentApi.getPaymentTransactionAuditLogsWithHistoryForId(transactionId, AuditLevel.FULL, tenantContext);
+        return Response.status(Status.OK).entity(getAuditLogsWithHistory(auditLogWithHistory)).build();
+    }
+
     @Override
     protected ObjectType getObjectType() {
         return ObjectType.TRANSACTION;
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
index 57e2596..c9ffb84 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
@@ -34,7 +34,10 @@ import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.payment.core.PaymentMethodProcessor;
 import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.core.PluginControlPaymentProcessor;
+import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.util.UUIDs;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
@@ -61,13 +64,15 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
     private final PaymentProcessor paymentProcessor;
     private final PaymentMethodProcessor paymentMethodProcessor;
     private final PluginControlPaymentProcessor pluginControlPaymentProcessor;
+    private final PaymentDao paymentDao;
 
     @Inject
-    public DefaultPaymentApi(final PaymentConfig paymentConfig, final PaymentProcessor paymentProcessor, final PaymentMethodProcessor paymentMethodProcessor, final PluginControlPaymentProcessor pluginControlPaymentProcessor, final InternalCallContextFactory internalCallContextFactory) {
+    public DefaultPaymentApi(final PaymentConfig paymentConfig, final PaymentProcessor paymentProcessor, final PaymentMethodProcessor paymentMethodProcessor, final PluginControlPaymentProcessor pluginControlPaymentProcessor, final PaymentDao paymentDao, final InternalCallContextFactory internalCallContextFactory) {
         super(paymentConfig, internalCallContextFactory);
         this.paymentProcessor = paymentProcessor;
         this.paymentMethodProcessor = paymentMethodProcessor;
         this.pluginControlPaymentProcessor = pluginControlPaymentProcessor;
+        this.paymentDao = paymentDao;
     }
 
     @Override
@@ -1072,6 +1077,26 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
     }
 
     @Override
+    public List<AuditLogWithHistory> getPaymentAuditLogsWithHistoryForId(final UUID paymentId, final AuditLevel auditLevel, final TenantContext tenantContext) {
+        return paymentDao.getPaymentAuditLogsWithHistoryForId(paymentId, auditLevel, internalCallContextFactory.createInternalTenantContext(paymentId, ObjectType.PAYMENT, tenantContext));
+    }
+
+    @Override
+    public List<AuditLogWithHistory> getPaymentMethodAuditLogsWithHistoryForId(final UUID paymentMethodId, final AuditLevel auditLevel, final TenantContext tenantContext) {
+        return paymentDao.getPaymentMethodAuditLogsWithHistoryForId(paymentMethodId, auditLevel, internalCallContextFactory.createInternalTenantContext(paymentMethodId, ObjectType.PAYMENT_METHOD, tenantContext));
+    }
+
+    @Override
+    public List<AuditLogWithHistory> getPaymentAttemptAuditLogsWithHistoryForId(final UUID paymentAttemptId, final AuditLevel auditLevel, final TenantContext tenantContext) {
+        return paymentDao.getPaymentAttemptAuditLogsWithHistoryForId(paymentAttemptId, auditLevel, internalCallContextFactory.createInternalTenantContext(paymentAttemptId, ObjectType.PAYMENT_ATTEMPT, tenantContext));
+    }
+
+    @Override
+    public List<AuditLogWithHistory> getPaymentTransactionAuditLogsWithHistoryForId(final UUID paymentTransactionId, final AuditLevel auditLevel, final TenantContext tenantContext) {
+        return paymentDao.getPaymentTransactionAuditLogsWithHistoryForId(paymentTransactionId, auditLevel, internalCallContextFactory.createInternalTenantContext(paymentTransactionId, ObjectType.TRANSACTION, tenantContext));
+    }
+
+    @Override
     public Payment getPaymentByTransactionId(final UUID transactionId, final boolean withPluginInfo, final boolean withAttempts, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentApiException {
         final Payment payment = paymentProcessor.getPaymentByTransactionId(transactionId, withPluginInfo, withAttempts, properties, context, internalCallContextFactory.createInternalTenantContext(transactionId, ObjectType.TRANSACTION, context));
         if (payment == null) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
index 114744a..b7aa24f 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
@@ -48,9 +48,14 @@ import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.TransactionStatus;
 import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.core.sm.PaymentStateMachineHelper;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
+import org.killbill.billing.util.audit.dao.AuditDao;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.Entity;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.DefaultPaginationSqlDaoHelper;
@@ -60,6 +65,7 @@ import org.killbill.billing.util.entity.dao.EntityDaoBase;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+import org.killbill.billing.util.tag.dao.TagSqlDao;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.bus.api.PersistentBus.EventBusException;
 import org.killbill.clock.Clock;
@@ -83,14 +89,16 @@ public class DefaultPaymentDao extends EntityDaoBase<PaymentModelDao, Payment, P
     private final DefaultPaginationSqlDaoHelper paginationHelper;
     private final PersistentBus eventBus;
     private final Clock clock;
+    private final AuditDao auditDao;
 
     @Inject
     public DefaultPaymentDao(final IDBI dbi, @Named(MAIN_RO_IDBI_NAMED) final IDBI roDbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher,
-                             final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory, final PersistentBus eventBus) {
+                             final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory, final PersistentBus eventBus, final AuditDao auditDao) {
         super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, roDbi, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory), PaymentSqlDao.class);
         this.paginationHelper = new DefaultPaginationSqlDaoHelper(transactionalSqlDao);
         this.eventBus = eventBus;
         this.clock = clock;
+        this.auditDao = auditDao;
     }
 
     @Override
@@ -663,6 +671,50 @@ public class DefaultPaymentDao extends EntityDaoBase<PaymentModelDao, Payment, P
         });
     }
 
+    @Override
+    public List<AuditLogWithHistory> getPaymentAuditLogsWithHistoryForId(final UUID paymentId, final AuditLevel auditLevel, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(true, new EntitySqlDaoTransactionWrapper<List<AuditLogWithHistory>>() {
+            @Override
+            public List<AuditLogWithHistory> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) {
+                final PaymentSqlDao transactional = entitySqlDaoWrapperFactory.become(PaymentSqlDao.class);
+                return auditDao.getAuditLogsWithHistoryForId(transactional, TableName.PAYMENTS, paymentId, auditLevel, context);
+            }
+        });
+    }
+
+    @Override
+    public List<AuditLogWithHistory> getPaymentMethodAuditLogsWithHistoryForId(final UUID paymentMethodId, final AuditLevel auditLevel, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(true, new EntitySqlDaoTransactionWrapper<List<AuditLogWithHistory>>() {
+            @Override
+            public List<AuditLogWithHistory> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) {
+                final PaymentMethodSqlDao transactional = entitySqlDaoWrapperFactory.become(PaymentMethodSqlDao.class);
+                return auditDao.getAuditLogsWithHistoryForId(transactional, TableName.PAYMENT_METHODS, paymentMethodId, auditLevel, context);
+            }
+        });
+    }
+
+    @Override
+    public List<AuditLogWithHistory> getPaymentAttemptAuditLogsWithHistoryForId(final UUID paymentAttemptId, final AuditLevel auditLevel, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(true, new EntitySqlDaoTransactionWrapper<List<AuditLogWithHistory>>() {
+            @Override
+            public List<AuditLogWithHistory> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) {
+                final PaymentAttemptSqlDao transactional = entitySqlDaoWrapperFactory.become(PaymentAttemptSqlDao.class);
+                return auditDao.getAuditLogsWithHistoryForId(transactional, TableName.PAYMENT_ATTEMPTS, paymentAttemptId, auditLevel, context);
+            }
+        });
+    }
+
+    @Override
+    public List<AuditLogWithHistory> getPaymentTransactionAuditLogsWithHistoryForId(final UUID paymentTransactionId, final AuditLevel auditLevel, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(true, new EntitySqlDaoTransactionWrapper<List<AuditLogWithHistory>>() {
+            @Override
+            public List<AuditLogWithHistory> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) {
+                final TransactionSqlDao transactional = entitySqlDaoWrapperFactory.become(TransactionSqlDao.class);
+                return auditDao.getAuditLogsWithHistoryForId(transactional, TableName.PAYMENT_TRANSACTIONS, paymentTransactionId, auditLevel, context);
+            }
+        });
+    }
+
     private void postPaymentEventFromTransaction(final UUID accountId,
                                                  final TransactionStatus transactionStatus,
                                                  final TransactionType transactionType,
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
index 375bbdd..fe1c1bf 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
@@ -28,8 +28,14 @@ import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.api.PaymentAttempt;
+import org.killbill.billing.payment.api.PaymentMethod;
+import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.TransactionStatus;
 import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
+import org.killbill.billing.util.entity.Entity;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.EntityDao;
 
@@ -104,4 +110,12 @@ public interface PaymentDao extends EntityDao<PaymentModelDao, Payment, PaymentA
     public void deletedPaymentMethod(UUID paymentMethodId, InternalCallContext context);
 
     public List<PaymentMethodModelDao> refreshPaymentMethods(String pluginName, List<PaymentMethodModelDao> paymentMethods, InternalCallContext context);
+
+    List<AuditLogWithHistory> getPaymentAuditLogsWithHistoryForId(UUID paymentId, AuditLevel auditLevel, InternalTenantContext context);
+
+    List<AuditLogWithHistory> getPaymentMethodAuditLogsWithHistoryForId(UUID paymentMethodId, AuditLevel auditLevel, InternalTenantContext context);
+
+    List<AuditLogWithHistory> getPaymentAttemptAuditLogsWithHistoryForId(UUID paymentAttemptId, AuditLevel auditLevel, InternalTenantContext context);
+
+    List<AuditLogWithHistory> getPaymentTransactionAuditLogsWithHistoryForId(UUID paymentTransactionId, AuditLevel auditLevel, InternalTenantContext context);
 }
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentMethodProcessorWithDB.java b/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentMethodProcessorWithDB.java
index 3912a51..e444b0a 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentMethodProcessorWithDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentMethodProcessorWithDB.java
@@ -27,8 +27,12 @@ import org.killbill.billing.payment.api.PaymentMethod;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.dao.PaymentMethodModelDao;
 import org.killbill.billing.payment.dao.PaymentMethodSqlDao;
+import org.killbill.billing.payment.dao.PaymentModelDao;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.audit.ChangeType;
 import org.killbill.billing.util.dao.EntityHistoryModelDao;
+import org.killbill.billing.util.dao.TableName;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -69,35 +73,34 @@ public class TestPaymentMethodProcessorWithDB extends PaymentTestSuiteWithEmbedd
         final UUID paymentMethodId = paymentMethodProcessor.createOrGetExternalPaymentMethod("pmExternalKey", account, PLUGIN_PROPERTIES, callContext, internalCallContext);
         final PaymentMethodModelDao paymentMethodModelDao = paymentDao.getPaymentMethod(paymentMethodId, internalCallContext);
 
-        final List<EntityHistoryModelDao<PaymentMethodModelDao, PaymentMethod>> history1 = getPaymentMethodHistory(paymentMethodModelDao.getRecordId());
-        Assert.assertEquals(history1.size(), 1);
-        Assert.assertEquals(history1.get(0).getChangeType(), ChangeType.INSERT);
-        Assert.assertEquals(history1.get(0).getEntity().getAccountRecordId(), paymentMethodModelDao.getAccountRecordId());
-        Assert.assertEquals(history1.get(0).getEntity().getTenantRecordId(), paymentMethodModelDao.getTenantRecordId());
-        Assert.assertEquals(history1.get(0).getEntity().getExternalKey(), paymentMethodModelDao.getExternalKey());
-        Assert.assertTrue(history1.get(0).getEntity().isActive());
+        List<AuditLogWithHistory> auditLogsWithHistory = paymentDao.getPaymentMethodAuditLogsWithHistoryForId(paymentMethodModelDao.getId(), AuditLevel.FULL, internalCallContext);
+        Assert.assertEquals(auditLogsWithHistory.size(), 1);
+
+        PaymentMethodModelDao history1 = (PaymentMethodModelDao) auditLogsWithHistory.get(0).getEntity();
+        Assert.assertEquals(auditLogsWithHistory.get(0).getChangeType(), ChangeType.INSERT);
+        Assert.assertEquals(history1.getAccountRecordId(), paymentMethodModelDao.getAccountRecordId());
+        Assert.assertEquals(history1.getTenantRecordId(), paymentMethodModelDao.getTenantRecordId());
+        Assert.assertEquals(history1.getExternalKey(), paymentMethodModelDao.getExternalKey());
+        Assert.assertTrue(history1.isActive());
 
         paymentMethodProcessor.deletedPaymentMethod(account, paymentMethodId, true, true, ImmutableList.<PluginProperty>of(), callContext, internalCallContext);
 
-        final List<EntityHistoryModelDao<PaymentMethodModelDao, PaymentMethod>> history2 = getPaymentMethodHistory(paymentMethodModelDao.getRecordId());
-        Assert.assertEquals(history2.size(), 2);
-        Assert.assertEquals(history2.get(0).getChangeType(), ChangeType.INSERT);
-        Assert.assertEquals(history2.get(0).getEntity().getAccountRecordId(), paymentMethodModelDao.getAccountRecordId());
-        Assert.assertEquals(history2.get(0).getEntity().getTenantRecordId(), paymentMethodModelDao.getTenantRecordId());
-        Assert.assertEquals(history2.get(0).getEntity().getExternalKey(), paymentMethodModelDao.getExternalKey());
-        Assert.assertTrue(history2.get(0).getEntity().isActive());
+        auditLogsWithHistory = paymentDao.getPaymentMethodAuditLogsWithHistoryForId(paymentMethodModelDao.getId(), AuditLevel.FULL, internalCallContext);
+        Assert.assertEquals(auditLogsWithHistory.size(), 2);
+
+        history1 = (PaymentMethodModelDao) auditLogsWithHistory.get(0).getEntity();
+        PaymentMethodModelDao history2 = (PaymentMethodModelDao) auditLogsWithHistory.get(1).getEntity();
+        Assert.assertEquals(auditLogsWithHistory.get(0).getChangeType(), ChangeType.INSERT);
+        Assert.assertEquals(history1.getAccountRecordId(), paymentMethodModelDao.getAccountRecordId());
+        Assert.assertEquals(history1.getTenantRecordId(), paymentMethodModelDao.getTenantRecordId());
+        Assert.assertEquals(history1.getExternalKey(), paymentMethodModelDao.getExternalKey());
+        Assert.assertTrue(history1.isActive());
         // Note: it looks like we don't consider this as a DELETE, probably because we can un-delete such payment methods?
-        Assert.assertEquals(history2.get(1).getChangeType(), ChangeType.UPDATE);
-        Assert.assertEquals(history2.get(1).getEntity().getAccountRecordId(), paymentMethodModelDao.getAccountRecordId());
-        Assert.assertEquals(history2.get(1).getEntity().getTenantRecordId(), paymentMethodModelDao.getTenantRecordId());
-        Assert.assertEquals(history2.get(1).getEntity().getExternalKey(), paymentMethodModelDao.getExternalKey());
+        Assert.assertEquals(auditLogsWithHistory.get(1).getChangeType(), ChangeType.UPDATE);
+        Assert.assertEquals(history2.getAccountRecordId(), paymentMethodModelDao.getAccountRecordId());
+        Assert.assertEquals(history2.getTenantRecordId(), paymentMethodModelDao.getTenantRecordId());
+        Assert.assertEquals(history2.getExternalKey(), paymentMethodModelDao.getExternalKey());
         // Note: upon deletion, the recorded state is the same as before the delete
-        Assert.assertTrue(history2.get(1).getEntity().isActive());
-    }
-
-    private List<EntityHistoryModelDao<PaymentMethodModelDao, PaymentMethod>> getPaymentMethodHistory(final Long paymentMethodRecordId) {
-        // See https://github.com/killbill/killbill/issues/335
-        final PaymentMethodSqlDao paymentMethodSqlDao = dbi.onDemand(PaymentMethodSqlDao.class);
-        return paymentMethodSqlDao.getHistoryForTargetRecordId(paymentMethodRecordId, internalCallContext);
+        Assert.assertTrue(history2.isActive());
     }
 }
diff --git a/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java b/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java
index 1db8422..5380fee 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java
@@ -39,6 +39,8 @@ import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.TransactionStatus;
 import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.entity.DefaultPagination;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.MockEntityDaoBase;
@@ -426,6 +428,26 @@ public class MockPaymentDao extends MockEntityDaoBase<PaymentModelDao, Payment, 
     }
 
     @Override
+    public List<AuditLogWithHistory> getPaymentAuditLogsWithHistoryForId(final UUID paymentId, final AuditLevel auditLevel, final InternalTenantContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<AuditLogWithHistory> getPaymentMethodAuditLogsWithHistoryForId(final UUID paymentMethodId, final AuditLevel auditLevel, final InternalTenantContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<AuditLogWithHistory> getPaymentAttemptAuditLogsWithHistoryForId(final UUID paymentAttemptId, final AuditLevel auditLevel, final InternalTenantContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<AuditLogWithHistory> getPaymentTransactionAuditLogsWithHistoryForId(final UUID paymentTransactionId, final AuditLevel auditLevel, final InternalTenantContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public PaymentMethodModelDao getPaymentMethodIncludedDeleted(final UUID paymentMethodId, final InternalTenantContext context) {
         return getPaymentMethod(paymentMethodId, context);
     }
diff --git a/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java b/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
index 89daedc..8c5599a 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
@@ -29,13 +29,13 @@ import org.killbill.billing.account.api.Account;
 import org.killbill.billing.api.FlakyRetryAnalyzer;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
-import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionStatus;
 import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.dao.PluginPropertySerializer.PluginPropertySerializerException;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.audit.ChangeType;
-import org.killbill.billing.util.dao.EntityHistoryModelDao;
 import org.killbill.billing.util.entity.Pagination;
 import org.testng.Assert;
 import org.testng.annotations.Test;
@@ -119,16 +119,18 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(savedPayment.getPaymentMethodId(), paymentModelDao.getPaymentMethodId());
         assertNull(savedPayment.getStateName());
 
-        final List<EntityHistoryModelDao<PaymentModelDao, Payment>> history1 = getPaymentHistory(savedPayment.getRecordId());
-        Assert.assertEquals(history1.size(), 1);
-        Assert.assertEquals(history1.get(0).getChangeType(), ChangeType.INSERT);
-        Assert.assertEquals(history1.get(0).getEntity().getAccountRecordId(), savedPayment.getAccountRecordId());
-        Assert.assertEquals(history1.get(0).getEntity().getTenantRecordId(), savedPayment.getTenantRecordId());
-        Assert.assertEquals(history1.get(0).getEntity().getExternalKey(), savedPayment.getExternalKey());
-        Assert.assertEquals(history1.get(0).getEntity().getStateName(), savedPayment.getStateName());
-        Assert.assertEquals(history1.get(0).getEntity().getLastSuccessStateName(), savedPayment.getLastSuccessStateName());
-        Assert.assertNull(history1.get(0).getEntity().getStateName());
-        Assert.assertNull(history1.get(0).getEntity().getLastSuccessStateName());
+        List<AuditLogWithHistory> auditLogsWithHistory = paymentDao.getPaymentAuditLogsWithHistoryForId(savedPayment.getId(), AuditLevel.FULL, internalCallContext);
+        Assert.assertEquals(auditLogsWithHistory.size(), 1);
+
+        PaymentModelDao history1 = (PaymentModelDao) auditLogsWithHistory.get(0).getEntity();
+        Assert.assertEquals(auditLogsWithHistory.get(0).getChangeType(), ChangeType.INSERT);
+        Assert.assertEquals(history1.getAccountRecordId(), savedPayment.getAccountRecordId());
+        Assert.assertEquals(history1.getTenantRecordId(), savedPayment.getTenantRecordId());
+        Assert.assertEquals(history1.getExternalKey(), savedPayment.getExternalKey());
+        Assert.assertEquals(history1.getStateName(), savedPayment.getStateName());
+        Assert.assertEquals(history1.getLastSuccessStateName(), savedPayment.getLastSuccessStateName());
+        Assert.assertNull(history1.getStateName());
+        Assert.assertNull(history1.getLastSuccessStateName());
 
         final PaymentModelDao savedPayment2 = paymentDao.getPayment(savedPayment.getId(), internalCallContext);
         assertEquals(savedPayment2.getId(), paymentModelDao.getId());
@@ -175,19 +177,22 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(savedTransactionModelDao2.getAmount().compareTo(BigDecimal.TEN), 0);
         assertEquals(savedTransactionModelDao2.getCurrency(), Currency.AED);
 
-        final List<EntityHistoryModelDao<PaymentModelDao, Payment>> history2 = getPaymentHistory(savedPayment.getRecordId());
-        Assert.assertEquals(history2.size(), 2);
-        Assert.assertEquals(history2.get(0).getChangeType(), ChangeType.INSERT);
-        Assert.assertEquals(history2.get(0).getEntity().getAccountRecordId(), savedPayment.getAccountRecordId());
-        Assert.assertEquals(history2.get(0).getEntity().getTenantRecordId(), savedPayment.getTenantRecordId());
-        Assert.assertEquals(history2.get(0).getEntity().getExternalKey(), savedPayment.getExternalKey());
-        Assert.assertEquals(history2.get(1).getChangeType(), ChangeType.UPDATE);
-        Assert.assertEquals(history2.get(1).getEntity().getAccountRecordId(), savedPayment.getAccountRecordId());
-        Assert.assertEquals(history2.get(1).getEntity().getTenantRecordId(), savedPayment.getTenantRecordId());
-        Assert.assertEquals(history2.get(1).getEntity().getExternalKey(), savedPayment.getExternalKey());
-        Assert.assertTrue(history2.get(1).getEntity().getUpdatedDate().compareTo(history2.get(0).getEntity().getUpdatedDate()) >= 0);
-        Assert.assertNull(history2.get(1).getEntity().getStateName());
-        Assert.assertNull(history2.get(1).getEntity().getLastSuccessStateName());
+        auditLogsWithHistory = paymentDao.getPaymentAuditLogsWithHistoryForId(savedPayment.getId(), AuditLevel.FULL, internalCallContext);
+        Assert.assertEquals(auditLogsWithHistory.size(), 2);
+
+        history1 = (PaymentModelDao) auditLogsWithHistory.get(0).getEntity();
+        PaymentModelDao history2 = (PaymentModelDao) auditLogsWithHistory.get(1).getEntity();
+        Assert.assertEquals(auditLogsWithHistory.get(0).getChangeType(), ChangeType.INSERT);
+        Assert.assertEquals(history1.getAccountRecordId(), savedPayment.getAccountRecordId());
+        Assert.assertEquals(history1.getTenantRecordId(), savedPayment.getTenantRecordId());
+        Assert.assertEquals(history1.getExternalKey(), savedPayment.getExternalKey());
+        Assert.assertEquals(auditLogsWithHistory.get(1).getChangeType(), ChangeType.UPDATE);
+        Assert.assertEquals(history2.getAccountRecordId(), savedPayment.getAccountRecordId());
+        Assert.assertEquals(history2.getTenantRecordId(), savedPayment.getTenantRecordId());
+        Assert.assertEquals(history2.getExternalKey(), savedPayment.getExternalKey());
+        Assert.assertTrue(history2.getUpdatedDate().compareTo(history2.getUpdatedDate()) >= 0);
+        Assert.assertNull(history2.getStateName());
+        Assert.assertNull(history2.getLastSuccessStateName());
 
         final List<PaymentTransactionModelDao> transactions = paymentDao.getTransactionsForPayment(savedPayment.getId(), internalCallContext);
         assertEquals(transactions.size(), 2);
@@ -203,24 +208,28 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(savedPayment4.getStateName(), "AUTH_ABORTED");
         assertEquals(savedPayment4.getLastSuccessStateName(), "AUTH_SUCCESS");
 
-        final List<EntityHistoryModelDao<PaymentModelDao, Payment>> history3 = getPaymentHistory(savedPayment.getRecordId());
-        Assert.assertEquals(history3.size(), 3);
-        Assert.assertEquals(history3.get(0).getChangeType(), ChangeType.INSERT);
-        Assert.assertEquals(history3.get(0).getEntity().getAccountRecordId(), savedPayment.getAccountRecordId());
-        Assert.assertEquals(history3.get(0).getEntity().getTenantRecordId(), savedPayment.getTenantRecordId());
-        Assert.assertEquals(history3.get(0).getEntity().getExternalKey(), savedPayment.getExternalKey());
-        Assert.assertEquals(history3.get(1).getChangeType(), ChangeType.UPDATE);
-        Assert.assertEquals(history3.get(1).getEntity().getAccountRecordId(), savedPayment.getAccountRecordId());
-        Assert.assertEquals(history3.get(1).getEntity().getTenantRecordId(), savedPayment.getTenantRecordId());
-        Assert.assertEquals(history3.get(1).getEntity().getExternalKey(), savedPayment.getExternalKey());
-        Assert.assertTrue(history3.get(1).getEntity().getUpdatedDate().compareTo(history3.get(0).getEntity().getUpdatedDate()) >= 0);
-        Assert.assertEquals(history3.get(2).getChangeType(), ChangeType.UPDATE);
-        Assert.assertEquals(history3.get(2).getEntity().getAccountRecordId(), savedPayment.getAccountRecordId());
-        Assert.assertEquals(history3.get(2).getEntity().getTenantRecordId(), savedPayment.getTenantRecordId());
-        Assert.assertEquals(history3.get(2).getEntity().getExternalKey(), savedPayment.getExternalKey());
-        Assert.assertTrue(history3.get(2).getEntity().getUpdatedDate().compareTo(history3.get(2).getEntity().getUpdatedDate()) >= 0);
-        Assert.assertEquals(history3.get(2).getEntity().getStateName(), savedPayment4.getStateName());
-        Assert.assertEquals(history3.get(2).getEntity().getLastSuccessStateName(), savedPayment4.getLastSuccessStateName());
+        auditLogsWithHistory = paymentDao.getPaymentAuditLogsWithHistoryForId(savedPayment.getId(), AuditLevel.FULL, internalCallContext);
+        Assert.assertEquals(auditLogsWithHistory.size(), 3);
+
+        history1 = (PaymentModelDao) auditLogsWithHistory.get(0).getEntity();
+        history2 = (PaymentModelDao) auditLogsWithHistory.get(1).getEntity();
+        final PaymentModelDao history3 = (PaymentModelDao) auditLogsWithHistory.get(2).getEntity();
+        Assert.assertEquals(auditLogsWithHistory.get(0).getChangeType(), ChangeType.INSERT);
+        Assert.assertEquals(history1.getAccountRecordId(), savedPayment.getAccountRecordId());
+        Assert.assertEquals(history1.getTenantRecordId(), savedPayment.getTenantRecordId());
+        Assert.assertEquals(history1.getExternalKey(), savedPayment.getExternalKey());
+        Assert.assertEquals(auditLogsWithHistory.get(1).getChangeType(), ChangeType.UPDATE);
+        Assert.assertEquals(history2.getAccountRecordId(), savedPayment.getAccountRecordId());
+        Assert.assertEquals(history2.getTenantRecordId(), savedPayment.getTenantRecordId());
+        Assert.assertEquals(history2.getExternalKey(), savedPayment.getExternalKey());
+        Assert.assertTrue(auditLogsWithHistory.get(1).getEntity().getUpdatedDate().compareTo(auditLogsWithHistory.get(0).getEntity().getUpdatedDate()) >= 0);
+        Assert.assertEquals(auditLogsWithHistory.get(2).getChangeType(), ChangeType.UPDATE);
+        Assert.assertEquals(history3.getAccountRecordId(), savedPayment.getAccountRecordId());
+        Assert.assertEquals(history3.getTenantRecordId(), savedPayment.getTenantRecordId());
+        Assert.assertEquals(history3.getExternalKey(), savedPayment.getExternalKey());
+        Assert.assertTrue(history3.getUpdatedDate().compareTo(history3.getUpdatedDate()) >= 0);
+        Assert.assertEquals(history3.getStateName(), savedPayment4.getStateName());
+        Assert.assertEquals(history3.getLastSuccessStateName(), savedPayment4.getLastSuccessStateName());
 
         final PaymentTransactionModelDao savedTransactionModelDao4 = paymentDao.getPaymentTransaction(savedTransactionModelDao2.getId(), internalCallContext);
         assertEquals(savedTransactionModelDao4.getTransactionExternalKey(), transactionExternalKey2);
@@ -597,11 +606,5 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
             }
         }));
     }
-
-    private List<EntityHistoryModelDao<PaymentModelDao, Payment>> getPaymentHistory(final Long paymentRecordId) {
-        // See https://github.com/killbill/killbill/issues/335
-        final PaymentSqlDao paymentSqlDao = dbi.onDemand(PaymentSqlDao.class);
-        return paymentSqlDao.getHistoryForTargetRecordId(paymentRecordId, internalCallContext);
-    }
 }
 
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
index 0e6ce18..704250d 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
@@ -47,6 +47,7 @@ import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.bus.PaymentBusEventHandler;
 import org.killbill.billing.payment.core.janitor.Janitor;
 import org.killbill.billing.payment.dao.PaymentAttemptModelDao;
+import org.killbill.billing.payment.dao.PaymentModelDao;
 import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.killbill.billing.payment.glue.DefaultPaymentService;
 import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
@@ -55,6 +56,8 @@ import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
 import org.killbill.billing.payment.provider.DefaultNoOpPaymentInfoPlugin;
 import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
 import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.entity.dao.DBRouterUntyped;
 import org.killbill.billing.util.entity.dao.DBRouterUntyped.THREAD_STATE;
@@ -338,7 +341,7 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
                                                            "foo", "bar", internalCallContext);
         testListener.assertListenerStatus();
 
-        final List<PaymentTransactionModelDao> paymentTransactionHistoryBeforeJanitor = getPaymentTransactionHistory(transactionExternalKey);
+        final List<AuditLogWithHistory> paymentTransactionHistoryBeforeJanitor = paymentDao.getPaymentTransactionAuditLogsWithHistoryForId(payment.getTransactions().get(0).getId(), AuditLevel.FULL, internalCallContext);
         Assert.assertEquals(paymentTransactionHistoryBeforeJanitor.size(), 3);
 
         // Move clock for notification to be processed
@@ -348,9 +351,11 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
         testListener.assertListenerStatus();
 
         // Proves the Janitor ran (and updated the transaction)
-        final List<PaymentTransactionModelDao> paymentTransactionHistoryAfterJanitor = getPaymentTransactionHistory(transactionExternalKey);
+        final List<AuditLogWithHistory> paymentTransactionHistoryAfterJanitor = paymentDao.getPaymentTransactionAuditLogsWithHistoryForId(payment.getTransactions().get(0).getId(), AuditLevel.FULL, internalCallContext);
         Assert.assertEquals(paymentTransactionHistoryAfterJanitor.size(), 4);
-        Assert.assertEquals(paymentTransactionHistoryAfterJanitor.get(3).getTransactionStatus(), TransactionStatus.PAYMENT_FAILURE);
+
+        PaymentTransactionModelDao history3 = (PaymentTransactionModelDao) paymentTransactionHistoryAfterJanitor.get(3).getEntity();
+        Assert.assertEquals(history3.getTransactionStatus(), TransactionStatus.PAYMENT_FAILURE);
 
         final Payment updatedPayment = paymentApi.getPayment(payment.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
         // Janitor should have moved us to PAYMENT_FAILURE
@@ -388,11 +393,11 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
         // NO because we will keep retrying as we can't fix it...
         //assertNotificationsCompleted(internalCallContext, 5);
 
-        final List<PaymentTransactionModelDao> paymentTransactionHistoryBeforeJanitor = getPaymentTransactionHistory(transactionExternalKey);
+        final List<AuditLogWithHistory> paymentTransactionHistoryBeforeJanitor = paymentDao.getPaymentTransactionAuditLogsWithHistoryForId(payment.getTransactions().get(0).getId(), AuditLevel.FULL, internalCallContext);
         Assert.assertEquals(paymentTransactionHistoryBeforeJanitor.size(), 3);
 
         // Nothing new happened
-        final List<PaymentTransactionModelDao> paymentTransactionHistoryAfterJanitor = getPaymentTransactionHistory(transactionExternalKey);
+        final List<AuditLogWithHistory> paymentTransactionHistoryAfterJanitor = paymentDao.getPaymentTransactionAuditLogsWithHistoryForId(payment.getTransactions().get(0).getId(), AuditLevel.FULL, internalCallContext);
         Assert.assertEquals(paymentTransactionHistoryAfterJanitor.size(), 3);
     }
 
@@ -528,35 +533,6 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
         return result;
     }
 
-    // I wish we had a simplest way to query our history rows..
-    private List<PaymentTransactionModelDao> getPaymentTransactionHistory(final String transactionExternalKey) {
-        return dbi.withHandle(new HandleCallback<List<PaymentTransactionModelDao>>() {
-            @Override
-            public List<PaymentTransactionModelDao> withHandle(final Handle handle) throws Exception {
-                final List<Map<String, Object>> queryResult = handle.select("select * from payment_transaction_history where transaction_external_key = ? order by record_id asc",
-                                                                            transactionExternalKey);
-                final List<PaymentTransactionModelDao> result = new ArrayList<PaymentTransactionModelDao>(queryResult.size());
-                for (final Map<String, Object> row : queryResult) {
-                    final PaymentTransactionModelDao transactionModelDao = new PaymentTransactionModelDao(UUID.fromString((String) row.get("id")),
-                                                                                                          null,
-                                                                                                          (String) row.get("transaction_external_key"),
-                                                                                                          null,
-                                                                                                          null,
-                                                                                                          UUID.fromString((String) row.get("payment_id")),
-                                                                                                          TransactionType.valueOf((String) row.get("transaction_type")),
-                                                                                                          null,
-                                                                                                          TransactionStatus.valueOf((String) row.get("transaction_status")),
-                                                                                                          (BigDecimal) row.get("amount"),
-                                                                                                          Currency.valueOf((String) row.get("currency")),
-                                                                                                          (String) row.get("gateway_error_code"),
-                                                                                                          String.valueOf(row.get("gateway_error_msg")));
-                    result.add(transactionModelDao);
-                }
-                return result;
-            }
-        });
-    }
-
     private void assertNotificationsCompleted(final InternalCallContext internalCallContext, final long timeoutSec) {
         try {
             await().atMost(timeoutSec, SECONDS).until(new Callable<Boolean>() {
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccount.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccount.java
index d479317..031493d 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccount.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccount.java
@@ -20,6 +20,7 @@ package org.killbill.billing.jaxrs;
 
 import java.math.BigDecimal;
 import java.util.Collection;
+import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
@@ -53,6 +54,7 @@ import com.google.inject.Inject;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
@@ -559,4 +561,37 @@ public class TestAccount extends TestJaxrsBase {
         assertEquals(auditLogsJson.get(3).getObjectId(), accountCustomFields.get(2).getCustomFieldId());
     }
 
+    @Test(groups = "slow", description = "retrieve account logs")
+    public void testGetAccountAuditLogsWithHistory() throws Exception {
+        final Account accountJson = createAccount();
+        assertNotNull(accountJson);
+
+        // Update Account
+        final Account newInput = new Account(accountJson.getAccountId(),
+                                             "zozo", 4, accountJson.getExternalKey(), "rr@google.com", 18,
+                                             "USD", null, false, null, null, "UTC",
+                                             "bl1", "bh2", "", "", "ca", "San Francisco", "usa", "en", "415-255-2991",
+                                             "notes", false, false, null, null);
+
+        final Account updatedAccount = killBillClient.updateAccount(newInput, requestOptions);
+
+
+        final List<AuditLog> auditLogWithHistories = killBillClient.getAccountAuditLogsWithHistory(accountJson.getAccountId());
+        assertEquals(auditLogWithHistories.size(), 2);
+        assertEquals(auditLogWithHistories.get(0).getChangeType(), ChangeType.INSERT.toString());
+        assertEquals(auditLogWithHistories.get(0).getObjectType(), ObjectType.ACCOUNT);
+        assertEquals(auditLogWithHistories.get(0).getObjectId(), accountJson.getAccountId());
+
+        final LinkedHashMap history1 = (LinkedHashMap) auditLogWithHistories.get(0).getHistory();
+        assertNotNull(history1);
+        assertEquals(history1.get("externalKey"), accountJson.getExternalKey());
+        assertEquals(history1.get("name"), accountJson.getName());
+
+        final LinkedHashMap history2 = (LinkedHashMap) auditLogWithHistories.get(1).getHistory();
+        assertNotNull(history2);
+        assertEquals(history2.get("externalKey"), accountJson.getExternalKey());
+        assertNotEquals(history2.get("name"), accountJson.getName());
+        assertEquals(history2.get("name"), "zozo");
+    }
+
 }
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccountEmail.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccountEmail.java
index f0ef8d6..29b6613 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccountEmail.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccountEmail.java
@@ -18,14 +18,21 @@
 
 package org.killbill.billing.jaxrs;
 
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.UUID;
 
+import org.killbill.billing.ObjectType;
 import org.killbill.billing.client.model.Account;
 import org.killbill.billing.client.model.AccountEmail;
+import org.killbill.billing.client.model.AuditLog;
+import org.killbill.billing.util.audit.ChangeType;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
 public class TestAccountEmail extends TestJaxrsBase {
 
     @Test(groups = "slow", description = "Can create and delete account emails")
@@ -75,4 +82,30 @@ public class TestAccountEmail extends TestJaxrsBase {
         killBillClient.addEmailToAccount(accountEmailJson2, createdBy, reason, comment);
         Assert.assertEquals(killBillClient.getEmailsForAccount(accountId), fourthEmails);
     }
+
+    @Test(groups = "slow", description = "retrieve account logs")
+    public void testGetAccountEmailAuditLogsWithHistory() throws Exception {
+        final Account accountJson = createAccount();
+        assertNotNull(accountJson);
+
+        final String email1 = UUID.randomUUID().toString();
+        final AccountEmail accountEmailJson1 = new AccountEmail(accountJson.getAccountId(), email1);
+
+        // Add an email
+        killBillClient.addEmailToAccount(accountEmailJson1, requestOptions);
+
+        // get all audit for the account
+        final List<AuditLog> auditLogsJson = killBillClient.getAccountAuditLogs(accountJson.getAccountId());
+        Assert.assertEquals(auditLogsJson.size(), 2);
+
+        final List<AuditLog> emailAuditLogWithHistories = killBillClient.getAccountEmailAuditLogsWithHistory(accountJson.getAccountId(), auditLogsJson.get(0).getObjectId());
+        assertEquals(emailAuditLogWithHistories.size(), 1);
+        assertEquals(emailAuditLogWithHistories.get(0).getChangeType(), ChangeType.INSERT.toString());
+        assertEquals(emailAuditLogWithHistories.get(0).getObjectType(), ObjectType.ACCOUNT_EMAIL);
+        assertEquals(emailAuditLogWithHistories.get(0).getObjectId(), auditLogsJson.get(0).getObjectId());
+
+        final LinkedHashMap history1 = (LinkedHashMap) emailAuditLogWithHistories.get(0).getHistory();
+        assertNotNull(history1);
+        assertEquals(history1.get("email"), email1);
+    }
 }
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCustomField.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCustomField.java
index c66ff3b..a0fadbb 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCustomField.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCustomField.java
@@ -18,6 +18,8 @@
 
 package org.killbill.billing.jaxrs;
 
+import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
@@ -25,14 +27,19 @@ import javax.annotation.Nullable;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.client.KillBillClientException;
 import org.killbill.billing.client.model.Account;
+import org.killbill.billing.client.model.AuditLog;
 import org.killbill.billing.client.model.CustomField;
 import org.killbill.billing.client.model.CustomFields;
 import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.ChangeType;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
 public class TestCustomField extends TestJaxrsBase {
 
 
@@ -105,6 +112,39 @@ public class TestCustomField extends TestJaxrsBase {
         Assert.assertEquals(allBundleCustomFieldsForAccount.size(), 5);
     }
 
+    @Test(groups = "slow", description = "retrieve account logs")
+    public void testCustomFieldTagAuditLogsWithHistory() throws Exception {
+        final Account accountJson = createAccount();
+        assertNotNull(accountJson);
+
+        final CustomField customField = new CustomField();
+        customField.setName("custom");
+        customField.setValue(UUID.randomUUID().toString().substring(0, 5));
+        killBillClient.createAccountCustomField(accountJson.getAccountId(), customField, requestOptions);
+
+        // get all audit for the account
+        final List<AuditLog> auditLogsJson = killBillClient.getAccountAuditLogs(accountJson.getAccountId());
+        Assert.assertEquals(auditLogsJson.size(), 2);
+        UUID objectId = null;
+        for (AuditLog auditLog : auditLogsJson) {
+            if (auditLog.getObjectType().equals(ObjectType.CUSTOM_FIELD)) {
+                objectId = auditLog.getObjectId();
+                break;
+            }
+        }
+        assertNotNull(objectId);
+        final List<AuditLog> customFieldAuditLogWithHistory = killBillClient.getCustomFieldsAuditLogsWithHistory(accountJson.getAccountId(), objectId);
+        assertEquals(customFieldAuditLogWithHistory.size(), 1);
+        assertEquals(customFieldAuditLogWithHistory.get(0).getChangeType(), ChangeType.INSERT.toString());
+        assertEquals(customFieldAuditLogWithHistory.get(0).getObjectType(), ObjectType.CUSTOM_FIELD);
+        assertEquals(customFieldAuditLogWithHistory.get(0).getObjectId(), objectId);
+
+        final LinkedHashMap history1 = (LinkedHashMap) customFieldAuditLogWithHistory.get(0).getHistory();
+        assertNotNull(history1);
+        assertEquals(history1.get("fieldName"), "custom");
+
+    }
+
     private void doSearchCustomField(final String searchKey, @Nullable final CustomField expectedCustomField) throws KillBillClientException {
         final CustomFields customFields = killBillClient.searchCustomFields(searchKey, requestOptions);
         if (expectedCustomField == null) {
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
index d08a80c..eb3e108 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
@@ -21,6 +21,8 @@ import java.math.BigDecimal;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -31,6 +33,7 @@ import org.killbill.billing.ObjectType;
 import org.killbill.billing.client.KillBillClientException;
 import org.killbill.billing.client.RequestOptions;
 import org.killbill.billing.client.model.Account;
+import org.killbill.billing.client.model.AuditLog;
 import org.killbill.billing.client.model.ComboPaymentTransaction;
 import org.killbill.billing.client.model.InvoicePayments;
 import org.killbill.billing.client.model.Payment;
@@ -51,6 +54,7 @@ import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
 import org.killbill.billing.payment.provider.MockPaymentControlProviderPlugin;
 import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
 import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.ChangeType;
 import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
@@ -64,6 +68,7 @@ import com.google.inject.Inject;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
@@ -766,6 +771,48 @@ public class TestPayment extends TestJaxrsBase {
         Assert.assertEquals(paymentTransactionTag.get(0).getTagDefinitionName(), tagDefinitionName);
     }
 
+    @Test(groups = "slow", description = "retrieve account logs")
+    public void testPaymentAuditLogsWithHistory() throws Exception {
+        final Account account = createAccountWithDefaultPaymentMethod();
+        final String externalPaymentKey = UUID.randomUUID().toString();
+        final UUID paymentId = testCreateRetrievePayment(account, null, externalPaymentKey, 1);
+
+        final Payment payment = killBillClient.getPaymentByExternalKey(externalPaymentKey);
+        assertEquals(payment.getPaymentId(), paymentId);
+
+        final List<AuditLog> paymentAuditLogWithHistory = killBillClient.getPaymentAuditLogsWithHistory(account.getAccountId(), paymentId);
+        assertEquals(paymentAuditLogWithHistory.size(), 8);
+        assertEquals(paymentAuditLogWithHistory.get(0).getChangeType(), ChangeType.INSERT.toString());
+        assertEquals(paymentAuditLogWithHistory.get(0).getObjectType(), ObjectType.PAYMENT);
+        assertEquals(paymentAuditLogWithHistory.get(0).getObjectId(), paymentId);
+
+        final LinkedHashMap history1 = (LinkedHashMap) paymentAuditLogWithHistory.get(0).getHistory();
+        assertNotNull(history1);
+        assertEquals(history1.get("stateName"), null );
+        final LinkedHashMap history2 = (LinkedHashMap) paymentAuditLogWithHistory.get(1).getHistory();
+        assertNotNull(history2);
+        assertEquals(history2.get("stateName"), "AUTH_SUCCESS" );
+        final LinkedHashMap history3 = (LinkedHashMap) paymentAuditLogWithHistory.get(2).getHistory();
+        assertNotNull(history3);
+        assertEquals(history3.get("stateName"), "AUTH_SUCCESS" );
+        final LinkedHashMap history4 = (LinkedHashMap) paymentAuditLogWithHistory.get(3).getHistory();
+        assertNotNull(history4);
+        assertEquals(history4.get("stateName"), "CAPTURE_SUCCESS" );
+        final LinkedHashMap history5 = (LinkedHashMap) paymentAuditLogWithHistory.get(4).getHistory();
+        assertNotNull(history5);
+        assertEquals(history5.get("stateName"), "CAPTURE_SUCCESS" );
+        final LinkedHashMap history6 = (LinkedHashMap) paymentAuditLogWithHistory.get(5).getHistory();
+        assertNotNull(history6);
+        assertEquals(history6.get("stateName"), "CAPTURE_SUCCESS" );
+        final LinkedHashMap history7 = (LinkedHashMap) paymentAuditLogWithHistory.get(6).getHistory();
+        assertNotNull(history7);
+        assertEquals(history7.get("stateName"), "CAPTURE_SUCCESS" );
+        final LinkedHashMap history8 = (LinkedHashMap) paymentAuditLogWithHistory.get(7).getHistory();
+        assertNotNull(history8);
+        assertEquals(history8.get("stateName"), "REFUND_SUCCESS" );
+
+    }
+
     private UUID testCreateRetrievePayment(final Account account, @Nullable final UUID paymentMethodId,
                                            final String paymentExternalKey, final int paymentNb) throws Exception {
         // Authorization
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPaymentMethod.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPaymentMethod.java
index e44da34..c2e6dc9 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPaymentMethod.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPaymentMethod.java
@@ -20,20 +20,26 @@ package org.killbill.billing.jaxrs;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.UUID;
 
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.client.model.Account;
+import org.killbill.billing.client.model.AuditLog;
 import org.killbill.billing.client.model.CustomField;
 import org.killbill.billing.client.model.CustomFields;
 import org.killbill.billing.client.model.PaymentMethod;
 import org.killbill.billing.client.model.PaymentMethods;
 import org.killbill.billing.client.model.PluginProperty;
 import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.ChangeType;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
 public class TestPaymentMethod extends TestJaxrsBase {
 
     @Test(groups = "slow", description = "Create/retrieve by externalKey")
@@ -137,6 +143,24 @@ public class TestPaymentMethod extends TestJaxrsBase {
         Assert.assertEquals(deletedCustomFields.size(), 0);
     }
 
+    @Test(groups = "slow", description = "retrieve account logs")
+    public void testPaymentMethodAuditLogsWithHistory() throws Exception {
+        final Account account = createAccountWithDefaultPaymentMethod();
+        assertNotNull(account);
+        final UUID paymentMethodId = account.getPaymentMethodId();
+
+        final List<AuditLog> paymentMethodAuditLogWithHistory = killBillClient.getPaymentMethodAuditLogsWithHistory(account.getAccountId(), paymentMethodId);
+        assertEquals(paymentMethodAuditLogWithHistory.size(), 1);
+        assertEquals(paymentMethodAuditLogWithHistory.get(0).getChangeType(), ChangeType.INSERT.toString());
+        assertEquals(paymentMethodAuditLogWithHistory.get(0).getObjectType(), ObjectType.PAYMENT_METHOD);
+        assertEquals(paymentMethodAuditLogWithHistory.get(0).getObjectId(), paymentMethodId);
+
+        final LinkedHashMap history1 = (LinkedHashMap) paymentMethodAuditLogWithHistory.get(0).getHistory();
+        assertNotNull(history1);
+        assertEquals(history1.get("accountId"), account.getAccountId().toString());
+
+    }
+
     private void doSearch(final String searchKey, final PaymentMethod paymentMethodJson) throws Exception {
         final List<PaymentMethod> results1 = killBillClient.searchPaymentMethodsByKey(searchKey, true);
         Assert.assertEquals(results1.size(), 1);
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestTag.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestTag.java
index ae69442..a4142aa 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestTag.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestTag.java
@@ -18,6 +18,7 @@
 
 package org.killbill.billing.jaxrs;
 
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.UUID;
 
@@ -29,11 +30,13 @@ import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.client.KillBillClientException;
 import org.killbill.billing.client.model.Account;
+import org.killbill.billing.client.model.AuditLog;
 import org.killbill.billing.client.model.Subscription;
 import org.killbill.billing.client.model.Tag;
 import org.killbill.billing.client.model.TagDefinition;
 import org.killbill.billing.client.model.Tags;
 import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.ChangeType;
 import org.killbill.billing.util.tag.ControlTagType;
 import org.killbill.billing.util.tag.dao.SystemTags;
 import org.testng.Assert;
@@ -165,6 +168,40 @@ public class TestTag extends TestJaxrsBase {
         }
     }
 
+    @Test(groups = "slow", description = "retrieve account logs")
+    public void testGetTagAuditLogsWithHistory() throws Exception {
+        final Account accountJson = createAccount();
+        assertNotNull(accountJson);
+
+        final TagDefinition accountTagDefInput = new TagDefinition(null, false, "accounttagdef", "nothing special",  ImmutableList.<ObjectType>of(ObjectType.TRANSACTION));
+        final TagDefinition accountTagDef = killBillClient.createTagDefinition(accountTagDefInput, requestOptions);
+        killBillClient.createAccountTag(accountJson.getAccountId(), accountTagDef.getId(), requestOptions);
+
+        // get all audit for the account
+        final List<AuditLog> auditLogsJson = killBillClient.getAccountAuditLogs(accountJson.getAccountId());
+        Assert.assertEquals(auditLogsJson.size(), 2);
+        UUID objectId = null;
+        for (AuditLog auditLog : auditLogsJson) {
+            if (auditLog.getObjectType().equals(ObjectType.TAG)) {
+                objectId = auditLog.getObjectId();
+                break;
+            }
+        }
+        assertNotNull(objectId);
+        final List<AuditLog> tagAuditLogWithHistories = killBillClient.getTagAuditLogsWithHistory(accountJson.getAccountId(), objectId);
+        assertEquals(tagAuditLogWithHistories.size(), 1);
+        assertEquals(tagAuditLogWithHistories.get(0).getChangeType(), ChangeType.INSERT.toString());
+        assertEquals(tagAuditLogWithHistories.get(0).getObjectType(), ObjectType.TAG);
+        assertEquals(tagAuditLogWithHistories.get(0).getObjectId(), objectId);
+
+        final LinkedHashMap history1 = (LinkedHashMap) tagAuditLogWithHistories.get(0).getHistory();
+        assertNotNull(history1);
+        assertEquals(history1.get("tagDefinitionId"), accountTagDef.getId().toString());
+        assertEquals(history1.get("objectId"), accountJson.getAccountId().toString());
+        assertEquals(history1.get("objectType"), ObjectType.ACCOUNT.toString());
+
+    }
+
     @Test(groups = "slow", description = "Can paginate through all tags")
     public void testTagsPagination() throws Exception {
         final Account account = createAccount();
diff --git a/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java b/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java
index b5e08a8..34d3d50 100644
--- a/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java
+++ b/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java
@@ -191,7 +191,7 @@ public class DefaultAuditDao implements AuditDao {
             @Override
             public List<AuditLogWithHistory> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
                 final Long targetRecordId = dbRouter.onDemand(true).getRecordIdFromObject(objectId.toString(), tableName.getTableName());
-                final List<EntityHistoryModelDao> objectHistory = transactional.getHistoryForTargetRecordId(targetRecordId, context);
+                final List<EntityHistoryModelDao> objectHistory = transactional.getHistoryForTargetRecordId(true, targetRecordId, context);
 
                 return ImmutableList.<AuditLogWithHistory>copyOf(Collections2.transform(entitySqlDaoWrapperFactory.become(EntitySqlDao.class).getAuditLogsViaHistoryForTargetRecordId(historyTableName.name(),
                                                                                                                                                                                       historyTableName.getTableName().toLowerCase(),
diff --git a/util/src/main/java/org/killbill/billing/util/customfield/api/DefaultCustomFieldUserApi.java b/util/src/main/java/org/killbill/billing/util/customfield/api/DefaultCustomFieldUserApi.java
index 45d033b..0a48837 100644
--- a/util/src/main/java/org/killbill/billing/util/customfield/api/DefaultCustomFieldUserApi.java
+++ b/util/src/main/java/org/killbill/billing/util/customfield/api/DefaultCustomFieldUserApi.java
@@ -22,8 +22,10 @@ import java.util.UUID;
 
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.CustomFieldApiException;
 import org.killbill.billing.util.api.CustomFieldUserApi;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
@@ -149,6 +151,11 @@ public class DefaultCustomFieldUserApi implements CustomFieldUserApi {
         return withCustomFieldsTransform(customFieldDao.getCustomFieldsForAccount(internalCallContextFactory.createInternalTenantContext(accountId, context)));
     }
 
+    @Override
+    public List<AuditLogWithHistory> getCustomFieldAuditLogsWithHistoryForId(final UUID customFieldId, final AuditLevel auditLevel, final TenantContext tenantContext) {
+        return customFieldDao.getCustomFieldAuditLogsWithHistoryForId(customFieldId, auditLevel, internalCallContextFactory.createInternalTenantContext(customFieldId, ObjectType.CUSTOM_FIELD, tenantContext));
+    }
+
     private List<CustomField> withCustomFieldsTransform(final Collection<CustomFieldModelDao> input) {
         return ImmutableList.<CustomField>copyOf(Collections2.transform(input, CUSTOM_FIELD_MODEL_DAO_CUSTOM_FIELD_FUNCTION));
     }
diff --git a/util/src/main/java/org/killbill/billing/util/customfield/dao/CustomFieldDao.java b/util/src/main/java/org/killbill/billing/util/customfield/dao/CustomFieldDao.java
index 6dea17a..313643e 100644
--- a/util/src/main/java/org/killbill/billing/util/customfield/dao/CustomFieldDao.java
+++ b/util/src/main/java/org/killbill/billing/util/customfield/dao/CustomFieldDao.java
@@ -22,7 +22,9 @@ import java.util.UUID;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.CustomFieldApiException;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.customfield.CustomField;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.EntityDao;
@@ -40,4 +42,6 @@ public interface CustomFieldDao extends EntityDao<CustomFieldModelDao, CustomFie
     void deleteCustomFields(Iterable<UUID> customFieldIds, InternalCallContext context) throws CustomFieldApiException;
 
     void updateCustomFields(Iterable<CustomFieldModelDao> customFieldIds, InternalCallContext context) throws CustomFieldApiException;
+
+    List<AuditLogWithHistory> getCustomFieldAuditLogsWithHistoryForId(UUID customFieldId, AuditLevel auditLevel, InternalTenantContext context);
 }
diff --git a/util/src/main/java/org/killbill/billing/util/customfield/dao/DefaultCustomFieldDao.java b/util/src/main/java/org/killbill/billing/util/customfield/dao/DefaultCustomFieldDao.java
index d541e9e..1677cc8 100644
--- a/util/src/main/java/org/killbill/billing/util/customfield/dao/DefaultCustomFieldDao.java
+++ b/util/src/main/java/org/killbill/billing/util/customfield/dao/DefaultCustomFieldDao.java
@@ -31,14 +31,18 @@ import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.events.BusInternalEvent;
+import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.CustomFieldApiException;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.audit.ChangeType;
+import org.killbill.billing.util.audit.dao.AuditDao;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.customfield.CustomField;
 import org.killbill.billing.util.customfield.api.DefaultCustomFieldCreationEvent;
 import org.killbill.billing.util.customfield.api.DefaultCustomFieldDeletionEvent;
 import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.DefaultPaginationSqlDaoHelper.Ordering;
 import org.killbill.billing.util.entity.dao.DefaultPaginationSqlDaoHelper.PaginationIteratorBuilder;
@@ -47,6 +51,7 @@ import org.killbill.billing.util.entity.dao.EntitySqlDao;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+import org.killbill.billing.util.tag.dao.TagSqlDao;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.clock.Clock;
 import org.skife.jdbi.v2.IDBI;
@@ -66,12 +71,14 @@ public class DefaultCustomFieldDao extends EntityDaoBase<CustomFieldModelDao, Cu
     private static final Logger log = LoggerFactory.getLogger(DefaultCustomFieldDao.class);
 
     private final PersistentBus bus;
+    private final AuditDao auditDao;
 
     @Inject
     public DefaultCustomFieldDao(final IDBI dbi, @Named(MAIN_RO_IDBI_NAMED) final IDBI roDbi, final Clock clock, final CacheControllerDispatcher controllerDispatcher,
-                                 final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory, final PersistentBus bus) {
+                                 final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory, final PersistentBus bus, final AuditDao auditDao) {
         super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, roDbi, clock, controllerDispatcher, nonEntityDao, internalCallContextFactory), CustomFieldSqlDao.class);
         this.bus = bus;
+        this.auditDao = auditDao;
     }
 
     @Override
@@ -166,6 +173,17 @@ public class DefaultCustomFieldDao extends EntityDaoBase<CustomFieldModelDao, Cu
     }
 
     @Override
+    public List<AuditLogWithHistory> getCustomFieldAuditLogsWithHistoryForId(final UUID customFieldId, final AuditLevel auditLevel, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(true, new EntitySqlDaoTransactionWrapper<List<AuditLogWithHistory>>() {
+            @Override
+            public List<AuditLogWithHistory> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) {
+                final CustomFieldSqlDao transactional = entitySqlDaoWrapperFactory.become(CustomFieldSqlDao.class);
+                return auditDao.getAuditLogsWithHistoryForId(transactional, TableName.CUSTOM_FIELD, customFieldId, auditLevel, context);
+            }
+        });
+    }
+
+    @Override
     protected CustomFieldApiException generateAlreadyExistsException(final CustomFieldModelDao entity, final InternalCallContext context) {
         return new CustomFieldApiException(ErrorCode.CUSTOM_FIELD_ALREADY_EXISTS, entity.getId());
     }
diff --git a/util/src/main/java/org/killbill/billing/util/dao/HistorySqlDao.java b/util/src/main/java/org/killbill/billing/util/dao/HistorySqlDao.java
index 23c763e..8bd523d 100644
--- a/util/src/main/java/org/killbill/billing/util/dao/HistorySqlDao.java
+++ b/util/src/main/java/org/killbill/billing/util/dao/HistorySqlDao.java
@@ -29,11 +29,15 @@ import org.killbill.commons.jdbi.binder.SmartBindBean;
 import org.skife.jdbi.v2.sqlobject.GetGeneratedKeys;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.customizers.Define;
 
 public interface HistorySqlDao<M extends EntityModelDao<E>, E extends Entity> {
 
+    //  bypassMappingRegistryCache is to be used in the mapping registry to bypass cache. This is useful for generic classes since it will prevent to return previously cached class, that could result in wrong maps.
+    //  https://github.com/killbill/killbill-commons/blob/work-for-release-0.19.x/jdbi/src/main/java/org/skife/jdbi/v2/MappingRegistry.java#L67
     @SqlQuery
-    public List<EntityHistoryModelDao<M, E>> getHistoryForTargetRecordId(@Bind("targetRecordId") final long targetRecordId,
+    public List<EntityHistoryModelDao<M, E>> getHistoryForTargetRecordId(@Define("bypassMappingRegistryCache") final boolean bypassMappingRegistryCache,
+                                                                         @Bind("targetRecordId") final long targetRecordId,
                                                                          @SmartBindBean InternalTenantContext context);
     @SqlUpdate
     @GetGeneratedKeys
diff --git a/util/src/main/java/org/killbill/billing/util/tag/api/DefaultTagUserApi.java b/util/src/main/java/org/killbill/billing/util/tag/api/DefaultTagUserApi.java
index eb81b50..920c958 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/api/DefaultTagUserApi.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/api/DefaultTagUserApi.java
@@ -24,9 +24,11 @@ import java.util.UUID;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.TagApiException;
 import org.killbill.billing.util.api.TagDefinitionApiException;
 import org.killbill.billing.util.api.TagUserApi;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
@@ -207,6 +209,16 @@ public class DefaultTagUserApi implements TagUserApi {
         return withModelTransform(tagDao.getTagsForAccount(includedDeleted, internalCallContextFactory.createInternalTenantContext(accountId, context)));
     }
 
+    @Override
+    public List<AuditLogWithHistory> getTagAuditLogsWithHistoryForId(final UUID tagId, final AuditLevel auditLevel, final TenantContext tenantContext) {
+        return tagDao.getTagAuditLogsWithHistoryForId(tagId, auditLevel, internalCallContextFactory.createInternalTenantContext(tagId, ObjectType.TAG, tenantContext));
+    }
+
+    @Override
+    public List<AuditLogWithHistory> getTagDefinitionAuditLogsWithHistoryForId(final UUID tagDefinitionId, final AuditLevel auditLevel, final TenantContext tenantContext) {
+        return tagDefinitionDao.getTagDefinitionAuditLogsWithHistoryForId(tagDefinitionId, auditLevel, internalCallContextFactory.createInternalTenantContext(tagDefinitionId, ObjectType.TAG_DEFINITION, tenantContext));
+    }
+
     private List<Tag> withModelTransform(final Collection<TagModelDao> input) {
         return ImmutableList.<Tag>copyOf(Collections2.transform(input, TAG_MODEL_DAO_TAG_FUNCTION));
     }
diff --git a/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDao.java b/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDao.java
index a2398a1..7c7298c 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDao.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDao.java
@@ -30,11 +30,15 @@ import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.events.TagInternalEvent;
+import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.TagApiException;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.audit.ChangeType;
+import org.killbill.billing.util.audit.dao.AuditDao;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.DefaultPaginationSqlDaoHelper.Ordering;
 import org.killbill.billing.util.entity.dao.DefaultPaginationSqlDaoHelper.PaginationIteratorBuilder;
@@ -66,13 +70,15 @@ public class DefaultTagDao extends EntityDaoBase<TagModelDao, Tag, TagApiExcepti
 
     private final TagEventBuilder tagEventBuilder;
     private final PersistentBus bus;
+    private final AuditDao auditDao;
 
     @Inject
     public DefaultTagDao(final IDBI dbi, @Named(MAIN_RO_IDBI_NAMED) final IDBI roDbi, final TagEventBuilder tagEventBuilder, final PersistentBus bus, final Clock clock,
-                         final CacheControllerDispatcher controllerDispatcher, final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
+                         final CacheControllerDispatcher controllerDispatcher, final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory, final AuditDao auditDao) {
         super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, roDbi, clock, controllerDispatcher, nonEntityDao, internalCallContextFactory), TagSqlDao.class);
         this.tagEventBuilder = tagEventBuilder;
         this.bus = bus;
+        this.auditDao = auditDao;
     }
 
     @Override
@@ -117,6 +123,17 @@ public class DefaultTagDao extends EntityDaoBase<TagModelDao, Tag, TagApiExcepti
     }
 
     @Override
+    public List<AuditLogWithHistory> getTagAuditLogsWithHistoryForId(final UUID tagId, final AuditLevel auditLevel, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(true, new EntitySqlDaoTransactionWrapper<List<AuditLogWithHistory>>() {
+            @Override
+            public List<AuditLogWithHistory> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) {
+                final TagSqlDao transactional = entitySqlDaoWrapperFactory.become(TagSqlDao.class);
+                return auditDao.getAuditLogsWithHistoryForId(transactional, TableName.TAG, tagId, auditLevel, context);
+            }
+        });
+    }
+
+    @Override
     protected void postBusEventFromTransaction(final TagModelDao tag, final TagModelDao savedTag, final ChangeType changeType,
                                                final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context)
             throws BillingExceptionBase {
diff --git a/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDefinitionDao.java b/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDefinitionDao.java
index 43d6ff5..abf19fa 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDefinitionDao.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDefinitionDao.java
@@ -31,11 +31,15 @@ import org.killbill.billing.ErrorCode;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.events.TagDefinitionInternalEvent;
+import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.TagDefinitionApiException;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.audit.ChangeType;
+import org.killbill.billing.util.audit.dao.AuditDao;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.dao.EntityDaoBase;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
@@ -62,13 +66,15 @@ public class DefaultTagDefinitionDao extends EntityDaoBase<TagDefinitionModelDao
 
     private final TagEventBuilder tagEventBuilder;
     private final PersistentBus bus;
+    private final AuditDao auditDao;
 
     @Inject
     public DefaultTagDefinitionDao(final IDBI dbi, @Named(MAIN_RO_IDBI_NAMED) final IDBI roDbi, final TagEventBuilder tagEventBuilder, final PersistentBus bus, final Clock clock,
-                                   final CacheControllerDispatcher controllerDispatcher, final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
+                                   final CacheControllerDispatcher controllerDispatcher, final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory, final AuditDao auditDao) {
         super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, roDbi, clock, controllerDispatcher, nonEntityDao, internalCallContextFactory), TagDefinitionSqlDao.class);
         this.tagEventBuilder = tagEventBuilder;
         this.bus = bus;
+        this.auditDao = auditDao;
     }
 
     @Override
@@ -232,6 +238,17 @@ public class DefaultTagDefinitionDao extends EntityDaoBase<TagDefinitionModelDao
         }
     }
 
+    @Override
+    public List<AuditLogWithHistory> getTagDefinitionAuditLogsWithHistoryForId(final UUID tagDefinitionId, final AuditLevel auditLevel, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(true, new EntitySqlDaoTransactionWrapper<List<AuditLogWithHistory>>() {
+            @Override
+            public List<AuditLogWithHistory> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) {
+                final TagDefinitionSqlDao transactional = entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class);
+                return auditDao.getAuditLogsWithHistoryForId(transactional, TableName.TAG_DEFINITIONS, tagDefinitionId, auditLevel, context);
+            }
+        });
+    }
+
     protected void postBusEventFromTransaction(final TagDefinitionModelDao tagDefinition, final TagDefinitionModelDao savedTagDefinition,
                                                final ChangeType changeType, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context)
             throws BillingExceptionBase {
diff --git a/util/src/main/java/org/killbill/billing/util/tag/dao/TagDao.java b/util/src/main/java/org/killbill/billing/util/tag/dao/TagDao.java
index 5dd9135..d09f069 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/dao/TagDao.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/dao/TagDao.java
@@ -22,7 +22,9 @@ import java.util.UUID;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.TagApiException;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.EntityDao;
 import org.killbill.billing.util.tag.Tag;
@@ -38,4 +40,7 @@ public interface TagDao extends EntityDao<TagModelDao, Tag, TagApiException> {
     List<TagModelDao> getTagsForAccountType(ObjectType objectType, boolean includedDeleted, InternalTenantContext internalTenantContext);
 
     List<TagModelDao> getTagsForAccount(boolean includedDeleted, InternalTenantContext internalTenantContext);
+
+    List<AuditLogWithHistory> getTagAuditLogsWithHistoryForId(UUID tagId, AuditLevel auditLevel, InternalTenantContext context);
+
 }
diff --git a/util/src/main/java/org/killbill/billing/util/tag/dao/TagDefinitionDao.java b/util/src/main/java/org/killbill/billing/util/tag/dao/TagDefinitionDao.java
index c056077..ba71d89 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/dao/TagDefinitionDao.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/dao/TagDefinitionDao.java
@@ -22,7 +22,9 @@ import java.util.UUID;
 
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.TagDefinitionApiException;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.entity.dao.EntityDao;
 import org.killbill.billing.util.tag.TagDefinition;
 
@@ -37,4 +39,6 @@ public interface TagDefinitionDao extends EntityDao<TagDefinitionModelDao, TagDe
     public TagDefinitionModelDao create(String definitionName, String description, String tagDefinitionObjectTypes, InternalCallContext context) throws TagDefinitionApiException;
 
     public void deleteById(UUID definitionId, InternalCallContext context) throws TagDefinitionApiException;
+
+    List<AuditLogWithHistory> getTagDefinitionAuditLogsWithHistoryForId(UUID tagDefinitionId, AuditLevel auditLevel, InternalTenantContext context);
 }
diff --git a/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java b/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java
index cbbf33a..3773ee1 100644
--- a/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java
+++ b/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java
@@ -191,7 +191,12 @@ public class MockAccountUserApi implements AccountUserApi {
     }
 
     @Override
-    public List<AuditLogWithHistory> getAuditLogsWithHistoryForId(final UUID uuid, final AuditLevel auditLevel, final TenantContext tenantContext) throws AccountApiException {
+    public List<AuditLogWithHistory> getAuditLogsWithHistoryForId(final UUID accountId, final AuditLevel auditLevel, final TenantContext tenantContext) throws AccountApiException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<AuditLogWithHistory> getEmailAuditLogsWithHistoryForId(final UUID accountEmailId, final AuditLevel auditLevel, final TenantContext tenantContext) throws AccountApiException {
         throw new UnsupportedOperationException();
     }
 }
diff --git a/util/src/test/java/org/killbill/billing/util/customfield/dao/MockCustomFieldDao.java b/util/src/test/java/org/killbill/billing/util/customfield/dao/MockCustomFieldDao.java
index ae80cd4..e3a782a 100644
--- a/util/src/test/java/org/killbill/billing/util/customfield/dao/MockCustomFieldDao.java
+++ b/util/src/test/java/org/killbill/billing/util/customfield/dao/MockCustomFieldDao.java
@@ -23,7 +23,9 @@ import java.util.UUID;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.CustomFieldApiException;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.customfield.CustomField;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.MockEntityDaoBase;
@@ -63,6 +65,11 @@ public class MockCustomFieldDao extends MockEntityDaoBase<CustomFieldModelDao, C
     }
 
     @Override
+    public List<AuditLogWithHistory> getCustomFieldAuditLogsWithHistoryForId(final UUID customFieldId, final AuditLevel auditLevel, final InternalTenantContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public Pagination<CustomFieldModelDao> searchCustomFields(final String searchKey, final Long offset, final Long limit, final InternalTenantContext context) {
         throw new UnsupportedOperationException();
     }
diff --git a/util/src/test/java/org/killbill/billing/util/tag/dao/MockTagDao.java b/util/src/test/java/org/killbill/billing/util/tag/dao/MockTagDao.java
index 636c76f..bfe0afe 100644
--- a/util/src/test/java/org/killbill/billing/util/tag/dao/MockTagDao.java
+++ b/util/src/test/java/org/killbill/billing/util/tag/dao/MockTagDao.java
@@ -26,7 +26,9 @@ import java.util.UUID;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.TagApiException;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.MockEntityDaoBase;
 import org.killbill.billing.util.tag.Tag;
@@ -106,6 +108,11 @@ public class MockTagDao extends MockEntityDaoBase<TagModelDao, Tag, TagApiExcept
         return tagStore.get(getAccountId(internalTenantContext.getAccountRecordId()));
     }
 
+    @Override
+    public List<AuditLogWithHistory> getTagAuditLogsWithHistoryForId(final UUID tagId, final AuditLevel auditLevel, final InternalTenantContext context) {
+        throw new UnsupportedOperationException();
+    }
+
     public void clear() {
         tagStore.clear();
     }
diff --git a/util/src/test/java/org/killbill/billing/util/tag/dao/MockTagDefinitionDao.java b/util/src/test/java/org/killbill/billing/util/tag/dao/MockTagDefinitionDao.java
index 8ae3ab6..16c91db 100644
--- a/util/src/test/java/org/killbill/billing/util/tag/dao/MockTagDefinitionDao.java
+++ b/util/src/test/java/org/killbill/billing/util/tag/dao/MockTagDefinitionDao.java
@@ -25,7 +25,9 @@ import java.util.concurrent.ConcurrentHashMap;
 
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.TagDefinitionApiException;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
 import org.killbill.billing.util.entity.dao.MockEntityDaoBase;
 import org.killbill.billing.util.tag.TagDefinition;
 
@@ -60,6 +62,11 @@ public class MockTagDefinitionDao extends MockEntityDaoBase<TagDefinitionModelDa
     }
 
     @Override
+    public List<AuditLogWithHistory> getTagDefinitionAuditLogsWithHistoryForId(final UUID tagDefinitionId, final AuditLevel auditLevel, final InternalTenantContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public List<TagDefinitionModelDao> getByIds(final Collection<UUID> definitionIds, final InternalTenantContext context) {
         return null;
     }