diff --git a/keyserver/src/keyserver.js b/keyserver/src/keyserver.js
--- a/keyserver/src/keyserver.js
+++ b/keyserver/src/keyserver.js
@@ -25,11 +25,13 @@
 import { jsonEndpoints } from './endpoints.js';
 import { logEndpointMetrics } from './middleware/endpoint-profiling.js';
 import { emailSubscriptionResponder } from './responders/comm-landing-responders.js';
+import { taggedCommFarcasterResponder } from './responders/farcaster-webhook-responders.js';
 import {
   jsonHandler,
   downloadHandler,
   htmlHandler,
   uploadHandler,
+  webhookPayloadHandler,
 } from './responders/handlers.js';
 import landingHandler from './responders/landing-handler.js';
 import { errorReportDownloadResponder } from './responders/report-responders.js';
@@ -297,6 +299,11 @@
         keyserverRouter.use(cors(keyserverCorsOptions));
       }
 
+      keyserverRouter.post(
+        '/fc_comm_tagged',
+        webhookPayloadHandler(taggedCommFarcasterResponder),
+      );
+
       for (const endpoint in jsonEndpoints) {
         // $FlowFixMe Flow thinks endpoint is string
         const responder = jsonEndpoints[endpoint];
diff --git a/keyserver/src/responders/farcaster-webhook-responders.js b/keyserver/src/responders/farcaster-webhook-responders.js
new file mode 100644
--- /dev/null
+++ b/keyserver/src/responders/farcaster-webhook-responders.js
@@ -0,0 +1,20 @@
+// @flow
+
+import type { $Request } from 'express';
+
+import { neynarWebhookCastCreatedEventValidator } from 'lib/types/validators/farcaster-webhook-validators.js';
+import { assertWithValidator } from 'lib/utils/validation-utils.js';
+
+const taggedCommFarcasterInputValidator =
+  neynarWebhookCastCreatedEventValidator;
+
+async function taggedCommFarcasterResponder(request: $Request): Promise<void> {
+  const event = assertWithValidator(
+    request.body,
+    taggedCommFarcasterInputValidator,
+  );
+
+  console.log(event);
+}
+
+export { taggedCommFarcasterResponder, taggedCommFarcasterInputValidator };
diff --git a/keyserver/src/responders/handlers.js b/keyserver/src/responders/handlers.js
--- a/keyserver/src/responders/handlers.js
+++ b/keyserver/src/responders/handlers.js
@@ -111,6 +111,28 @@
   };
 }
 
+type WebhookPayloadResponder = (request: $Request) => Promise<void>;
+function webhookPayloadHandler(
+  responder: WebhookPayloadResponder,
+): (req: $Request, res: $Response) => Promise<void> {
+  return async (req: $Request, res: $Response) => {
+    try {
+      if (!req.body || typeof req.body !== 'object') {
+        throw new ServerError('invalid_parameters');
+      }
+
+      const responderResult = await responder(req);
+
+      if (res.headersSent) {
+        return;
+      }
+      res.json({ success: true, ...responderResult });
+    } catch (e) {
+      await handleException(e, res);
+    }
+  };
+}
+
 function httpGetHandler(
   responder: HTTPGetResponder,
 ): (req: $Request, res: $Response) => Promise<void> {
@@ -245,4 +267,5 @@
   downloadHandler,
   htmlHandler,
   uploadHandler,
+  webhookPayloadHandler,
 };
diff --git a/lib/types/farcaster-types.js b/lib/types/farcaster-types.js
--- a/lib/types/farcaster-types.js
+++ b/lib/types/farcaster-types.js
@@ -41,3 +41,38 @@
   +description: string,
   ...
 };
+
+export type NeynarWebhookCastAuthor = {
+  +object: 'user',
+  +fid: number,
+  +custody_address: string,
+  +username: string,
+  +display_name: string,
+  +pfp_url: string,
+  ...
+};
+
+export type NeynarWebhookCastCreatedData = {
+  +object: 'cast',
+  +hash: string,
+  +thread_hash: string,
+  +text: string,
+  +channel?: ?NeynarWebhookChannel,
+  +parent_hash?: ?string,
+  +author: NeynarWebhookCastAuthor,
+  ...
+};
+
+export type NeynarWebhookChannel = {
+  +id: string,
+  +name: string,
+  +image_url: string,
+  ...
+};
+
+export type NeynarWebhookCastCreatedEvent = {
+  +created_at: number,
+  +type: 'cast.created',
+  +data: NeynarWebhookCastCreatedData,
+  ...
+};
diff --git a/lib/types/validators/farcaster-webhook-validators.js b/lib/types/validators/farcaster-webhook-validators.js
new file mode 100644
--- /dev/null
+++ b/lib/types/validators/farcaster-webhook-validators.js
@@ -0,0 +1,45 @@
+// @flow
+
+import t, { type TInterface } from 'tcomb';
+
+import type {
+  NeynarWebhookCastCreatedData,
+  NeynarWebhookChannel,
+  NeynarWebhookCastAuthor,
+  NeynarWebhookCastCreatedEvent,
+} from '../farcaster-types.js';
+
+export const neynarWebhookCastAuthorValidator: TInterface<NeynarWebhookCastAuthor> =
+  t.interface<NeynarWebhookCastAuthor>({
+    object: t.enums.of(['user']),
+    fid: t.Number,
+    custody_address: t.String,
+    username: t.String,
+    display_name: t.String,
+    pfp_url: t.String,
+  });
+
+export const neynarWebhookChannelValidator: TInterface<NeynarWebhookChannel> =
+  t.interface<NeynarWebhookChannel>({
+    id: t.String,
+    name: t.String,
+    image_url: t.String,
+  });
+
+export const neynarWebhookCastCreatedDataValidator: TInterface<NeynarWebhookCastCreatedData> =
+  t.interface<NeynarWebhookCastCreatedData>({
+    object: t.enums.of(['cast']),
+    hash: t.String,
+    thread_hash: t.String,
+    text: t.String,
+    channel: t.maybe(neynarWebhookChannelValidator),
+    parent_hash: t.maybe(t.String),
+    author: neynarWebhookCastAuthorValidator,
+  });
+
+export const neynarWebhookCastCreatedEventValidator: TInterface<NeynarWebhookCastCreatedEvent> =
+  t.interface<NeynarWebhookCastCreatedEvent>({
+    created_at: t.Number,
+    type: t.enums.of(['cast.created']),
+    data: neynarWebhookCastCreatedDataValidator,
+  });