diff --git a/src/Module/BaseAdmin.php b/src/Module/BaseAdmin.php
index a7b38a503..01215dc8e 100644
--- a/src/Module/BaseAdmin.php
+++ b/src/Module/BaseAdmin.php
@@ -121,6 +121,7 @@ abstract class BaseAdmin extends BaseModule
 				'webfinger'    => ['webfinger'         , DI::l10n()->t('check webfinger')         , 'webfinger'],
 				'itemsource'   => ['admin/item/source' , DI::l10n()->t('Item Source')             , 'itemsource'],
 				'babel'        => ['babel'             , DI::l10n()->t('Babel')                   , 'babel'],
+				'debug/ap'     => ['debug/ap'          , DI::l10n()->t('ActivityPub Conversion')  , 'debug/ap'],
 			]],
 		];
 
diff --git a/src/Module/Debug/ActivityPubConversion.php b/src/Module/Debug/ActivityPubConversion.php
new file mode 100644
index 000000000..87a531d5b
--- /dev/null
+++ b/src/Module/Debug/ActivityPubConversion.php
@@ -0,0 +1,144 @@
+<?php
+/**
+ * @copyright Copyright (C) 2020, Friendica
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module\Debug;
+
+use Friendica\BaseModule;
+use Friendica\Content\Text;
+use Friendica\Core\Logger;
+use Friendica\Core\Renderer;
+use Friendica\DI;
+use Friendica\Model\Item;
+use Friendica\Model\Tag;
+use Friendica\Protocol\ActivityPub;
+use Friendica\Util\JsonLD;
+use Friendica\Util\XML;
+
+class ActivityPubConversion extends BaseModule
+{
+	public static function content(array $parameters = [])
+	{
+		function visible_whitespace($s)
+		{
+			return '<pre>' . htmlspecialchars($s) . '</pre>';
+		}
+
+		$results = [];
+		if (!empty($_REQUEST['source'])) {
+			try {
+				$source = json_decode($_REQUEST['source'], true);
+				$trust_source = true;
+				$uid = local_user();
+				$push = false;
+
+				if (!$source) {
+					throw new \Exception('Failed to decode source JSON');
+				}
+
+				$formatted = json_encode($source, JSON_PRETTY_PRINT);
+				$results[] = [
+					'title'   => DI::l10n()->t('Formatted'),
+					'content' => visible_whitespace(trim(var_export($formatted, true), "'")),
+				];
+				$results[] = [
+					'title'   => DI::l10n()->t('Source'),
+					'content' => visible_whitespace(var_export($source, true))
+				];
+				$activity = JsonLD::compact($source);
+				if (!$activity) {
+					throw new \Exception('Failed to compact JSON');
+				}
+				$results[] = [
+					'title'   => DI::l10n()->t('Activity'),
+					'content' => visible_whitespace(var_export($activity, true))
+				];
+
+				$type = JsonLD::fetchElement($activity, '@type');
+
+				if (!$type) {
+					throw new \Exception('Empty type');
+				}
+
+				if (!JsonLD::fetchElement($activity, 'as:object', '@id')) {
+					throw new \Exception('Empty object');
+				}
+
+				if (!JsonLD::fetchElement($activity, 'as:actor', '@id')) {
+					throw new \Exception('Empty actor');
+				}
+
+				// Don't trust the source if "actor" differs from "attributedTo". The content could be forged.
+				if ($trust_source && ($type == 'as:Create') && is_array($activity['as:object'])) {
+					$actor = JsonLD::fetchElement($activity, 'as:actor', '@id');
+					$attributed_to = JsonLD::fetchElement($activity['as:object'], 'as:attributedTo', '@id');
+					$trust_source = ($actor == $attributed_to);
+					if (!$trust_source) {
+						throw new \Exception('Not trusting actor: ' . $actor . '. It differs from attributedTo: ' . $attributed_to);
+					}
+				}
+
+				// $trust_source is called by reference and is set to true if the content was retrieved successfully
+				$object_data = ActivityPub\Receiver::prepareObjectData($activity, $uid, $push, $trust_source);
+				if (empty($object_data)) {
+					throw new \Exception('No object data found');
+				}
+
+				if (!$trust_source) {
+					throw new \Exception('No trust for activity type "' . $type . '", so we quit now.');
+				}
+
+				if (!empty($body) && empty($object_data['raw'])) {
+					$object_data['raw'] = $body;
+				}
+
+				// Internal flag for thread completion. See Processor.php
+				if (!empty($activity['thread-completion'])) {
+					$object_data['thread-completion'] = $activity['thread-completion'];
+				}
+
+				$results[] = [
+					'title'   => DI::l10n()->t('Object data'),
+					'content' => visible_whitespace(var_export($object_data, true))
+				];
+
+				$item = ActivityPub\Processor::createItem($object_data);
+
+				$results[] = [
+					'title'   => DI::l10n()->t('Result Item'),
+					'content' => visible_whitespace(var_export($item, true))
+				];
+			} catch (\Throwable $e) {
+				$results[] = [
+					'title'   => DI::l10n()->t('Error'),
+					'content' => $e->getMessage(),
+				];
+			}
+		}
+
+		$tpl = Renderer::getMarkupTemplate('debug/activitypubconversion.tpl');
+		$o = Renderer::replaceMacros($tpl, [
+			'$source'          => ['source', DI::l10n()->t('Source activity'), $_REQUEST['source'] ?? '', ''],
+			'$results'       => $results
+		]);
+
+		return $o;
+	}
+}
diff --git a/static/routes.config.php b/static/routes.config.php
index ac7882693..8c3fba99b 100644
--- a/static/routes.config.php
+++ b/static/routes.config.php
@@ -107,6 +107,7 @@ return [
 	'/apps'                => [Module\Apps::class,         [R::GET]],
 	'/attach/{item:\d+}'   => [Module\Attach::class,       [R::GET]],
 	'/babel'               => [Module\Debug\Babel::class,  [R::GET, R::POST]],
+	'/debug/ap'            => [Module\Debug\ActivityPubConversion::class,  [R::GET, R::POST]],
 	'/bookmarklet'         => [Module\Bookmarklet::class,  [R::GET]],
 
 	'/community[/{content}[/{accounttype}]]' => [Module\Conversation\Community::class, [R::GET]],
diff --git a/view/templates/debug/activitypubconversion.tpl b/view/templates/debug/activitypubconversion.tpl
new file mode 100644
index 000000000..dfc6d7367
--- /dev/null
+++ b/view/templates/debug/activitypubconversion.tpl
@@ -0,0 +1,24 @@
+<h2>ActivityPub Conversion</h2>
+<form action="debug/ap" method="post" class="panel panel-default">
+	<div class="panel-body">
+		<div class="form-group">
+			{{include file="field_textarea.tpl" field=$source}}
+		</div>
+		<p><button type="submit" class="btn btn-primary">Submit</button></p>
+	</div>
+</form>
+
+{{if $results}}
+<div class="babel-results">
+	{{foreach $results as $result}}
+	<div class="panel panel-default">
+		<div class="panel-heading">
+			<h3 class="panel-title">{{$result.title}}</h3>
+		</div>
+		<div class="panel-body">
+			{{$result.content nofilter}}
+		</div>
+	</div>
+	{{/foreach}}
+</div>
+{{/if}}
\ No newline at end of file