diff --git a/core/modules/jsonapi/jsonapi.api.php b/core/modules/jsonapi/jsonapi.api.php
index c011e77f79..fa9be05fa3 100644
--- a/core/modules/jsonapi/jsonapi.api.php
+++ b/core/modules/jsonapi/jsonapi.api.php
@@ -349,6 +349,59 @@ function hook_jsonapi_entity_field_filter_access(\Drupal\Core\Field\FieldDefinit
   return AccessResult::neutral();
 }
 
+/**
+ * Allow access to revisions of content.
+ *
+ * Normally JSON API forbids access to all revisions of entities apart from core
+ * entity types Node and Media since these have interfaces for properly
+ * determining access.
+ *
+ * If you know what you are doing and understand the risks you can implement
+ * this hook to provide custom logic for other Entity types to allow revision
+ * access.
+ *
+ * The example below allows access to revisions of paragraph entities by
+ * checking if the user has access to the parent node revision the paragraph
+ * revision is on.
+ *
+ * @return \Drupal\Core\Access\AccessResultInterface
+ */
+function hook_jsonapi_revision_view_access(\Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Session\AccountInterface $account) {
+  /** @var \Drupal\paragraphs\Entity\Paragraph $entity */
+  if (!$entity instanceof \Drupal\paragraphs\ParagraphInterface) {
+    return AccessResult::neutral();
+  }
+
+  $parent = $entity->getParentEntity();
+
+  if ($parent instanceof \Drupal\node\NodeInterface) {
+    $field_name = $entity->get('parent_field_name')[0]->getValue();
+
+    $database = \Drupal::database();
+    $node_revision = $database
+      ->select('node_revision__' . $field_name['value'], 'r')
+      ->fields('r', ['revision_id'])
+      ->condition('r.' . $field_name['value'] . '_target_id', $entity->id())
+      ->condition('r.' . $field_name['value'] . '_target_revision_id', $entity->getRevisionId())
+      ->execute()
+      ->fetch();
+
+    if ($parent->getRevisionId() !== $node_revision->revision_id) {
+      $parent = node_revision_load($node_revision->revision_id);
+    }
+
+    /** @var \Drupal\node\Access\NodeRevisionAccessCheck $node_revision_access_service */
+    $node_revision_access_service = \Drupal::service('access_check.node.revision');
+
+    return AccessResult::allowedIf($node_revision_access_service
+      ->checkAccess($parent, $account, 'view'))
+      ->cachePerPermissions()
+      ->addCacheableDependency($entity);
+  }
+
+  return AccessResult::neutral('Paragraphs revisions are only supported on nodes with JSON:API');
+}
+
 /**
  * @} End of "addtogroup hooks".
  */
diff --git a/core/modules/jsonapi/src/Access/EntityAccessChecker.php b/core/modules/jsonapi/src/Access/EntityAccessChecker.php
index d3d4fa06e0..455306c06a 100644
--- a/core/modules/jsonapi/src/Access/EntityAccessChecker.php
+++ b/core/modules/jsonapi/src/Access/EntityAccessChecker.php
@@ -256,10 +256,22 @@ protected function checkRevisionViewAccess(EntityInterface $entity, AccountInter
         break;
 
       default:
-        $reason = 'Only node and media revisions are supported by JSON:API.';
-        $reason .= ' For context, see https://www.drupal.org/project/jsonapi/issues/2992833#comment-12818258.';
-        $reason .= ' To contribute, see https://www.drupal.org/project/drupal/issues/2350939 and https://www.drupal.org/project/drupal/issues/2809177.';
-        $access = AccessResult::neutral($reason);
+        // Allow modules that know what they are doing a way to provide custom revision access.
+        $custom_access_list = \Drupal::moduleHandler()->invokeAll('jsonapi_revision_view_access', [$entity, $account]);
+
+        if (empty($custom_access_list)) {
+          $reason = 'Only node and media revisions are supported by JSON:API.';
+          $reason .= ' For context, see https://www.drupal.org/project/jsonapi/issues/2992833#comment-12818258.';
+          $reason .= ' To contribute, see https://www.drupal.org/project/drupal/issues/2350939 and https://www.drupal.org/project/drupal/issues/2809177.';
+          $access = AccessResult::neutral($reason);
+        }
+        else {
+          /** @var \Drupal\Core\Access\AccessResultInterface $access */
+          $access = array_shift($custom_access_list);
+          foreach ($custom_access_list as $other) {
+            $access = $access->orIf($other);
+          }
+        }
     }
     // Apply content_moderation's additional access logic.
     // @see \Drupal\content_moderation\Access\LatestRevisionCheck::access()
diff --git a/core/modules/jsonapi/src/IncludeResolver.php b/core/modules/jsonapi/src/IncludeResolver.php
index d8a283df0d..4469104d4a 100644
--- a/core/modules/jsonapi/src/IncludeResolver.php
+++ b/core/modules/jsonapi/src/IncludeResolver.php
@@ -139,12 +139,24 @@ protected function resolveIncludeTree(array $include_tree, Data $data, Data $inc
         assert(!empty($target_type));
         foreach ($field_list as $field_item) {
           assert($field_item instanceof EntityReferenceItem);
-          $references[$target_type][] = $field_item->get($field_item::mainPropertyName())->getValue();
+          if ($resource_object->getResourceType()->isVersionable()) {
+            // Load the revision id.
+            $references[$target_type . ':revision_ids'][] = $field_item->get('target_revision_id')->getValue();
+          }
+          else {
+            $references[$target_type . ':ids'][] = $field_item->get($field_item::mainPropertyName())->getValue();
+          }
         }
       }
-      foreach ($references as $target_type => $ids) {
+      foreach ($references as $target_type_and_rev => $ids) {
+        list($target_type, $revision_type) = explode(':', $target_type_and_rev);
+
         $entity_storage = $this->entityTypeManager->getStorage($target_type);
-        $targeted_entities = $entity_storage->loadMultiple(array_unique($ids));
+
+        $targeted_entities = ($revision_type === 'revision_ids')
+          ? $entity_storage->loadMultipleRevisions($ids)
+          : $entity_storage->loadMultiple(array_unique($ids));
+
         $access_checked_entities = array_map(function (EntityInterface $entity) {
           return $this->entityAccessChecker->getAccessCheckedResourceObject($entity);
         }, $targeted_entities);
