diff --git a/packages/parser/src/envelopes/apigw.ts b/packages/parser/src/envelopes/apigw.ts
index 49d094b405..ee4d71e9c0 100644
--- a/packages/parser/src/envelopes/apigw.ts
+++ b/packages/parser/src/envelopes/apigw.ts
@@ -1,18 +1,40 @@
-import { parse } from './envelope.js';
-import { z, ZodSchema } from 'zod';
+import { Envelope } from './envelope.js';
+import { z, type ZodSchema } from 'zod';
 import { APIGatewayProxyEventSchema } from '../schemas/apigw.js';
+import type { ParsedResult } from '../types/parser.js';
 
 /**
  * API Gateway envelope to extract data within body key
  */
-export const apiGatewayEnvelope = <T extends ZodSchema>(
-  data: unknown,
-  schema: T
-): z.infer<T> => {
-  const parsedEnvelope = APIGatewayProxyEventSchema.parse(data);
-  if (!parsedEnvelope.body) {
-    throw new Error('Body field of API Gateway event is undefined');
+export class ApiGatewayEnvelope extends Envelope {
+  public static parse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): z.infer<T> {
+    return super.parse(APIGatewayProxyEventSchema.parse(data).body, schema);
   }
 
-  return parse(parsedEnvelope.body, schema);
-};
+  public static safeParse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): ParsedResult<unknown, z.infer<T>> {
+    const parsedEnvelope = APIGatewayProxyEventSchema.safeParse(data);
+    if (!parsedEnvelope.success) {
+      return {
+        ...parsedEnvelope,
+        originalEvent: data,
+      };
+    }
+
+    const parsedBody = super.safeParse(parsedEnvelope.data.body, schema);
+
+    if (!parsedBody.success) {
+      return {
+        ...parsedBody,
+        originalEvent: data,
+      };
+    }
+
+    return parsedBody;
+  }
+}
diff --git a/packages/parser/src/envelopes/apigwv2.ts b/packages/parser/src/envelopes/apigwv2.ts
index decadfcc57..3da86e26e4 100644
--- a/packages/parser/src/envelopes/apigwv2.ts
+++ b/packages/parser/src/envelopes/apigwv2.ts
@@ -1,18 +1,40 @@
-import { parse } from './envelope.js';
-import { z, ZodSchema } from 'zod';
+import { z, type ZodSchema } from 'zod';
 import { APIGatewayProxyEventV2Schema } from '../schemas/apigwv2.js';
+import { Envelope } from './envelope.js';
+import type { ParsedResult } from '../types/index.js';
 
 /**
  * API Gateway V2 envelope to extract data within body key
  */
-export const apiGatewayV2Envelope = <T extends ZodSchema>(
-  data: unknown,
-  schema: T
-): z.infer<T> => {
-  const parsedEnvelope = APIGatewayProxyEventV2Schema.parse(data);
-  if (!parsedEnvelope.body) {
-    throw new Error('Body field of API Gateway event is undefined');
+export class ApiGatewayV2Envelope extends Envelope {
+  public static parse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): z.infer<T> {
+    return super.parse(APIGatewayProxyEventV2Schema.parse(data).body, schema);
   }
 
-  return parse(parsedEnvelope.body, schema);
-};
+  public static safeParse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): ParsedResult {
+    const parsedEnvelope = APIGatewayProxyEventV2Schema.safeParse(data);
+    if (!parsedEnvelope.success) {
+      return {
+        ...parsedEnvelope,
+        originalEvent: data,
+      };
+    }
+
+    const parsedBody = super.safeParse(parsedEnvelope.data.body, schema);
+
+    if (!parsedBody.success) {
+      return {
+        ...parsedBody,
+        originalEvent: data,
+      };
+    }
+
+    return parsedBody;
+  }
+}
diff --git a/packages/parser/src/envelopes/cloudwatch.ts b/packages/parser/src/envelopes/cloudwatch.ts
index 848e7ab070..f90e6455f9 100644
--- a/packages/parser/src/envelopes/cloudwatch.ts
+++ b/packages/parser/src/envelopes/cloudwatch.ts
@@ -1,6 +1,7 @@
-import { parse } from './envelope.js';
-import { z, ZodSchema } from 'zod';
-import { CloudWatchLogsSchema } from '../schemas/cloudwatch.js';
+import { z, type ZodSchema } from 'zod';
+import { Envelope } from './envelope.js';
+import { CloudWatchLogsSchema } from '../schemas/index.js';
+import type { ParsedResult } from '../types/index.js';
 
 /**
  * CloudWatch Envelope to extract a List of log records.
@@ -11,13 +12,49 @@ import { CloudWatchLogsSchema } from '../schemas/cloudwatch.js';
  *
  *  Note: The record will be parsed the same way so if model is str
  */
-export const cloudWatchEnvelope = <T extends ZodSchema>(
-  data: unknown,
-  schema: T
-): z.infer<T> => {
-  const parsedEnvelope = CloudWatchLogsSchema.parse(data);
+export class CloudWatchEnvelope extends Envelope {
+  public static parse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): z.infer<T> {
+    const parsedEnvelope = CloudWatchLogsSchema.parse(data);
 
-  return parsedEnvelope.awslogs.data.logEvents.map((record) => {
-    return parse(record.message, schema);
-  });
-};
+    return parsedEnvelope.awslogs.data.logEvents.map((record) => {
+      return super.parse(record.message, schema);
+    });
+  }
+
+  public static safeParse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): ParsedResult {
+    const parsedEnvelope = CloudWatchLogsSchema.safeParse(data);
+
+    if (!parsedEnvelope.success) {
+      return {
+        success: false,
+        error: parsedEnvelope.error,
+        originalEvent: data,
+      };
+    }
+    const parsedLogEvents: z.infer<T>[] = [];
+
+    for (const record of parsedEnvelope.data.awslogs.data.logEvents) {
+      const parsedMessage = super.safeParse(record.message, schema);
+      if (!parsedMessage.success) {
+        return {
+          success: false,
+          error: parsedMessage.error,
+          originalEvent: data,
+        };
+      } else {
+        parsedLogEvents.push(parsedMessage.data);
+      }
+    }
+
+    return {
+      success: true,
+      data: parsedLogEvents,
+    };
+  }
+}
diff --git a/packages/parser/src/envelopes/dynamodb.ts b/packages/parser/src/envelopes/dynamodb.ts
index bb378b2d0b..4898369422 100644
--- a/packages/parser/src/envelopes/dynamodb.ts
+++ b/packages/parser/src/envelopes/dynamodb.ts
@@ -1,6 +1,7 @@
-import { parse } from './envelope.js';
-import { z, ZodSchema } from 'zod';
-import { DynamoDBStreamSchema } from '../schemas/dynamodb.js';
+import { z, type ZodSchema } from 'zod';
+import { DynamoDBStreamSchema } from '../schemas/index.js';
+import type { ParsedResult, ParsedResultError } from '../types/index.js';
+import { Envelope } from './envelope.js';
 
 type DynamoDBStreamEnvelopeResponse<T extends ZodSchema> = {
   NewImage: z.infer<T>;
@@ -13,16 +14,58 @@ type DynamoDBStreamEnvelopeResponse<T extends ZodSchema> = {
  * Note: Values are the parsed models. Images' values can also be None, and
  * length of the list is the record's amount in the original event.
  */
-export const dynamoDDStreamEnvelope = <T extends ZodSchema>(
-  data: unknown,
-  schema: T
-): DynamoDBStreamEnvelopeResponse<T>[] => {
-  const parsedEnvelope = DynamoDBStreamSchema.parse(data);
+export class DynamoDBStreamEnvelope extends Envelope {
+  public static parse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): DynamoDBStreamEnvelopeResponse<z.infer<T>>[] {
+    const parsedEnvelope = DynamoDBStreamSchema.parse(data);
+
+    return parsedEnvelope.Records.map((record) => {
+      return {
+        NewImage: super.parse(record.dynamodb.NewImage, schema),
+        OldImage: super.parse(record.dynamodb.OldImage, schema),
+      };
+    });
+  }
+
+  public static safeParse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): ParsedResult {
+    const parsedEnvelope = DynamoDBStreamSchema.safeParse(data);
+
+    if (!parsedEnvelope.success) {
+      return {
+        success: false,
+        error: parsedEnvelope.error,
+        originalEvent: data,
+      };
+    }
+    const parsedLogEvents: DynamoDBStreamEnvelopeResponse<z.infer<T>>[] = [];
+
+    for (const record of parsedEnvelope.data.Records) {
+      const parsedNewImage = super.safeParse(record.dynamodb.NewImage, schema);
+      const parsedOldImage = super.safeParse(record.dynamodb.OldImage, schema);
+      if (!parsedNewImage.success || !parsedOldImage.success) {
+        return {
+          success: false,
+          error: !parsedNewImage.success
+            ? parsedNewImage.error
+            : (parsedOldImage as ParsedResultError<unknown>).error,
+          originalEvent: data,
+        };
+      } else {
+        parsedLogEvents.push({
+          NewImage: parsedNewImage.data,
+          OldImage: parsedOldImage.data,
+        });
+      }
+    }
 
-  return parsedEnvelope.Records.map((record) => {
     return {
-      NewImage: parse(record.dynamodb.NewImage, schema),
-      OldImage: parse(record.dynamodb.OldImage, schema),
+      success: true,
+      data: parsedLogEvents,
     };
-  });
-};
+  }
+}
diff --git a/packages/parser/src/envelopes/envelope.ts b/packages/parser/src/envelopes/envelope.ts
index 4c2dd9570d..ed55758153 100644
--- a/packages/parser/src/envelopes/envelope.ts
+++ b/packages/parser/src/envelopes/envelope.ts
@@ -1,23 +1,70 @@
-import { z, ZodSchema } from 'zod';
+import { z, type ZodSchema } from 'zod';
+import type { ParsedResult } from '../types/parser.js';
 
-/**
- * Abstract function to parse the content of the envelope using provided schema.
- * Both inputs are provided as unknown by the user.
- * We expect the data to be either string that can be parsed to json or object.
- * @internal
- * @param data data to parse
- * @param schema schema
- */
-export const parse = <T extends ZodSchema>(
-  data: unknown,
-  schema: T
-): z.infer<T>[] => {
-  if (typeof data === 'string') {
-    return schema.parse(JSON.parse(data));
-  } else if (typeof data === 'object') {
-    return schema.parse(data);
-  } else
-    throw new Error(
-      `Invalid data type for envelope. Expected string or object, got ${typeof data}`
-    );
-};
+export class Envelope {
+  /**
+   * Abstract function to parse the content of the envelope using provided schema.
+   * Both inputs are provided as unknown by the user.
+   * We expect the data to be either string that can be parsed to json or object.
+   * @internal
+   * @param data data to parse
+   * @param schema schema
+   */
+  public static readonly parse = <T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): z.infer<T> => {
+    if (typeof data === 'string') {
+      return schema.parse(JSON.parse(data));
+    } else if (typeof data === 'object') {
+      return schema.parse(data);
+    } else
+      throw new Error(
+        `Invalid data type for envelope. Expected string or object, got ${typeof data}`
+      );
+  };
+
+  /**
+   * Abstract function to safely parse the content of the envelope using provided schema.
+   * safeParse is used to avoid throwing errors, thus we catuch all errors and wrap them in the result.
+   * @param input
+   * @param schema
+   */
+  public static readonly safeParse = <T extends ZodSchema>(
+    input: unknown,
+    schema: T
+  ): ParsedResult<unknown, z.infer<T>> => {
+    try {
+      if (typeof input !== 'object' && typeof input !== 'string') {
+        return {
+          success: false,
+          error: new Error(
+            `Invalid data type for envelope. Expected string or object, got ${typeof input}`
+          ),
+          originalEvent: input,
+        };
+      }
+
+      const parsed = schema.safeParse(
+        typeof input === 'string' ? JSON.parse(input) : input
+      );
+
+      return parsed.success
+        ? {
+            success: true,
+            data: parsed.data,
+          }
+        : {
+            success: false,
+            error: parsed.error,
+            originalEvent: input,
+          };
+    } catch (e) {
+      return {
+        success: false,
+        error: e as Error,
+        originalEvent: input,
+      };
+    }
+  };
+}
diff --git a/packages/parser/src/envelopes/event-bridge.ts b/packages/parser/src/envelopes/event-bridge.ts
index 4484635348..0d166d673b 100644
--- a/packages/parser/src/envelopes/event-bridge.ts
+++ b/packages/parser/src/envelopes/event-bridge.ts
@@ -1,13 +1,41 @@
-import { parse } from './envelope.js';
-import { z, ZodSchema } from 'zod';
-import { EventBridgeSchema } from '../schemas/eventbridge.js';
+import { Envelope } from './envelope.js';
+import { z, type ZodSchema } from 'zod';
+import { EventBridgeSchema } from '../schemas/index.js';
+import type { ParsedResult } from '../types/index.js';
 
 /**
  * Envelope for EventBridge schema that extracts and parses data from the `detail` key.
  */
-export const eventBridgeEnvelope = <T extends ZodSchema>(
-  data: unknown,
-  schema: T
-): z.infer<T> => {
-  return parse(EventBridgeSchema.parse(data).detail, schema);
-};
+export class EventBridgeEnvelope extends Envelope {
+  public static parse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): z.infer<T> {
+    return super.parse(EventBridgeSchema.parse(data).detail, schema);
+  }
+
+  public static safeParse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): ParsedResult {
+    const parsedEnvelope = EventBridgeSchema.safeParse(data);
+
+    if (!parsedEnvelope.success) {
+      return {
+        ...parsedEnvelope,
+        originalEvent: data,
+      };
+    }
+
+    const parsedDetail = super.safeParse(parsedEnvelope.data.detail, schema);
+
+    if (!parsedDetail.success) {
+      return {
+        ...parsedDetail,
+        originalEvent: data,
+      };
+    }
+
+    return parsedDetail;
+  }
+}
diff --git a/packages/parser/src/envelopes/index.ts b/packages/parser/src/envelopes/index.ts
index dd28c4aeea..3d1487a7b0 100644
--- a/packages/parser/src/envelopes/index.ts
+++ b/packages/parser/src/envelopes/index.ts
@@ -1,13 +1,13 @@
-export { apiGatewayEnvelope } from './apigw.js';
-export { apiGatewayV2Envelope } from './apigwv2.js';
-export { cloudWatchEnvelope } from './cloudwatch.js';
-export { dynamoDDStreamEnvelope } from './dynamodb.js';
-export { eventBridgeEnvelope } from './event-bridge.js';
-export { kafkaEnvelope } from './kafka.js';
-export { kinesisEnvelope } from './kinesis.js';
-export { kinesisFirehoseEnvelope } from './kinesis-firehose.js';
-export { lambdaFunctionUrlEnvelope } from './lambda.js';
-export { snsEnvelope, snsSqsEnvelope } from './sns.js';
-export { sqsEnvelope } from './sqs.js';
-export { vpcLatticeEnvelope } from './vpc-lattice.js';
-export { vpcLatticeV2Envelope } from './vpc-latticev2.js';
+export { ApiGatewayEnvelope } from './apigw.js';
+export { ApiGatewayV2Envelope } from './apigwv2.js';
+export { CloudWatchEnvelope } from './cloudwatch.js';
+export { DynamoDBStreamEnvelope } from './dynamodb.js';
+export { EventBridgeEnvelope } from './event-bridge.js';
+export { KafkaEnvelope } from './kafka.js';
+export { KinesisEnvelope } from './kinesis.js';
+export { KinesisFirehoseEnvelope } from './kinesis-firehose.js';
+export { LambdaFunctionUrlEnvelope } from './lambda.js';
+export { SnsEnvelope, SnsSqsEnvelope } from './sns.js';
+export { SqsEnvelope } from './sqs.js';
+export { VpcLatticeEnvelope } from './vpc-lattice.js';
+export { VpcLatticeV2Envelope } from './vpc-latticev2.js';
diff --git a/packages/parser/src/envelopes/kafka.ts b/packages/parser/src/envelopes/kafka.ts
index 86eb44063f..1bbd442f3f 100644
--- a/packages/parser/src/envelopes/kafka.ts
+++ b/packages/parser/src/envelopes/kafka.ts
@@ -1,9 +1,10 @@
-import { z, ZodSchema } from 'zod';
-import { parse } from './envelope.js';
+import { z, type ZodSchema } from 'zod';
+import { Envelope } from './envelope.js';
 import {
   KafkaMskEventSchema,
   KafkaSelfManagedEventSchema,
 } from '../schemas/kafka.js';
+import { ParsedResult, KafkaMskEvent } from '../types/index.js';
 
 /**
  * Kafka event envelope to extract data within body key
@@ -13,26 +14,66 @@ import {
  * Note: Records will be parsed the same way so if model is str,
  * all items in the list will be parsed as str and not as JSON (and vice versa)
  */
-export const kafkaEnvelope = <T extends ZodSchema>(
-  data: unknown,
-  schema: T
-): z.infer<T> => {
-  // manually fetch event source to deside between Msk or SelfManaged
-
-  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
-  // @ts-ignore
-  const eventSource = data['eventSource'];
-
-  const parsedEnvelope:
-    | z.infer<typeof KafkaMskEventSchema>
-    | z.infer<typeof KafkaSelfManagedEventSchema> =
-    eventSource === 'aws:kafka'
-      ? KafkaMskEventSchema.parse(data)
-      : KafkaSelfManagedEventSchema.parse(data);
-
-  return Object.values(parsedEnvelope.records).map((topicRecord) => {
-    return topicRecord.map((record) => {
-      return parse(record.value, schema);
+
+export class KafkaEnvelope extends Envelope {
+  public static parse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): z.infer<T> {
+    // manually fetch event source to deside between Msk or SelfManaged
+    const eventSource = (data as KafkaMskEvent)['eventSource'];
+
+    const parsedEnvelope:
+      | z.infer<typeof KafkaMskEventSchema>
+      | z.infer<typeof KafkaSelfManagedEventSchema> =
+      eventSource === 'aws:kafka'
+        ? KafkaMskEventSchema.parse(data)
+        : KafkaSelfManagedEventSchema.parse(data);
+
+    return Object.values(parsedEnvelope.records).map((topicRecord) => {
+      return topicRecord.map((record) => {
+        return super.parse(record.value, schema);
+      });
     });
-  });
-};
+  }
+
+  public static safeParse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): ParsedResult {
+    // manually fetch event source to deside between Msk or SelfManaged
+    const eventSource = (data as KafkaMskEvent)['eventSource'];
+
+    const parsedEnvelope =
+      eventSource === 'aws:kafka'
+        ? KafkaMskEventSchema.safeParse(data)
+        : KafkaSelfManagedEventSchema.safeParse(data);
+
+    if (!parsedEnvelope.success) {
+      return {
+        ...parsedEnvelope,
+        originalEvent: data,
+      };
+    }
+    const parsedRecords: z.infer<T>[] = [];
+
+    for (const topicRecord of Object.values(parsedEnvelope.data.records)) {
+      for (const record of topicRecord) {
+        const parsedRecord = super.safeParse(record.value, schema);
+        if (!parsedRecord.success) {
+          return {
+            success: false,
+            error: parsedRecord.error,
+            originalEvent: data,
+          };
+        }
+        parsedRecords.push(parsedRecord.data);
+      }
+    }
+
+    return {
+      success: true,
+      data: parsedRecords,
+    };
+  }
+}
diff --git a/packages/parser/src/envelopes/kinesis-firehose.ts b/packages/parser/src/envelopes/kinesis-firehose.ts
index e51ae47a12..7dca3cae73 100644
--- a/packages/parser/src/envelopes/kinesis-firehose.ts
+++ b/packages/parser/src/envelopes/kinesis-firehose.ts
@@ -1,6 +1,7 @@
-import { parse } from './envelope.js';
-import { z, ZodSchema } from 'zod';
-import { KinesisFirehoseSchema } from '../schemas/kinesis-firehose.js';
+import { z, type ZodSchema } from 'zod';
+import { Envelope } from './envelope.js';
+import { KinesisFirehoseSchema } from '../schemas/index.js';
+import type { ParsedResult } from '../types/index.js';
 
 /**
  * Kinesis Firehose Envelope to extract array of Records
@@ -14,13 +15,47 @@ import { KinesisFirehoseSchema } from '../schemas/kinesis-firehose.js';
  *
  *  https://docs.aws.amazon.com/lambda/latest/dg/services-kinesisfirehose.html
  */
-export const kinesisFirehoseEnvelope = <T extends ZodSchema>(
-  data: unknown,
-  schema: T
-): z.infer<T> => {
-  const parsedEnvelope = KinesisFirehoseSchema.parse(data);
+export class KinesisFirehoseEnvelope extends Envelope {
+  public static parse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): z.infer<T> {
+    const parsedEnvelope = KinesisFirehoseSchema.parse(data);
 
-  return parsedEnvelope.records.map((record) => {
-    return parse(record.data, schema);
-  });
-};
+    return parsedEnvelope.records.map((record) => {
+      return super.parse(record.data, schema);
+    });
+  }
+
+  public static safeParse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): ParsedResult {
+    const parsedEnvelope = KinesisFirehoseSchema.safeParse(data);
+
+    if (!parsedEnvelope.success) {
+      return {
+        ...parsedEnvelope,
+        originalEvent: data,
+      };
+    }
+    const parsedRecords: z.infer<T>[] = [];
+
+    for (const record of parsedEnvelope.data.records) {
+      const parsedData = super.safeParse(record.data, schema);
+      if (!parsedData.success) {
+        return {
+          success: false,
+          error: parsedData.error,
+          originalEvent: data,
+        };
+      }
+      parsedRecords.push(parsedData.data);
+    }
+
+    return {
+      success: true,
+      data: parsedRecords,
+    };
+  }
+}
diff --git a/packages/parser/src/envelopes/kinesis.ts b/packages/parser/src/envelopes/kinesis.ts
index 311223042d..470df40522 100644
--- a/packages/parser/src/envelopes/kinesis.ts
+++ b/packages/parser/src/envelopes/kinesis.ts
@@ -1,6 +1,7 @@
-import { parse } from './envelope.js';
-import { z, ZodSchema } from 'zod';
+import { Envelope } from './envelope.js';
+import { z, type ZodSchema } from 'zod';
 import { KinesisDataStreamSchema } from '../schemas/kinesis.js';
+import type { ParsedResult } from '../types/index.js';
 
 /**
  * Kinesis Data Stream Envelope to extract array of Records
@@ -12,13 +13,46 @@ import { KinesisDataStreamSchema } from '../schemas/kinesis.js';
  * Note: Records will be parsed the same way so if model is str,
  * all items in the list will be parsed as str and not as JSON (and vice versa)
  */
-export const kinesisEnvelope = <T extends ZodSchema>(
-  data: unknown,
-  schema: T
-): z.infer<T> => {
-  const parsedEnvelope = KinesisDataStreamSchema.parse(data);
+export class KinesisEnvelope extends Envelope {
+  public static parse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): z.infer<T> {
+    const parsedEnvelope = KinesisDataStreamSchema.parse(data);
 
-  return parsedEnvelope.Records.map((record) => {
-    return parse(record.kinesis.data, schema);
-  });
-};
+    return parsedEnvelope.Records.map((record) => {
+      return super.parse(record.kinesis.data, schema);
+    });
+  }
+
+  public static safeParse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): ParsedResult {
+    const parsedEnvelope = KinesisDataStreamSchema.safeParse(data);
+    if (!parsedEnvelope.success) {
+      return {
+        ...parsedEnvelope,
+        originalEvent: data,
+      };
+    }
+
+    const parsedRecords: z.infer<T>[] = [];
+
+    for (const record of parsedEnvelope.data.Records) {
+      const parsedRecord = super.safeParse(record.kinesis.data, schema);
+      if (!parsedRecord.success) {
+        return {
+          ...parsedRecord,
+          originalEvent: data,
+        };
+      }
+      parsedRecords.push(parsedRecord.data);
+    }
+
+    return {
+      success: true,
+      data: parsedRecords,
+    };
+  }
+}
diff --git a/packages/parser/src/envelopes/lambda.ts b/packages/parser/src/envelopes/lambda.ts
index 3ac1f2b8c6..a839c68287 100644
--- a/packages/parser/src/envelopes/lambda.ts
+++ b/packages/parser/src/envelopes/lambda.ts
@@ -1,18 +1,46 @@
-import { parse } from './envelope.js';
-import { z, ZodSchema } from 'zod';
-import { LambdaFunctionUrlSchema } from '../schemas/lambda.js';
+import { Envelope } from './envelope.js';
+import { z, type ZodSchema } from 'zod';
+import { LambdaFunctionUrlSchema } from '../schemas/index.js';
+import type { ParsedResult } from '../types/index.js';
 
 /**
  * Lambda function URL envelope to extract data within body key
  */
-export const lambdaFunctionUrlEnvelope = <T extends ZodSchema>(
-  data: unknown,
-  schema: T
-): z.infer<T> => {
-  const parsedEnvelope = LambdaFunctionUrlSchema.parse(data);
-  if (!parsedEnvelope.body) {
-    throw new Error('Body field of Lambda function URL event is undefined');
+export class LambdaFunctionUrlEnvelope extends Envelope {
+  public static parse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): z.infer<T> {
+    const parsedEnvelope = LambdaFunctionUrlSchema.parse(data);
+
+    if (!parsedEnvelope.body) {
+      throw new Error('Body field of Lambda function URL event is undefined');
+    }
+
+    return super.parse(parsedEnvelope.body, schema);
   }
 
-  return parse(parsedEnvelope.body, schema);
-};
+  public static safeParse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): ParsedResult {
+    const parsedEnvelope = LambdaFunctionUrlSchema.safeParse(data);
+
+    if (!parsedEnvelope.success) {
+      return {
+        ...parsedEnvelope,
+        originalEvent: data,
+      };
+    }
+
+    const parsedBody = super.safeParse(parsedEnvelope.data.body, schema);
+    if (!parsedBody.success) {
+      return {
+        ...parsedBody,
+        originalEvent: data,
+      };
+    }
+
+    return parsedBody;
+  }
+}
diff --git a/packages/parser/src/envelopes/sns.ts b/packages/parser/src/envelopes/sns.ts
index 3e897a00a8..1141dff305 100644
--- a/packages/parser/src/envelopes/sns.ts
+++ b/packages/parser/src/envelopes/sns.ts
@@ -1,7 +1,8 @@
-import { z, ZodSchema } from 'zod';
-import { parse } from './envelope.js';
+import { z, type ZodSchema } from 'zod';
+import { Envelope } from './envelope.js';
 import { SnsSchema, SnsSqsNotificationSchema } from '../schemas/sns.js';
 import { SqsSchema } from '../schemas/sqs.js';
+import type { ParsedResult } from '../types/index.js';
 
 /**
  * SNS Envelope to extract array of Records
@@ -12,16 +13,49 @@ import { SqsSchema } from '../schemas/sqs.js';
  * Note: Records will be parsed the same way so if model is str,
  * all items in the list will be parsed as str and npt as JSON (and vice versa)
  */
-export const snsEnvelope = <T extends ZodSchema>(
-  data: unknown,
-  schema: T
-): z.infer<T> => {
-  const parsedEnvelope = SnsSchema.parse(data);
+export class SnsEnvelope extends Envelope {
+  public static parse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): z.infer<T> {
+    const parsedEnvelope = SnsSchema.parse(data);
 
-  return parsedEnvelope.Records.map((record) => {
-    return parse(record.Sns.Message, schema);
-  });
-};
+    return parsedEnvelope.Records.map((record) => {
+      return super.parse(record.Sns.Message, schema);
+    });
+  }
+
+  public static safeParse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): ParsedResult {
+    const parsedEnvelope = SnsSchema.safeParse(data);
+
+    if (!parsedEnvelope.success) {
+      return {
+        ...parsedEnvelope,
+        originalEvent: data,
+      };
+    }
+
+    const parsedMessages: z.infer<T>[] = [];
+    for (const record of parsedEnvelope.data.Records) {
+      const parsedMessage = super.safeParse(record.Sns.Message, schema);
+      if (!parsedMessage.success) {
+        return {
+          ...parsedMessage,
+          originalEvent: data,
+        };
+      }
+      parsedMessages.push(parsedMessage.data);
+    }
+
+    return {
+      success: true,
+      data: parsedMessages,
+    };
+  }
+}
 
 /**
  *  SNS plus SQS Envelope to extract array of Records
@@ -34,17 +68,68 @@ export const snsEnvelope = <T extends ZodSchema>(
  *  3. Finally, parse provided model against payload extracted
  *
  */
-export const snsSqsEnvelope = <T extends ZodSchema>(
-  data: unknown,
-  schema: T
-): z.infer<T> => {
-  const parsedEnvelope = SqsSchema.parse(data);
-
-  return parsedEnvelope.Records.map((record) => {
-    const snsNotification = SnsSqsNotificationSchema.parse(
-      JSON.parse(record.body)
-    );
-
-    return parse(snsNotification.Message, schema);
-  });
-};
+export class SnsSqsEnvelope extends Envelope {
+  public static parse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): z.infer<T> {
+    const parsedEnvelope = SqsSchema.parse(data);
+
+    return parsedEnvelope.Records.map((record) => {
+      const snsNotification = SnsSqsNotificationSchema.parse(
+        JSON.parse(record.body)
+      );
+
+      return super.parse(snsNotification.Message, schema);
+    });
+  }
+
+  public static safeParse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): ParsedResult {
+    const parsedEnvelope = SqsSchema.safeParse(data);
+    if (!parsedEnvelope.success) {
+      return {
+        ...parsedEnvelope,
+        originalEvent: data,
+      };
+    }
+
+    const parsedMessages: z.infer<T>[] = [];
+
+    // JSON.parse can throw an error, thus we catch it and return ParsedErrorResult
+    try {
+      for (const record of parsedEnvelope.data.Records) {
+        const snsNotification = SnsSqsNotificationSchema.safeParse(
+          JSON.parse(record.body)
+        );
+        if (!snsNotification.success) {
+          return {
+            ...snsNotification,
+            originalEvent: data,
+          };
+        }
+        const parsedMessage = super.safeParse(
+          snsNotification.data.Message,
+          schema
+        );
+        if (!parsedMessage.success) {
+          return {
+            ...parsedMessage,
+            originalEvent: data,
+          };
+        }
+        parsedMessages.push(parsedMessage.data);
+      }
+    } catch (e) {
+      return {
+        success: false,
+        error: e as Error,
+        originalEvent: data,
+      };
+    }
+
+    return { success: true, data: parsedMessages };
+  }
+}
diff --git a/packages/parser/src/envelopes/sqs.ts b/packages/parser/src/envelopes/sqs.ts
index 2757663a95..b1c8f6c059 100644
--- a/packages/parser/src/envelopes/sqs.ts
+++ b/packages/parser/src/envelopes/sqs.ts
@@ -1,6 +1,7 @@
-import { z, ZodSchema } from 'zod';
+import { z, type ZodSchema } from 'zod';
 import { SqsSchema } from '../schemas/sqs.js';
-import { parse } from './envelope.js';
+import { Envelope } from './envelope.js';
+import type { ParsedResult } from '../types/index.js';
 
 /**
  *  SQS Envelope to extract array of Records
@@ -11,13 +12,42 @@ import { parse } from './envelope.js';
  *  Note: Records will be parsed the same way so if model is str,
  *  all items in the list will be parsed as str and npt as JSON (and vice versa)
  */
-export const sqsEnvelope = <T extends ZodSchema>(
-  data: unknown,
-  schema: T
-): z.infer<T> => {
-  const parsedEnvelope = SqsSchema.parse(data);
+export class SqsEnvelope extends Envelope {
+  public static parse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): z.infer<T> {
+    const parsedEnvelope = SqsSchema.parse(data);
 
-  return parsedEnvelope.Records.map((record) => {
-    return parse(record.body, schema);
-  });
-};
+    return parsedEnvelope.Records.map((record) => {
+      return super.parse(record.body, schema);
+    });
+  }
+
+  public static safeParse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): ParsedResult {
+    const parsedEnvelope = SqsSchema.safeParse(data);
+    if (!parsedEnvelope.success) {
+      return {
+        ...parsedEnvelope,
+        originalEvent: data,
+      };
+    }
+
+    const parsedRecords: z.infer<T>[] = [];
+    for (const record of parsedEnvelope.data.Records) {
+      const parsedRecord = super.safeParse(record.body, schema);
+      if (!parsedRecord.success) {
+        return {
+          ...parsedRecord,
+          originalEvent: data,
+        };
+      }
+      parsedRecords.push(parsedRecord.data);
+    }
+
+    return { success: true, data: parsedRecords };
+  }
+}
diff --git a/packages/parser/src/envelopes/vpc-lattice.ts b/packages/parser/src/envelopes/vpc-lattice.ts
index 03d2998757..a7150dd5a4 100644
--- a/packages/parser/src/envelopes/vpc-lattice.ts
+++ b/packages/parser/src/envelopes/vpc-lattice.ts
@@ -1,15 +1,43 @@
-import { parse } from './envelope.js';
-import { z, ZodSchema } from 'zod';
-import { VpcLatticeSchema } from '../schemas/vpc-lattice.js';
+import { z, type ZodSchema } from 'zod';
+import { Envelope } from './envelope.js';
+import { VpcLatticeSchema } from '../schemas/index.js';
+import type { ParsedResult } from '../types/index.js';
 
 /**
  * Amazon VPC Lattice envelope to extract data within body key
  */
-export const vpcLatticeEnvelope = <T extends ZodSchema>(
-  data: unknown,
-  schema: T
-): z.infer<T> => {
-  const parsedEnvelope = VpcLatticeSchema.parse(data);
-
-  return parse(parsedEnvelope.body, schema);
-};
+
+export class VpcLatticeEnvelope extends Envelope {
+  public static parse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): z.infer<T> {
+    const parsedEnvelope = VpcLatticeSchema.parse(data);
+
+    return super.parse(parsedEnvelope.body, schema);
+  }
+
+  public static safeParse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): ParsedResult {
+    const parsedEnvelope = VpcLatticeSchema.safeParse(data);
+    if (!parsedEnvelope.success) {
+      return {
+        ...parsedEnvelope,
+        originalEvent: data,
+      };
+    }
+
+    const parsedBody = super.safeParse(parsedEnvelope.data.body, schema);
+
+    if (!parsedBody.success) {
+      return {
+        ...parsedBody,
+        originalEvent: data,
+      };
+    }
+
+    return parsedBody;
+  }
+}
diff --git a/packages/parser/src/envelopes/vpc-latticev2.ts b/packages/parser/src/envelopes/vpc-latticev2.ts
index a3fa4389c0..7ef31b0702 100644
--- a/packages/parser/src/envelopes/vpc-latticev2.ts
+++ b/packages/parser/src/envelopes/vpc-latticev2.ts
@@ -1,15 +1,42 @@
-import { parse } from './envelope.js';
-import { z, ZodSchema } from 'zod';
-import { VpcLatticeV2Schema } from '../schemas/vpc-latticev2.js';
+import { Envelope } from './envelope.js';
+import { z, type ZodSchema } from 'zod';
+import { VpcLatticeV2Schema } from '../schemas/index.js';
+import type { ParsedResult } from '../types/index.js';
 
 /**
  * Amazon VPC Lattice envelope to extract data within body key
  */
-export const vpcLatticeV2Envelope = <T extends ZodSchema>(
-  data: unknown,
-  schema: T
-): z.infer<T> => {
-  const parsedEnvelope = VpcLatticeV2Schema.parse(data);
-
-  return parse(parsedEnvelope.body, schema);
-};
+export class VpcLatticeV2Envelope extends Envelope {
+  public static parse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): z.infer<T> {
+    const parsedEnvelope = VpcLatticeV2Schema.parse(data);
+
+    return super.parse(parsedEnvelope.body, schema);
+  }
+
+  public static safeParse<T extends ZodSchema>(
+    data: unknown,
+    schema: T
+  ): ParsedResult {
+    const parsedEnvelope = VpcLatticeV2Schema.safeParse(data);
+    if (!parsedEnvelope.success) {
+      return {
+        ...parsedEnvelope,
+        originalEvent: data,
+      };
+    }
+
+    const parsedBody = super.safeParse(parsedEnvelope.data.body, schema);
+
+    if (!parsedBody.success) {
+      return {
+        ...parsedBody,
+        originalEvent: data,
+      };
+    }
+
+    return parsedBody;
+  }
+}
diff --git a/packages/parser/src/index.ts b/packages/parser/src/index.ts
index bb7ee75eb5..940216fe90 100644
--- a/packages/parser/src/index.ts
+++ b/packages/parser/src/index.ts
@@ -1 +1 @@
-export { parser } from './parser.js';
+export { parser } from './parserDecorator.js';
diff --git a/packages/parser/src/middleware/parser.ts b/packages/parser/src/middleware/parser.ts
index 8a2c26c327..0c536b0aeb 100644
--- a/packages/parser/src/middleware/parser.ts
+++ b/packages/parser/src/middleware/parser.ts
@@ -1,7 +1,8 @@
 import { type MiddyLikeRequest } from '@aws-lambda-powertools/commons/types';
 import { type MiddlewareObj } from '@middy/core';
 import { type ZodSchema } from 'zod';
-import { type ParserOptions } from '../types/ParserOptions.js';
+import { type ParserOptions } from '../types/parser.js';
+import { parse } from '../parser.js';
 
 /**
  * A middiy middleware to parse your event.
@@ -35,12 +36,9 @@ const parser = <S extends ZodSchema>(
   options: ParserOptions<S>
 ): MiddlewareObj => {
   const before = (request: MiddyLikeRequest): void => {
-    const { schema, envelope } = options;
-    if (envelope) {
-      request.event = envelope(request.event, schema);
-    } else {
-      request.event = schema.parse(request.event);
-    }
+    const { schema, envelope, safeParse } = options;
+
+    request.event = parse(request.event, envelope, schema, safeParse);
   };
 
   return {
diff --git a/packages/parser/src/parser.ts b/packages/parser/src/parser.ts
index 1320b643a8..89ed2e4ffb 100644
--- a/packages/parser/src/parser.ts
+++ b/packages/parser/src/parser.ts
@@ -1,57 +1,66 @@
-import { HandlerMethodDecorator } from '@aws-lambda-powertools/commons/types';
-import { Context, Handler } from 'aws-lambda';
-import { ZodSchema } from 'zod';
-import { type ParserOptions } from './types/ParserOptions.js';
+import type { ParsedResult, Envelope } from './types/index.js';
+import { z, type ZodSchema } from 'zod';
 
 /**
- * A decorator to parse your event.
+ * Parse the data using the provided schema, envelope and safeParse flag
  *
  * @example
  * ```typescript
- *
- * import { parser } from '@aws-lambda-powertools/parser';
- * import { sqsEnvelope } from '@aws-lambda-powertools/parser/envelopes/sqs';
- *
+ * import { z } from 'zod';
+ * import type { SqsEvent, ParsedResult } from '@aws-lambda-powertools/parser/types';
+ * import { SqsEnvelope } from '@aws-lambda-powertools/parser/types/envelopes';
+ * import { parse } from '@aws-lambda-powertools/parser';
  *
  * const Order = z.object({
- *  orderId: z.string(),
- *  description: z.string(),
- * }
+ *   orderId: z.string(),
+ *   description: z.string(),
+ * });
  *
- * class Lambda extends LambdaInterface {
+ * const handler = async (event: SqsEvent, context: unknown): Promise<unknown> => {
+ *   const parsedEvent = parse(event, SqsEnvelope, Order);
  *
- *   @parser({ envelope: sqsEnvelope, schema: OrderSchema })
- *   public async handler(event: Order, _context: Context): Promise<unknown> {
- *   // sqs event is parsed and the payload is extracted and parsed
- *   // apply business logic to your Order event
- *   const res = processOrder(event);
- *   return res;
- *   }
+ *   const parsedSafe: ParsedResult<SqsEnvelope> = parse(event, SqsEnvelope, Order, true)
  * }
- *
- * @param options
+ * @param data the data to parse
+ * @param envelope the envelope to use, can be undefined
+ * @param schema the schema to use
+ * @param safeParse whether to use safeParse or not, if true it will return a ParsedResult with the original event if the parsing fails
  */
-export const parser = <S extends ZodSchema>(
-  options: ParserOptions<S>
-): HandlerMethodDecorator => {
-  return (_target, _propertyKey, descriptor) => {
-    const original = descriptor.value!;
-
-    const { schema, envelope } = options;
+const parse = <T extends ZodSchema, E extends Envelope>(
+  data: z.infer<T>,
+  envelope: E | undefined,
+  schema: T,
+  safeParse?: boolean
+): ParsedResult | z.infer<T> => {
+  if (envelope && safeParse) {
+    return envelope.safeParse(data, schema);
+  }
+  if (envelope) {
+    return envelope.parse(data, schema);
+  }
+  if (safeParse) {
+    return safeParseSchema(data, schema);
+  }
 
-    descriptor.value = async function (
-      this: Handler,
-      event: unknown,
-      context: Context,
-      callback
-    ) {
-      const parsedEvent = envelope
-        ? envelope(event, schema)
-        : schema.parse(event);
+  return schema.parse(data);
+};
 
-      return original.call(this, parsedEvent, context, callback);
-    };
+/**
+ * Parse the data safely using the provided schema.
+ * This function will not throw an error if the parsing fails, instead it will return a ParsedResultError with the original event.
+ * Otherwise, it will return ParsedResultSuccess with the parsed data.
+ * @param data the data to parse
+ * @param schema the zod schema to use
+ */
+const safeParseSchema = <T extends ZodSchema>(
+  data: z.infer<T>,
+  schema: T
+): ParsedResult => {
+  const result = schema.safeParse(data);
 
-    return descriptor;
-  };
+  return result.success
+    ? result
+    : { success: false, error: result.error, originalEvent: data };
 };
+
+export { parse };
diff --git a/packages/parser/src/parserDecorator.ts b/packages/parser/src/parserDecorator.ts
new file mode 100644
index 0000000000..0572d3a22e
--- /dev/null
+++ b/packages/parser/src/parserDecorator.ts
@@ -0,0 +1,94 @@
+import type { HandlerMethodDecorator } from '@aws-lambda-powertools/commons/types';
+import type { Context, Handler } from 'aws-lambda';
+import { ZodSchema, z } from 'zod';
+import { parse } from './parser.js';
+import type { ParserOptions, ParsedResult } from './types/index.js';
+
+/**
+ * A decorator to parse your event.
+ *
+ * @example
+ * ```typescript
+ * import { z } from 'zod';
+ * import type { LambdaInterface } from '@aws-lambda-powertools/commons/types';
+ * import type { SqSEvent } from '@aws-lambda-powertools/parser/types;
+ * import { parser } from '@aws-lambda-powertools/parser';
+ * import { SqsEnvelope } from '@aws-lambda-powertools/parser/envelopes';
+ *
+ * const Order = z.object({
+ *   orderId: z.string(),
+ *   description: z.string(),
+ * });
+ *
+ * class Lambda implements LambdaInterface {
+ *
+ *   ⁣@parser({ envelope: SqsEnvelope, schema: OrderSchema })
+ *   public async handler(event: Order, _context: Context): Promise<unknown> {
+ *   // sqs event is parsed and the payload is extracted and parsed
+ *   // apply business logic to your Order event
+ *   const res = processOrder(event);
+ *   return res;
+ *   }
+ * }
+ *
+ * ```
+ *
+ * In case you want to parse the event and handle the error, you can use the safeParse option.
+ * The safeParse option will return an object with the parsed event and an error object if the parsing fails.
+ *
+ * @example
+ * ```typescript
+ *
+ * import type { LambdaInterface } from '@aws-lambda-powertools/commons/types';
+ * import type { SqSEvent, ParsedResult } from '@aws-lambda-powertools/parser/types;
+ * import { z } from 'zod';
+ * import { parser } from '@aws-lambda-powertools/parser';
+ * import { SqsEnvelope } from '@aws-lambda-powertools/parser/envelopes';
+ *
+ *
+ * const Order = z.object({
+ *   orderId: z.string(),
+ *   description: z.string(),
+ * }
+ *
+ * class Lambda implements LambdaInterface {
+ *
+ *   ⁣git@parser({ envelope: SqsEnvelope, schema: OrderSchema,  safeParse: true })
+ *   public async handler(event: ParsedResult<Order>, _context: unknown): Promise<unknown> {
+ *     if (event.success) {
+ *      // event.data is the parsed event object of type Order
+ *     } else {
+ *      // event.error is the error object, you can inspect and recover
+ *      // event.originalEvent is the original event that failed to parse
+ *     }
+ *   }
+ * }
+ * ```
+ *
+ * @param options Configure the parser with the `schema`, `envelope` and whether to `safeParse` or not
+ */
+export const parser = <S extends ZodSchema>(
+  options: ParserOptions<S>
+): HandlerMethodDecorator => {
+  return (_target, _propertyKey, descriptor) => {
+    const original = descriptor.value!;
+
+    const { schema, envelope, safeParse } = options;
+
+    descriptor.value = async function (
+      this: Handler,
+      event: unknown,
+      context: Context,
+      callback
+    ) {
+      const parsedEvent: ParsedResult<
+        typeof event,
+        z.infer<typeof schema>
+      > = parse(event, envelope, schema, safeParse);
+
+      return original.call(this, parsedEvent, context, callback);
+    };
+
+    return descriptor;
+  };
+};
diff --git a/packages/parser/src/types/ParserOptions.ts b/packages/parser/src/types/ParserOptions.ts
deleted file mode 100644
index 57fcb9beca..0000000000
--- a/packages/parser/src/types/ParserOptions.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import type { ZodSchema } from 'zod';
-import { Envelope } from './envelope.js';
-
-export type ParserOptions<S extends ZodSchema> = {
-  schema: S;
-  envelope?: Envelope;
-};
diff --git a/packages/parser/src/types/envelope.ts b/packages/parser/src/types/envelope.ts
index e54958dca2..9e049c1178 100644
--- a/packages/parser/src/types/envelope.ts
+++ b/packages/parser/src/types/envelope.ts
@@ -1,29 +1,32 @@
-import { type apiGatewayEnvelope } from '../envelopes/apigw.js';
-import { type apiGatewayV2Envelope } from '../envelopes/apigwv2.js';
-import { type cloudWatchEnvelope } from '../envelopes/cloudwatch.js';
-import { type dynamoDDStreamEnvelope } from '../envelopes/dynamodb.js';
-import { type kafkaEnvelope } from '../envelopes/kafka.js';
-import { type kinesisEnvelope } from '../envelopes/kinesis.js';
-import { type kinesisFirehoseEnvelope } from '../envelopes/kinesis-firehose.js';
-import { type lambdaFunctionUrlEnvelope } from '../envelopes/lambda.js';
-import { type snsEnvelope, type snsSqsEnvelope } from '../envelopes/sns.js';
-import { type sqsEnvelope } from '../envelopes/sqs.js';
-import { type vpcLatticeEnvelope } from '../envelopes/vpc-lattice.js';
-import { type vpcLatticeV2Envelope } from '../envelopes/vpc-latticev2.js';
-import { type eventBridgeEnvelope } from '../envelopes/event-bridge.js';
+import type {
+  ApiGatewayEnvelope,
+  KinesisFirehoseEnvelope,
+  KinesisEnvelope,
+  KafkaEnvelope,
+  CloudWatchEnvelope,
+  EventBridgeEnvelope,
+  ApiGatewayV2Envelope,
+  DynamoDBStreamEnvelope,
+  LambdaFunctionUrlEnvelope,
+  SnsEnvelope,
+  SnsSqsEnvelope,
+  SqsEnvelope,
+  VpcLatticeEnvelope,
+  VpcLatticeV2Envelope,
+} from '../envelopes/index.js';
 
 export type Envelope =
-  | typeof apiGatewayEnvelope
-  | typeof apiGatewayV2Envelope
-  | typeof cloudWatchEnvelope
-  | typeof dynamoDDStreamEnvelope
-  | typeof eventBridgeEnvelope
-  | typeof kafkaEnvelope
-  | typeof kinesisEnvelope
-  | typeof kinesisFirehoseEnvelope
-  | typeof lambdaFunctionUrlEnvelope
-  | typeof snsEnvelope
-  | typeof snsSqsEnvelope
-  | typeof sqsEnvelope
-  | typeof vpcLatticeEnvelope
-  | typeof vpcLatticeV2Envelope;
+  | typeof ApiGatewayEnvelope
+  | typeof ApiGatewayV2Envelope
+  | typeof CloudWatchEnvelope
+  | typeof DynamoDBStreamEnvelope
+  | typeof EventBridgeEnvelope
+  | typeof KafkaEnvelope
+  | typeof KinesisEnvelope
+  | typeof KinesisFirehoseEnvelope
+  | typeof LambdaFunctionUrlEnvelope
+  | typeof SnsEnvelope
+  | typeof SnsSqsEnvelope
+  | typeof SqsEnvelope
+  | typeof VpcLatticeEnvelope
+  | typeof VpcLatticeV2Envelope;
diff --git a/packages/parser/src/types/index.ts b/packages/parser/src/types/index.ts
index d1c8e6fc13..26052e60b6 100644
--- a/packages/parser/src/types/index.ts
+++ b/packages/parser/src/types/index.ts
@@ -1,4 +1,9 @@
-export type { ParserOptions } from './ParserOptions.js';
+export type {
+  ParserOptions,
+  ParsedResult,
+  ParsedResultSuccess,
+  ParsedResultError,
+} from '../types/parser.js';
 export type { Envelope } from './envelope.js';
 
 export type {
diff --git a/packages/parser/src/types/parser.ts b/packages/parser/src/types/parser.ts
new file mode 100644
index 0000000000..c775a78d94
--- /dev/null
+++ b/packages/parser/src/types/parser.ts
@@ -0,0 +1,30 @@
+import type { ZodSchema, ZodError } from 'zod';
+import type { Envelope } from './envelope.js';
+
+type ParserOptions<S extends ZodSchema> = {
+  schema: S;
+  envelope?: Envelope;
+  safeParse?: boolean;
+};
+
+type ParsedResultSuccess<Output> = {
+  success: true;
+  data: Output;
+};
+
+type ParsedResultError<Input> = {
+  success: false;
+  error: ZodError | Error;
+  originalEvent: Input;
+};
+
+type ParsedResult<Input = unknown, Output = unknown> =
+  | ParsedResultSuccess<Output>
+  | ParsedResultError<Input>;
+
+export type {
+  ParserOptions,
+  ParsedResult,
+  ParsedResultError,
+  ParsedResultSuccess,
+};
diff --git a/packages/parser/src/types/schema.ts b/packages/parser/src/types/schema.ts
index 6e15f17bd0..6b18b57968 100644
--- a/packages/parser/src/types/schema.ts
+++ b/packages/parser/src/types/schema.ts
@@ -90,31 +90,31 @@ type VpcLatticeEvent = z.infer<typeof VpcLatticeSchema>;
 
 type VpcLatticeEventV2 = z.infer<typeof VpcLatticeV2Schema>;
 
-export {
-  type ALBEvent,
-  type ALBMultiValueHeadersEvent,
-  type APIGatewayProxyEvent,
-  type APIGatewayProxyEventV2,
-  type CloudFormationCustomResourceCreateEvent,
-  type CloudFormationCustomResourceDeleteEvent,
-  type CloudFormationCustomResourceUpdateEvent,
-  type CloudWatchLogsEvent,
-  type DynamoDBStreamEvent,
-  type EventBridgeEvent,
-  type KafkaSelfManagedEvent,
-  type KafkaMskEvent,
-  type KinesisDataStreamEvent,
-  type KinesisDataStreamRecord,
-  type KinesisDataStreamRecordPayload,
-  type KinesisFireHoseEvent,
-  type KinesisFireHoseSqsEvent,
-  type LambdaFunctionUrlEvent,
-  type S3Event,
-  type S3EventNotificationEventBridge,
-  type S3SqsEventNotification,
-  type SesEvent,
-  type SnsEvent,
-  type SqsEvent,
-  type VpcLatticeEvent,
-  type VpcLatticeEventV2,
+export type {
+  ALBEvent,
+  ALBMultiValueHeadersEvent,
+  APIGatewayProxyEvent,
+  APIGatewayProxyEventV2,
+  CloudFormationCustomResourceCreateEvent,
+  CloudFormationCustomResourceDeleteEvent,
+  CloudFormationCustomResourceUpdateEvent,
+  CloudWatchLogsEvent,
+  DynamoDBStreamEvent,
+  EventBridgeEvent,
+  KafkaSelfManagedEvent,
+  KafkaMskEvent,
+  KinesisDataStreamEvent,
+  KinesisDataStreamRecord,
+  KinesisDataStreamRecordPayload,
+  KinesisFireHoseEvent,
+  KinesisFireHoseSqsEvent,
+  LambdaFunctionUrlEvent,
+  S3Event,
+  S3EventNotificationEventBridge,
+  S3SqsEventNotification,
+  SesEvent,
+  SnsEvent,
+  SqsEvent,
+  VpcLatticeEvent,
+  VpcLatticeEventV2,
 };
diff --git a/packages/parser/tests/unit/envelope.test.ts b/packages/parser/tests/unit/envelope.test.ts
new file mode 100644
index 0000000000..1f92ed6e64
--- /dev/null
+++ b/packages/parser/tests/unit/envelope.test.ts
@@ -0,0 +1,83 @@
+import { z, ZodError } from 'zod';
+import { Envelope } from '../../src/envelopes/envelope.js';
+
+describe('envelope: ', () => {
+  describe('parseSafe', () => {
+    it('returns success response when input is object', () => {
+      const result = Envelope.safeParse(
+        '{"name": "John"}',
+        z.object({ name: z.string() })
+      );
+      expect(result).toEqual({
+        success: true,
+        data: { name: 'John' },
+      });
+    });
+    it('returns success response when input is string', () => {
+      const result = Envelope.safeParse(
+        { name: 'John' },
+        z.object({ name: z.string() })
+      );
+      expect(result).toEqual({
+        success: true,
+        data: { name: 'John' },
+      });
+    });
+    it('returns error when input does not match schema', () => {
+      const result = Envelope.safeParse(
+        { name: 123 },
+        z.object({ name: z.string() })
+      );
+      expect(result).toEqual({
+        success: false,
+        error: expect.any(ZodError),
+        originalEvent: { name: 123 },
+      });
+    });
+
+    it('returns error when input is invalid JSON string', () => {
+      let err: unknown;
+      try {
+        JSON.parse('{name: "John"}');
+      } catch (e) {
+        err = e;
+      }
+      const result = Envelope.safeParse(
+        '{name: "John"}',
+        z.object({ name: z.string() })
+      );
+      expect(result).toEqual({
+        success: false,
+        error: err,
+        originalEvent: '{name: "John"}',
+      });
+    });
+  });
+
+  describe('parse', () => {
+    it('returns parsed data when input is object', () => {
+      const result = Envelope.parse(
+        { name: 'John' },
+        z.object({ name: z.string() })
+      );
+      expect(result).toEqual({ name: 'John' });
+    });
+    it('returns parsed data when input is string', () => {
+      const result = Envelope.parse(
+        '{"name": "John"}',
+        z.object({ name: z.string() })
+      );
+      expect(result).toEqual({ name: 'John' });
+    });
+    it('throw custom error if input is not string or object', () => {
+      expect(() => Envelope.parse(123, z.object({ name: z.string() }))).toThrow(
+        'Invalid data type for envelope. Expected string or object, got number'
+      );
+    });
+    it('throws error when input does not match schema', () => {
+      expect(() =>
+        Envelope.parse({ name: 123 }, z.object({ name: z.string() }))
+      ).toThrow();
+    });
+  });
+});
diff --git a/packages/parser/tests/unit/envelopes/apigwt.test.ts b/packages/parser/tests/unit/envelopes/apigwt.test.ts
index 03417fcc67..c9df0c03c7 100644
--- a/packages/parser/tests/unit/envelopes/apigwt.test.ts
+++ b/packages/parser/tests/unit/envelopes/apigwt.test.ts
@@ -7,23 +7,82 @@
 import { generateMock } from '@anatine/zod-mock';
 import { TestEvents, TestSchema } from '../schema/utils.js';
 import { APIGatewayProxyEvent } from '../../../src/types/';
-import { apiGatewayEnvelope } from '../../../src/envelopes/';
+import { ApiGatewayEnvelope } from '../../../src/envelopes/index.js';
+import { ZodError } from 'zod';
 
 describe('ApigwEnvelope ', () => {
-  it('should parse custom schema in envelope', () => {
-    const testCustomSchemaObject = generateMock(TestSchema);
-    const testEvent = TestEvents.apiGatewayProxyEvent as APIGatewayProxyEvent;
+  describe('parse', () => {
+    it('should parse custom schema in envelope', () => {
+      const testCustomSchemaObject = generateMock(TestSchema);
+      const testEvent = TestEvents.apiGatewayProxyEvent as APIGatewayProxyEvent;
 
-    testEvent.body = JSON.stringify(testCustomSchemaObject);
+      testEvent.body = JSON.stringify(testCustomSchemaObject);
 
-    const resp = apiGatewayEnvelope(testEvent, TestSchema);
-    expect(resp).toEqual(testCustomSchemaObject);
+      const resp = ApiGatewayEnvelope.parse(testEvent, TestSchema);
+      expect(resp).toEqual(testCustomSchemaObject);
+    });
+
+    it('should throw no body provided', () => {
+      const testEvent = TestEvents.apiGatewayProxyEvent as APIGatewayProxyEvent;
+      testEvent.body = undefined;
+
+      expect(() => ApiGatewayEnvelope.parse(testEvent, TestSchema)).toThrow();
+    });
+    it('should throw invalid event provided', () => {
+      const testEvent = TestEvents.apiGatewayProxyEvent as APIGatewayProxyEvent;
+      testEvent.body = 'invalid';
+
+      expect(() => ApiGatewayEnvelope.parse(testEvent, TestSchema)).toThrow();
+    });
   });
 
-  it('should throw no body provided', () => {
-    const testEvent = TestEvents.apiGatewayProxyEvent as APIGatewayProxyEvent;
-    testEvent.body = undefined;
+  describe('safeParse', () => {
+    it('should parse custom schema in envelope', () => {
+      const testCustomSchemaObject = generateMock(TestSchema);
+      const testEvent = TestEvents.apiGatewayProxyEvent as APIGatewayProxyEvent;
+
+      testEvent.body = JSON.stringify(testCustomSchemaObject);
+
+      const resp = ApiGatewayEnvelope.safeParse(testEvent, TestSchema);
+      expect(resp).toEqual({
+        success: true,
+        data: testCustomSchemaObject,
+      });
+    });
+
+    it('should return success false with original body if no body provided', () => {
+      const testEvent = TestEvents.apiGatewayProxyEvent as APIGatewayProxyEvent;
+      testEvent.body = undefined;
+
+      const resp = ApiGatewayEnvelope.safeParse(testEvent, TestSchema);
+      expect(resp).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: testEvent,
+      });
+    });
+
+    it('should return success false with original body if invalid body provided', () => {
+      const testEvent = TestEvents.apiGatewayProxyEvent as APIGatewayProxyEvent;
+      testEvent.body = 'invalid';
 
-    expect(() => apiGatewayEnvelope(testEvent, TestSchema)).toThrow();
+      const resp = ApiGatewayEnvelope.safeParse(testEvent, TestSchema);
+      expect(resp).toEqual({
+        success: false,
+        error: expect.any(SyntaxError),
+        originalEvent: testEvent,
+      });
+    });
+    it('should return success false if event is invalid', () => {
+      const resp = ApiGatewayEnvelope.safeParse(
+        'invalid' as unknown,
+        TestSchema
+      );
+      expect(resp).toEqual({
+        success: false,
+        error: expect.any(ZodError),
+        originalEvent: 'invalid',
+      });
+    });
   });
 });
diff --git a/packages/parser/tests/unit/envelopes/apigwv2.test.ts b/packages/parser/tests/unit/envelopes/apigwv2.test.ts
index 0d2d225bf1..dd5a82609b 100644
--- a/packages/parser/tests/unit/envelopes/apigwv2.test.ts
+++ b/packages/parser/tests/unit/envelopes/apigwv2.test.ts
@@ -7,24 +7,88 @@
 import { TestEvents, TestSchema } from '../schema/utils.js';
 import { generateMock } from '@anatine/zod-mock';
 import { APIGatewayProxyEventV2 } from 'aws-lambda';
-import { apiGatewayV2Envelope } from '../../../src/envelopes/';
+import { ApiGatewayV2Envelope } from '../../../src/envelopes/index.js';
 
 describe('ApiGwV2Envelope ', () => {
-  it('should parse custom schema in envelope', () => {
-    const testEvent =
-      TestEvents.apiGatewayProxyV2Event as APIGatewayProxyEventV2;
-    const data = generateMock(TestSchema);
+  describe('parse', () => {
+    it('should parse custom schema in envelope', () => {
+      const testEvent =
+        TestEvents.apiGatewayProxyV2Event as APIGatewayProxyEventV2;
+      const data = generateMock(TestSchema);
 
-    testEvent.body = JSON.stringify(data);
+      testEvent.body = JSON.stringify(data);
 
-    expect(apiGatewayV2Envelope(testEvent, TestSchema)).toEqual(data);
+      expect(ApiGatewayV2Envelope.parse(testEvent, TestSchema)).toEqual(data);
+    });
+
+    it('should throw when no body provided', () => {
+      const testEvent =
+        TestEvents.apiGatewayProxyV2Event as APIGatewayProxyEventV2;
+      testEvent.body = undefined;
+
+      expect(() => ApiGatewayV2Envelope.parse(testEvent, TestSchema)).toThrow();
+    });
+
+    it('should throw when invalid body provided', () => {
+      const testEvent =
+        TestEvents.apiGatewayProxyV2Event as APIGatewayProxyEventV2;
+      testEvent.body = 'invalid';
+
+      expect(() => ApiGatewayV2Envelope.parse(testEvent, TestSchema)).toThrow();
+    });
+    it('should throw when invalid event provided', () => {
+      expect(() =>
+        ApiGatewayV2Envelope.parse({ foo: 'bar' }, TestSchema)
+      ).toThrow();
+    });
   });
 
-  it('should throw when no body provided', () => {
-    const testEvent =
-      TestEvents.apiGatewayProxyV2Event as APIGatewayProxyEventV2;
-    testEvent.body = undefined;
+  describe('safeParse', () => {
+    it('should parse custom schema in envelope', () => {
+      const testEvent =
+        TestEvents.apiGatewayProxyV2Event as APIGatewayProxyEventV2;
+      const data = generateMock(TestSchema);
+
+      testEvent.body = JSON.stringify(data);
+
+      expect(ApiGatewayV2Envelope.safeParse(testEvent, TestSchema)).toEqual({
+        success: true,
+        data,
+      });
+    });
+
+    it('should return success false with original body if no body provided', () => {
+      const testEvent =
+        TestEvents.apiGatewayProxyV2Event as APIGatewayProxyEventV2;
+      testEvent.body = undefined;
+
+      expect(ApiGatewayV2Envelope.safeParse(testEvent, TestSchema)).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: testEvent,
+      });
+    });
+
+    it('should return success false with original body if invalid body provided', () => {
+      const testEvent =
+        TestEvents.apiGatewayProxyV2Event as APIGatewayProxyEventV2;
+      testEvent.body = 'invalid';
+
+      expect(ApiGatewayV2Envelope.safeParse(testEvent, TestSchema)).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: testEvent,
+      });
+    });
 
-    expect(() => apiGatewayV2Envelope(testEvent, TestSchema)).toThrow();
+    it('should return success false with original event if invalid event provided', () => {
+      expect(
+        ApiGatewayV2Envelope.safeParse({ foo: 'bar' }, TestSchema)
+      ).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: { foo: 'bar' },
+      });
+    });
   });
 });
diff --git a/packages/parser/tests/unit/envelopes/cloudwatch.test.ts b/packages/parser/tests/unit/envelopes/cloudwatch.test.ts
index e47d85f26d..adfe6e52f9 100644
--- a/packages/parser/tests/unit/envelopes/cloudwatch.test.ts
+++ b/packages/parser/tests/unit/envelopes/cloudwatch.test.ts
@@ -11,53 +11,121 @@ import {
   CloudWatchLogsDecodeSchema,
 } from '../../../src/schemas/';
 import { TestSchema } from '../schema/utils.js';
-import { cloudWatchEnvelope } from '../../../src/envelopes/';
+import { CloudWatchEnvelope } from '../../../src/envelopes/index.js';
+import { ZodError } from 'zod';
 
 describe('CloudWatch', () => {
-  it('should parse custom schema in envelope', () => {
-    const testEvent = {
-      awslogs: {
-        data: '',
-      },
-    };
-
-    const data = generateMock(TestSchema);
-    const eventMock = generateMock(CloudWatchLogEventSchema, {
-      stringMap: {
-        message: () => JSON.stringify(data),
-      },
+  describe('parse', () => {
+    it('should parse custom schema in envelope', () => {
+      const testEvent = {
+        awslogs: {
+          data: '',
+        },
+      };
+
+      const data = generateMock(TestSchema);
+      const eventMock = generateMock(CloudWatchLogEventSchema, {
+        stringMap: {
+          message: () => JSON.stringify(data),
+        },
+      });
+
+      const logMock = generateMock(CloudWatchLogsDecodeSchema);
+      logMock.logEvents = [eventMock];
+
+      testEvent.awslogs.data = gzipSync(
+        Buffer.from(JSON.stringify(logMock), 'utf8')
+      ).toString('base64');
+
+      expect(CloudWatchEnvelope.parse(testEvent, TestSchema)).toEqual([data]);
     });
 
-    const logMock = generateMock(CloudWatchLogsDecodeSchema);
-    logMock.logEvents = [eventMock];
+    it('should throw when schema does not match', () => {
+      const testEvent = {
+        awslogs: {
+          data: '',
+        },
+      };
 
-    testEvent.awslogs.data = gzipSync(
-      Buffer.from(JSON.stringify(logMock), 'utf8')
-    ).toString('base64');
+      const eventMock = generateMock(CloudWatchLogEventSchema, {
+        stringMap: {
+          message: () => JSON.stringify({ foo: 'bar' }),
+        },
+      });
 
-    expect(cloudWatchEnvelope(testEvent, TestSchema)).toEqual([data]);
+      const logMock = generateMock(CloudWatchLogsDecodeSchema);
+      logMock.logEvents = [eventMock];
+
+      testEvent.awslogs.data = gzipSync(
+        Buffer.from(JSON.stringify(logMock), 'utf8')
+      ).toString('base64');
+
+      expect(() => CloudWatchEnvelope.parse(testEvent, TestSchema)).toThrow();
+    });
   });
 
-  it('should throw when schema does not match', () => {
-    const testEvent = {
-      awslogs: {
-        data: '',
-      },
-    };
-
-    const eventMock = generateMock(CloudWatchLogEventSchema, {
-      stringMap: {
-        message: () => JSON.stringify({ foo: 'bar' }),
-      },
+  describe('safeParse', () => {
+    it('should parse custom schema in envelope', () => {
+      const testEvent = {
+        awslogs: {
+          data: '',
+        },
+      };
+
+      const data = generateMock(TestSchema);
+      const eventMock = generateMock(CloudWatchLogEventSchema, {
+        stringMap: {
+          message: () => JSON.stringify(data),
+        },
+      });
+
+      const logMock = generateMock(CloudWatchLogsDecodeSchema);
+      logMock.logEvents = [eventMock];
+
+      testEvent.awslogs.data = gzipSync(
+        Buffer.from(JSON.stringify(logMock), 'utf8')
+      ).toString('base64');
+
+      const actual = CloudWatchEnvelope.safeParse(testEvent, TestSchema);
+      expect(actual).toEqual({
+        success: true,
+        data: [data],
+      });
     });
 
-    const logMock = generateMock(CloudWatchLogsDecodeSchema);
-    logMock.logEvents = [eventMock];
+    it('should return success false when schema does not match', () => {
+      const testEvent = {
+        awslogs: {
+          data: '',
+        },
+      };
+
+      const eventMock = generateMock(CloudWatchLogEventSchema, {
+        stringMap: {
+          message: () => JSON.stringify({ foo: 'bar' }),
+        },
+      });
 
-    testEvent.awslogs.data = gzipSync(
-      Buffer.from(JSON.stringify(logMock), 'utf8')
-    ).toString('base64');
+      const logMock = generateMock(CloudWatchLogsDecodeSchema);
+      logMock.logEvents = [eventMock];
 
-    expect(() => cloudWatchEnvelope(testEvent, TestSchema)).toThrow();
+      testEvent.awslogs.data = gzipSync(
+        Buffer.from(JSON.stringify(logMock), 'utf8')
+      ).toString('base64');
+
+      expect(CloudWatchEnvelope.safeParse(testEvent, TestSchema)).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: testEvent,
+      });
+    });
+
+    it('should return success false when envelope does not match', () => {
+      expect(CloudWatchEnvelope.safeParse({ foo: 'bar' }, TestSchema)).toEqual({
+        success: false,
+        error: expect.any(ZodError),
+        originalEvent: { foo: 'bar' },
+      });
+    });
   });
 });
diff --git a/packages/parser/tests/unit/envelopes/dynamodb.test.ts b/packages/parser/tests/unit/envelopes/dynamodb.test.ts
index 0c9c940daf..4d2c9fb657 100644
--- a/packages/parser/tests/unit/envelopes/dynamodb.test.ts
+++ b/packages/parser/tests/unit/envelopes/dynamodb.test.ts
@@ -7,37 +7,122 @@
 import { generateMock } from '@anatine/zod-mock';
 import { TestEvents } from '../schema/utils.js';
 import { DynamoDBStreamEvent } from 'aws-lambda';
-import { z } from 'zod';
-import { dynamoDDStreamEnvelope } from '../../../src/envelopes/';
+import { z, ZodError } from 'zod';
+import { DynamoDBStreamEnvelope } from '../../../src/envelopes/index.js';
 
 describe('DynamoDB', () => {
   const schema = z.object({
     Message: z.record(z.literal('S'), z.string()),
     Id: z.record(z.literal('N'), z.number().min(0).max(100)),
   });
+  const mockOldImage = generateMock(schema);
+  const mockNewImage = generateMock(schema);
+  const dynamodbEvent = TestEvents.dynamoStreamEvent as DynamoDBStreamEvent;
+  (dynamodbEvent.Records[0].dynamodb!.NewImage as typeof mockNewImage) =
+    mockNewImage;
+  (dynamodbEvent.Records[1].dynamodb!.NewImage as typeof mockNewImage) =
+    mockNewImage;
+  (dynamodbEvent.Records[0].dynamodb!.OldImage as typeof mockOldImage) =
+    mockOldImage;
+  (dynamodbEvent.Records[1].dynamodb!.OldImage as typeof mockOldImage) =
+    mockOldImage;
+  describe('parse', () => {
+    it('parse should parse dynamodb envelope', () => {
+      const parsed = DynamoDBStreamEnvelope.parse(dynamodbEvent, schema);
+      expect(parsed[0]).toEqual({
+        OldImage: mockOldImage,
+        NewImage: mockNewImage,
+      });
+      expect(parsed[1]).toEqual({
+        OldImage: mockOldImage,
+        NewImage: mockNewImage,
+      });
+    });
+    it('parse should throw error if envelope invalid', () => {
+      expect(() =>
+        DynamoDBStreamEnvelope.parse({ foo: 'bar' }, schema)
+      ).toThrow();
+    });
+    it('parse should throw error if new or old image is invalid', () => {
+      const ddbEvent = TestEvents.dynamoStreamEvent as DynamoDBStreamEvent;
+      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+      // @ts-ignore
+      ddbEvent.Records[0].dynamodb!.NewImage.Id = 'foo';
+      expect(() => DynamoDBStreamEnvelope.parse(ddbEvent, schema)).toThrow();
+    });
+  });
+
+  describe('safeParse', () => {
+    it('safeParse should parse dynamodb envelope', () => {
+      const parsed = DynamoDBStreamEnvelope.safeParse(dynamodbEvent, schema);
+      expect(parsed.success).toBe(true);
+      expect(parsed).toEqual({
+        success: true,
+        data: [
+          {
+            OldImage: mockOldImage,
+            NewImage: mockNewImage,
+          },
+          {
+            OldImage: mockOldImage,
+            NewImage: mockNewImage,
+          },
+        ],
+      });
+    });
+    it('safeParse should return error if NewImage is invalid', () => {
+      const invalidDDBEvent =
+        TestEvents.dynamoStreamEvent as DynamoDBStreamEvent;
 
-  it('should parse dynamodb envelope', () => {
-    const mockOldImage = generateMock(schema);
-    const mockNewImage = generateMock(schema);
-    const dynamodbEvent = TestEvents.dynamoStreamEvent as DynamoDBStreamEvent;
-
-    (dynamodbEvent.Records[0].dynamodb!.NewImage as typeof mockNewImage) =
-      mockNewImage;
-    (dynamodbEvent.Records[1].dynamodb!.NewImage as typeof mockNewImage) =
-      mockNewImage;
-    (dynamodbEvent.Records[0].dynamodb!.OldImage as typeof mockOldImage) =
-      mockOldImage;
-    (dynamodbEvent.Records[1].dynamodb!.OldImage as typeof mockOldImage) =
-      mockOldImage;
-
-    const parsed = dynamoDDStreamEnvelope(dynamodbEvent, schema);
-    expect(parsed[0]).toEqual({
-      OldImage: mockOldImage,
-      NewImage: mockNewImage,
+      (invalidDDBEvent.Records[0].dynamodb!.NewImage as typeof mockNewImage) = {
+        Id: { N: 101 },
+        Message: { S: 'foo' },
+      };
+      (invalidDDBEvent.Records[1].dynamodb!.NewImage as typeof mockNewImage) =
+        mockNewImage;
+      (invalidDDBEvent.Records[0].dynamodb!.OldImage as typeof mockOldImage) =
+        mockOldImage;
+      (invalidDDBEvent.Records[1].dynamodb!.OldImage as typeof mockOldImage) =
+        mockOldImage;
+
+      const parsed = DynamoDBStreamEnvelope.safeParse(invalidDDBEvent, schema);
+      expect(parsed).toEqual({
+        success: false,
+        error: expect.any(ZodError),
+        originalEvent: invalidDDBEvent,
+      });
+    });
+
+    it('safeParse should return error if OldImage is invalid', () => {
+      const invalidDDBEvent =
+        TestEvents.dynamoStreamEvent as DynamoDBStreamEvent;
+
+      (invalidDDBEvent.Records[0].dynamodb!.OldImage as typeof mockNewImage) = {
+        Id: { N: 101 },
+        Message: { S: 'foo' },
+      };
+      (invalidDDBEvent.Records[1].dynamodb!.NewImage as typeof mockNewImage) =
+        mockNewImage;
+      (invalidDDBEvent.Records[0].dynamodb!.OldImage as typeof mockOldImage) =
+        mockOldImage;
+      (invalidDDBEvent.Records[0].dynamodb!.NewImage as typeof mockNewImage) =
+        mockNewImage;
+
+      const parsed = DynamoDBStreamEnvelope.safeParse(invalidDDBEvent, schema);
+      expect(parsed).toEqual({
+        success: false,
+        error: expect.any(ZodError),
+        originalEvent: invalidDDBEvent,
+      });
     });
-    expect(parsed[1]).toEqual({
-      OldImage: mockOldImage,
-      NewImage: mockNewImage,
+
+    it('safeParse should return error if envelope is invalid', () => {
+      const parsed = DynamoDBStreamEnvelope.safeParse({ foo: 'bar' }, schema);
+      expect(parsed).toEqual({
+        success: false,
+        error: expect.any(ZodError),
+        originalEvent: { foo: 'bar' },
+      });
     });
   });
 });
diff --git a/packages/parser/tests/unit/envelopes/eventbridge.test.ts b/packages/parser/tests/unit/envelopes/eventbridge.test.ts
index 5baaf12f9f..746d95b160 100644
--- a/packages/parser/tests/unit/envelopes/eventbridge.test.ts
+++ b/packages/parser/tests/unit/envelopes/eventbridge.test.ts
@@ -7,45 +7,116 @@
 import { TestEvents, TestSchema } from '../schema/utils.js';
 import { generateMock } from '@anatine/zod-mock';
 import { EventBridgeEvent } from 'aws-lambda';
-import { eventBridgeEnvelope } from '../../../src/envelopes/';
+import { ZodError } from 'zod';
+import { EventBridgeEnvelope } from '../../../src/envelopes/index.js';
 
 describe('EventBridgeEnvelope ', () => {
-  it('should parse eventbridge event', () => {
-    const eventBridgeEvent = TestEvents.eventBridgeEvent as EventBridgeEvent<
-      string,
-      object
-    >;
+  describe('parse', () => {
+    it('should parse eventbridge event', () => {
+      const eventBridgeEvent = TestEvents.eventBridgeEvent as EventBridgeEvent<
+        string,
+        object
+      >;
 
-    const data = generateMock(TestSchema);
+      const data = generateMock(TestSchema);
 
-    eventBridgeEvent.detail = data;
+      eventBridgeEvent.detail = data;
 
-    expect(eventBridgeEnvelope(eventBridgeEvent, TestSchema)).toEqual(data);
-  });
+      expect(EventBridgeEnvelope.parse(eventBridgeEvent, TestSchema)).toEqual(
+        data
+      );
+    });
+
+    it('should throw error if detail type does not match schema', () => {
+      const eventBridgeEvent = TestEvents.eventBridgeEvent as EventBridgeEvent<
+        string,
+        object
+      >;
+
+      eventBridgeEvent.detail = {
+        foo: 'bar',
+      };
 
-  it('should throw error if detail type does not match schema', () => {
-    const eventBridgeEvent = TestEvents.eventBridgeEvent as EventBridgeEvent<
-      string,
-      object
-    >;
+      expect(() =>
+        EventBridgeEnvelope.parse(eventBridgeEvent, TestSchema)
+      ).toThrowError();
+    });
 
-    eventBridgeEvent.detail = {
-      foo: 'bar',
-    };
+    it('should throw when invalid data type provided', () => {
+      const eventBridgeEvent = TestEvents.eventBridgeEvent as EventBridgeEvent<
+        string,
+        object
+      >;
 
-    expect(() =>
-      eventBridgeEnvelope(eventBridgeEvent, TestSchema)
-    ).toThrowError();
+      eventBridgeEvent.detail = 1 as unknown as object;
+
+      expect(() =>
+        EventBridgeEnvelope.parse(eventBridgeEvent, TestSchema)
+      ).toThrow();
+    });
   });
 
-  it('should throw when invalid data type provided', () => {
-    const eventBridgeEvent = TestEvents.eventBridgeEvent as EventBridgeEvent<
-      string,
-      object
-    >;
+  describe('safeParse', () => {
+    it('should safe parse eventbridge event', () => {
+      const eventBridgeEvent = TestEvents.eventBridgeEvent as EventBridgeEvent<
+        string,
+        object
+      >;
+
+      const data = generateMock(TestSchema);
+
+      eventBridgeEvent.detail = data;
+
+      expect(
+        EventBridgeEnvelope.safeParse(eventBridgeEvent, TestSchema)
+      ).toEqual({
+        success: true,
+        data: data,
+      });
+    });
+
+    it('should safe parse eventbridge event and return original event if invalid', () => {
+      const eventBridgeEvent = TestEvents.eventBridgeEvent as EventBridgeEvent<
+        string,
+        object
+      >;
+
+      eventBridgeEvent.detail = {
+        foo: 'bar',
+      };
+
+      expect(
+        EventBridgeEnvelope.safeParse(eventBridgeEvent, TestSchema)
+      ).toEqual({
+        success: false,
+        error: expect.any(ZodError),
+        originalEvent: eventBridgeEvent,
+      });
+    });
+
+    it('should safe parse eventbridge event and return original event if invalid data type', () => {
+      const eventBridgeEvent = TestEvents.eventBridgeEvent as EventBridgeEvent<
+        string,
+        object
+      >;
+
+      eventBridgeEvent.detail = 1 as unknown as object;
 
-    eventBridgeEvent.detail = 1 as unknown as object;
+      expect(
+        EventBridgeEnvelope.safeParse(eventBridgeEvent, TestSchema)
+      ).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: eventBridgeEvent,
+      });
+    });
 
-    expect(() => eventBridgeEnvelope(eventBridgeEvent, TestSchema)).toThrow();
+    it('should return original event and error envelope is invalid', () => {
+      expect(EventBridgeEnvelope.safeParse(1, TestSchema)).toEqual({
+        success: false,
+        error: expect.any(ZodError),
+        originalEvent: 1,
+      });
+    });
   });
 });
diff --git a/packages/parser/tests/unit/envelopes/kafka.test.ts b/packages/parser/tests/unit/envelopes/kafka.test.ts
index 37657bf93a..5c55013244 100644
--- a/packages/parser/tests/unit/envelopes/kafka.test.ts
+++ b/packages/parser/tests/unit/envelopes/kafka.test.ts
@@ -7,33 +7,90 @@
 import { generateMock } from '@anatine/zod-mock';
 import { TestEvents, TestSchema } from '../schema/utils.js';
 import { MSKEvent, SelfManagedKafkaEvent } from 'aws-lambda';
-import { kafkaEnvelope } from '../../../src/envelopes/';
+import { KafkaEnvelope } from '../../../src/envelopes/index.js';
 
 describe('Kafka', () => {
-  it('should parse MSK kafka envelope', () => {
-    const mock = generateMock(TestSchema);
+  describe('parse', () => {
+    it('should parse MSK kafka envelope', () => {
+      const mock = generateMock(TestSchema);
 
-    const kafkaEvent = TestEvents.kafkaEventMsk as MSKEvent;
-    kafkaEvent.records['mytopic-0'][0].value = Buffer.from(
-      JSON.stringify(mock)
-    ).toString('base64');
+      const kafkaEvent = TestEvents.kafkaEventMsk as MSKEvent;
+      kafkaEvent.records['mytopic-0'][0].value = Buffer.from(
+        JSON.stringify(mock)
+      ).toString('base64');
 
-    const result = kafkaEnvelope(kafkaEvent, TestSchema);
+      const result = KafkaEnvelope.parse(kafkaEvent, TestSchema);
 
-    expect(result).toEqual([[mock]]);
-  });
+      expect(result).toEqual([[mock]]);
+    });
+
+    it('should parse Self Managed kafka envelope', () => {
+      const mock = generateMock(TestSchema);
+
+      const kafkaEvent =
+        TestEvents.kafkaEventSelfManaged as SelfManagedKafkaEvent;
+      kafkaEvent.records['mytopic-0'][0].value = Buffer.from(
+        JSON.stringify(mock)
+      ).toString('base64');
+
+      const result = KafkaEnvelope.parse(kafkaEvent, TestSchema);
+
+      expect(result).toEqual([[mock]]);
+    });
+
+    describe('safeParse', () => {
+      it('should parse MSK kafka envelope', () => {
+        const mock = generateMock(TestSchema);
+
+        const kafkaEvent = TestEvents.kafkaEventMsk as MSKEvent;
+        kafkaEvent.records['mytopic-0'][0].value = Buffer.from(
+          JSON.stringify(mock)
+        ).toString('base64');
+
+        const result = KafkaEnvelope.safeParse(kafkaEvent, TestSchema);
+
+        expect(result).toEqual({
+          success: true,
+          data: [mock],
+        });
+      });
+
+      it('should parse Self Managed kafka envelope', () => {
+        const mock = generateMock(TestSchema);
+
+        const kafkaEvent =
+          TestEvents.kafkaEventSelfManaged as SelfManagedKafkaEvent;
+        kafkaEvent.records['mytopic-0'][0].value = Buffer.from(
+          JSON.stringify(mock)
+        ).toString('base64');
+
+        const result = KafkaEnvelope.safeParse(kafkaEvent, TestSchema);
 
-  it('should parse Self Managed kafka envelope', () => {
-    const mock = generateMock(TestSchema);
+        expect(result).toEqual({
+          success: true,
+          data: [mock],
+        });
+      });
 
-    const kafkaEvent =
-      TestEvents.kafkaEventSelfManaged as SelfManagedKafkaEvent;
-    kafkaEvent.records['mytopic-0'][0].value = Buffer.from(
-      JSON.stringify(mock)
-    ).toString('base64');
+      it('should return original event on failure', () => {
+        const kafkaEvent = TestEvents.kafkaEventMsk as MSKEvent;
+        kafkaEvent.records['mytopic-0'][0].value = 'not a valid json';
 
-    const result = kafkaEnvelope(kafkaEvent, TestSchema);
+        const result = KafkaEnvelope.safeParse(kafkaEvent, TestSchema);
 
-    expect(result).toEqual([[mock]]);
+        expect(result).toEqual({
+          success: false,
+          error: expect.any(Error),
+          originalEvent: kafkaEvent,
+        });
+      });
+      it('should return original event and error if envelope is invalid', () => {
+        expect(KafkaEnvelope.safeParse({ foo: 'bar' }, TestSchema)).toEqual({
+          success: false,
+          error: expect.any(Error),
+          originalEvent: { foo: 'bar' },
+        });
+      });
+    });
   });
 });
diff --git a/packages/parser/tests/unit/envelopes/kinesis-firehose.test.ts b/packages/parser/tests/unit/envelopes/kinesis-firehose.test.ts
index 65c7be5fd5..50951d7c0c 100644
--- a/packages/parser/tests/unit/envelopes/kinesis-firehose.test.ts
+++ b/packages/parser/tests/unit/envelopes/kinesis-firehose.test.ts
@@ -8,48 +8,157 @@ import { TestEvents, TestSchema } from '../schema/utils.js';
 import { generateMock } from '@anatine/zod-mock';
 import { KinesisFirehoseSchema } from '../../../src/schemas/';
 import { z } from 'zod';
-import { kinesisFirehoseEnvelope } from '../../../src/envelopes/';
+import { KinesisFirehoseEnvelope } from '../../../src/envelopes/index.js';
 
 describe('Kinesis Firehose Envelope', () => {
-  it('should parse records for PutEvent', () => {
-    const mock = generateMock(TestSchema);
-    const testEvent = TestEvents.kinesisFirehosePutEvent as z.infer<
-      typeof KinesisFirehoseSchema
-    >;
+  describe('parse', () => {
+    it('should parse records for PutEvent', () => {
+      const mock = generateMock(TestSchema);
+      const testEvent = TestEvents.kinesisFirehosePutEvent as z.infer<
+        typeof KinesisFirehoseSchema
+      >;
 
-    testEvent.records.map((record) => {
-      record.data = Buffer.from(JSON.stringify(mock)).toString('base64');
+      testEvent.records.map((record) => {
+        record.data = Buffer.from(JSON.stringify(mock)).toString('base64');
+      });
+
+      const resp = KinesisFirehoseEnvelope.parse(testEvent, TestSchema);
+      expect(resp).toEqual([mock, mock]);
     });
 
-    const resp = kinesisFirehoseEnvelope(testEvent, TestSchema);
-    expect(resp).toEqual([mock, mock]);
-  });
+    it('should parse a single record for SQS event', () => {
+      const mock = generateMock(TestSchema);
+      const testEvent = TestEvents.kinesisFirehoseSQSEvent as z.infer<
+        typeof KinesisFirehoseSchema
+      >;
+
+      testEvent.records.map((record) => {
+        record.data = Buffer.from(JSON.stringify(mock)).toString('base64');
+      });
+
+      const resp = KinesisFirehoseEnvelope.parse(testEvent, TestSchema);
+      expect(resp).toEqual([mock]);
+    });
+
+    it('should parse records for kinesis event', () => {
+      const mock = generateMock(TestSchema);
+      const testEvent = TestEvents.kinesisFirehoseKinesisEvent as z.infer<
+        typeof KinesisFirehoseSchema
+      >;
 
-  it('should parse a single record for SQS event', () => {
-    const mock = generateMock(TestSchema);
-    const testEvent = TestEvents.kinesisFirehoseSQSEvent as z.infer<
-      typeof KinesisFirehoseSchema
-    >;
+      testEvent.records.map((record) => {
+        record.data = Buffer.from(JSON.stringify(mock)).toString('base64');
+      });
 
-    testEvent.records.map((record) => {
-      record.data = Buffer.from(JSON.stringify(mock)).toString('base64');
+      const resp = KinesisFirehoseEnvelope.parse(testEvent, TestSchema);
+      expect(resp).toEqual([mock, mock]);
     });
+    it('should throw if record is not base64 encoded', () => {
+      const testEvent = TestEvents.kinesisFirehosePutEvent as z.infer<
+        typeof KinesisFirehoseSchema
+      >;
 
-    const resp = kinesisFirehoseEnvelope(testEvent, TestSchema);
-    expect(resp).toEqual([mock]);
+      testEvent.records.map((record) => {
+        record.data = 'not base64 encoded';
+      });
+
+      expect(() => {
+        KinesisFirehoseEnvelope.parse(testEvent, TestSchema);
+      }).toThrow();
+    });
+    it('should throw if envelope is invalid', () => {
+      expect(() => {
+        KinesisFirehoseEnvelope.parse({ foo: 'bar' }, TestSchema);
+      }).toThrow();
+    });
+    it('should throw when schema does not match record', () => {
+      const testEvent = TestEvents.kinesisFirehosePutEvent as z.infer<
+        typeof KinesisFirehoseSchema
+      >;
+
+      testEvent.records.map((record) => {
+        record.data = Buffer.from('not a valid json').toString('base64');
+      });
+
+      expect(() => {
+        KinesisFirehoseEnvelope.parse(testEvent, TestSchema);
+      }).toThrow();
+    });
   });
+  describe('safeParse', () => {
+    it('should parse records for PutEvent', () => {
+      const mock = generateMock(TestSchema);
+      const testEvent = TestEvents.kinesisFirehosePutEvent as z.infer<
+        typeof KinesisFirehoseSchema
+      >;
 
-  it('should parse records for kinesis event', () => {
-    const mock = generateMock(TestSchema);
-    const testEvent = TestEvents.kinesisFirehoseKinesisEvent as z.infer<
-      typeof KinesisFirehoseSchema
-    >;
+      testEvent.records.map((record) => {
+        record.data = Buffer.from(JSON.stringify(mock)).toString('base64');
+      });
 
-    testEvent.records.map((record) => {
-      record.data = Buffer.from(JSON.stringify(mock)).toString('base64');
+      const resp = KinesisFirehoseEnvelope.safeParse(testEvent, TestSchema);
+      expect(resp).toEqual({ success: true, data: [mock, mock] });
     });
 
-    const resp = kinesisFirehoseEnvelope(testEvent, TestSchema);
-    expect(resp).toEqual([mock, mock]);
+    it('should parse a single record for SQS event', () => {
+      const mock = generateMock(TestSchema);
+      const testEvent = TestEvents.kinesisFirehoseSQSEvent as z.infer<
+        typeof KinesisFirehoseSchema
+      >;
+
+      testEvent.records.map((record) => {
+        record.data = Buffer.from(JSON.stringify(mock)).toString('base64');
+      });
+
+      const resp = KinesisFirehoseEnvelope.safeParse(testEvent, TestSchema);
+      expect(resp).toEqual({ success: true, data: [mock] });
+    });
+
+    it('should parse records for kinesis event', () => {
+      const mock = generateMock(TestSchema);
+      const testEvent = TestEvents.kinesisFirehoseKinesisEvent as z.infer<
+        typeof KinesisFirehoseSchema
+      >;
+
+      testEvent.records.map((record) => {
+        record.data = Buffer.from(JSON.stringify(mock)).toString('base64');
+      });
+
+      const resp = KinesisFirehoseEnvelope.safeParse(testEvent, TestSchema);
+      expect(resp).toEqual({ success: true, data: [mock, mock] });
+    });
+    it('should return original event if envelope is invalid', () => {
+      expect(
+        KinesisFirehoseEnvelope.safeParse({ foo: 'bar' }, TestSchema)
+      ).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: { foo: 'bar' },
+      });
+    });
+    it('should return original event if record is not base64 encoded', () => {
+      const testEvent = TestEvents.kinesisFirehosePutEvent as z.infer<
+        typeof KinesisFirehoseSchema
+      >;
+
+      testEvent.records.map((record) => {
+        record.data = 'not base64 encoded';
+      });
+
+      expect(KinesisFirehoseEnvelope.safeParse(testEvent, TestSchema)).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: testEvent,
+      });
+    });
+    it('should return original event envelope is invalid', () => {
+      expect(
+        KinesisFirehoseEnvelope.safeParse({ foo: 'bar' }, TestSchema)
+      ).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: { foo: 'bar' },
+      });
+    });
   });
 });
diff --git a/packages/parser/tests/unit/envelopes/kinesis.test.ts b/packages/parser/tests/unit/envelopes/kinesis.test.ts
index 67dfc464eb..f84149c252 100644
--- a/packages/parser/tests/unit/envelopes/kinesis.test.ts
+++ b/packages/parser/tests/unit/envelopes/kinesis.test.ts
@@ -7,20 +7,66 @@
 import { generateMock } from '@anatine/zod-mock';
 import { KinesisStreamEvent } from 'aws-lambda';
 import { TestEvents, TestSchema } from '../schema/utils.js';
-import { kinesisEnvelope } from '../../../src/envelopes/';
+import { KinesisEnvelope } from '../../../src/envelopes/index.js';
+import { ZodError } from 'zod';
 
-describe('Kinesis', () => {
-  it('should parse Kinesis Stream event', () => {
-    const mock = generateMock(TestSchema);
-    const testEvent = TestEvents.kinesisStreamEvent as KinesisStreamEvent;
+describe('KinesisEnvelope', () => {
+  describe('parse', () => {
+    it('should parse Kinesis Stream event', () => {
+      const mock = generateMock(TestSchema);
+      const testEvent = TestEvents.kinesisStreamEvent as KinesisStreamEvent;
 
-    testEvent.Records.map((record) => {
-      record.kinesis.data = Buffer.from(JSON.stringify(mock)).toString(
-        'base64'
-      );
+      testEvent.Records.map((record) => {
+        record.kinesis.data = Buffer.from(JSON.stringify(mock)).toString(
+          'base64'
+        );
+      });
+
+      const resp = KinesisEnvelope.parse(testEvent, TestSchema);
+      expect(resp).toEqual([mock, mock]);
+    });
+    it('should throw if envelope is invalid', () => {
+      expect(() => KinesisEnvelope.parse({ foo: 'bar' }, TestSchema)).toThrow();
+    });
+    it('should throw if record is invalid', () => {
+      const testEvent = TestEvents.kinesisStreamEvent as KinesisStreamEvent;
+      testEvent.Records[0].kinesis.data = 'invalid';
+      expect(() => KinesisEnvelope.parse(testEvent, TestSchema)).toThrow();
     });
+  });
+
+  describe('safeParse', () => {
+    it('should parse Kinesis Stream event', () => {
+      const mock = generateMock(TestSchema);
+      const testEvent = TestEvents.kinesisStreamEvent as KinesisStreamEvent;
 
-    const resp = kinesisEnvelope(testEvent, TestSchema);
-    expect(resp).toEqual([mock, mock]);
+      testEvent.Records.map((record) => {
+        record.kinesis.data = Buffer.from(JSON.stringify(mock)).toString(
+          'base64'
+        );
+      });
+
+      const resp = KinesisEnvelope.safeParse(testEvent, TestSchema);
+      expect(resp).toEqual({ success: true, data: [mock, mock] });
+    });
+    it('should return original event if envelope is invalid', () => {
+      const testEvent = { foo: 'bar' };
+      const resp = KinesisEnvelope.safeParse(testEvent, TestSchema);
+      expect(resp).toEqual({
+        success: false,
+        error: expect.any(ZodError),
+        originalEvent: testEvent,
+      });
+    });
+    it('should return original event if record is invalid', () => {
+      const testEvent = TestEvents.kinesisStreamEvent as KinesisStreamEvent;
+      testEvent.Records[0].kinesis.data = 'invalid';
+      const resp = KinesisEnvelope.safeParse(testEvent, TestSchema);
+      expect(resp).toEqual({
+        success: false,
+        error: expect.any(SyntaxError),
+        originalEvent: testEvent,
+      });
+    });
   });
 });
diff --git a/packages/parser/tests/unit/envelopes/lambda.test.ts b/packages/parser/tests/unit/envelopes/lambda.test.ts
index 1cf6e20cea..56b0551cfd 100644
--- a/packages/parser/tests/unit/envelopes/lambda.test.ts
+++ b/packages/parser/tests/unit/envelopes/lambda.test.ts
@@ -1,30 +1,92 @@
+import { LambdaFunctionUrlEnvelope } from '../../../src/envelopes/index.js';
+import { TestEvents, TestSchema } from '../schema/utils.js';
+import { generateMock } from '@anatine/zod-mock';
+import { APIGatewayProxyEventV2 } from 'aws-lambda';
+
 /**
  * Test built in schema envelopes for Lambda Functions URL
  *
  * @group unit/parser/envelopes
  */
 
-import { TestEvents, TestSchema } from '../schema/utils.js';
-import { generateMock } from '@anatine/zod-mock';
-import { APIGatewayProxyEventV2 } from 'aws-lambda';
-import { lambdaFunctionUrlEnvelope } from '../../../src/envelopes/';
-
 describe('Lambda Functions Url ', () => {
-  it('should parse custom schema in envelope', () => {
-    const testEvent =
-      TestEvents.lambdaFunctionUrlEvent as APIGatewayProxyEventV2;
-    const data = generateMock(TestSchema);
+  describe('parse', () => {
+    it('should parse custom schema in envelope', () => {
+      const testEvent =
+        TestEvents.lambdaFunctionUrlEvent as APIGatewayProxyEventV2;
+      const data = generateMock(TestSchema);
 
-    testEvent.body = JSON.stringify(data);
+      testEvent.body = JSON.stringify(data);
 
-    expect(lambdaFunctionUrlEnvelope(testEvent, TestSchema)).toEqual(data);
+      expect(LambdaFunctionUrlEnvelope.parse(testEvent, TestSchema)).toEqual(
+        data
+      );
+    });
+
+    it('should throw when no body provided', () => {
+      const testEvent =
+        TestEvents.apiGatewayProxyV2Event as APIGatewayProxyEventV2;
+      testEvent.body = undefined;
+
+      expect(() =>
+        LambdaFunctionUrlEnvelope.parse(testEvent, TestSchema)
+      ).toThrow();
+    });
+
+    it('should throw when envelope is not valid', () => {
+      expect(() =>
+        LambdaFunctionUrlEnvelope.parse({ foo: 'bar' }, TestSchema)
+      ).toThrow();
+    });
+
+    it('should throw when body does not match schema', () => {
+      const testEvent =
+        TestEvents.lambdaFunctionUrlEvent as APIGatewayProxyEventV2;
+      testEvent.body = JSON.stringify({ foo: 'bar' });
+
+      expect(() =>
+        LambdaFunctionUrlEnvelope.parse(testEvent, TestSchema)
+      ).toThrow();
+    });
   });
+  describe('safeParse', () => {
+    it('should parse custom schema in envelope', () => {
+      const testEvent =
+        TestEvents.lambdaFunctionUrlEvent as APIGatewayProxyEventV2;
+      const data = generateMock(TestSchema);
+
+      testEvent.body = JSON.stringify(data);
+
+      expect(
+        LambdaFunctionUrlEnvelope.safeParse(testEvent, TestSchema)
+      ).toEqual({
+        success: true,
+        data,
+      });
+    });
+
+    it('should return original event when envelope is not valid', () => {
+      expect(
+        LambdaFunctionUrlEnvelope.safeParse({ foo: 'bar' }, TestSchema)
+      ).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: { foo: 'bar' },
+      });
+    });
 
-  it('should throw when no body provided', () => {
-    const testEvent =
-      TestEvents.apiGatewayProxyV2Event as APIGatewayProxyEventV2;
-    testEvent.body = undefined;
+    it('should return original event when body does not match schema', () => {
+      const testEvent =
+        TestEvents.lambdaFunctionUrlEvent as APIGatewayProxyEventV2;
+      testEvent.body = JSON.stringify({ foo: 'bar' });
 
-    expect(() => lambdaFunctionUrlEnvelope(testEvent, TestSchema)).toThrow();
+      expect(
+        LambdaFunctionUrlEnvelope.safeParse(testEvent, TestSchema)
+      ).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: testEvent,
+      });
+    });
   });
 });
diff --git a/packages/parser/tests/unit/envelopes/sns.test.ts b/packages/parser/tests/unit/envelopes/sns.test.ts
index 0bf36c94c0..c1e9b67bee 100644
--- a/packages/parser/tests/unit/envelopes/sns.test.ts
+++ b/packages/parser/tests/unit/envelopes/sns.test.ts
@@ -4,48 +4,160 @@
  * @group unit/parser/envelopes
  */
 
-import { z } from 'zod';
+import { z, ZodError } from 'zod';
 import { generateMock } from '@anatine/zod-mock';
 import { SNSEvent, SQSEvent } from 'aws-lambda';
 import { TestEvents, TestSchema } from '../schema/utils.js';
-import { snsEnvelope, snsSqsEnvelope } from '../../../src/envelopes/';
+import { SnsEnvelope, SnsSqsEnvelope } from '../../../src/envelopes/index.js';
 
-describe('SNS Envelope', () => {
-  it('should parse custom schema in envelope', () => {
-    const testEvent = TestEvents.snsEvent as SNSEvent;
+describe('Sns and SQS Envelope', () => {
+  describe('SnsSqsEnvelope parse', () => {
+    it('should parse sqs inside sns envelope', () => {
+      const snsSqsTestEvent = TestEvents.snsSqsEvent as SQSEvent;
 
-    const testRecords = [] as z.infer<typeof TestSchema>[];
+      const data = generateMock(TestSchema);
+      const snsEvent = JSON.parse(snsSqsTestEvent.Records[0].body);
+      snsEvent.Message = JSON.stringify(data);
 
-    testEvent.Records.map((record) => {
-      const value = generateMock(TestSchema);
-      testRecords.push(value);
-      record.Sns.Message = JSON.stringify(value);
-    });
+      snsSqsTestEvent.Records[0].body = JSON.stringify(snsEvent);
 
-    expect(snsEnvelope(testEvent, TestSchema)).toEqual(testRecords);
+      expect(SnsSqsEnvelope.parse(snsSqsTestEvent, TestSchema)).toEqual([data]);
+    });
   });
+  describe('SnsSqsEnvelope safeParse', () => {
+    it('should parse sqs inside sns envelope', () => {
+      const snsSqsTestEvent = TestEvents.snsSqsEvent as SQSEvent;
+
+      const data = generateMock(TestSchema);
+      const snsEvent = JSON.parse(snsSqsTestEvent.Records[0].body);
+      snsEvent.Message = JSON.stringify(data);
+
+      snsSqsTestEvent.Records[0].body = JSON.stringify(snsEvent);
+
+      expect(SnsSqsEnvelope.safeParse(snsSqsTestEvent, TestSchema)).toEqual({
+        success: true,
+        data: [data],
+      });
+    });
+    it('should return error when envelope is not valid', () => {
+      expect(SnsSqsEnvelope.safeParse({ foo: 'bar' }, TestSchema)).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: { foo: 'bar' },
+      });
+    });
+    it('should return error if message does not match schema', () => {
+      const snsSqsTestEvent = TestEvents.snsSqsEvent as SQSEvent;
+
+      const snsEvent = JSON.parse(snsSqsTestEvent.Records[0].body);
+      snsEvent.Message = JSON.stringify({
+        foo: 'bar',
+      });
+
+      snsSqsTestEvent.Records[0].body = JSON.stringify(snsEvent);
 
-  it('should throw if message does not macht schema', () => {
-    const testEvent = TestEvents.snsEvent as SNSEvent;
+      expect(SnsSqsEnvelope.safeParse(snsSqsTestEvent, TestSchema)).toEqual({
+        success: false,
+        error: expect.any(ZodError),
+        originalEvent: snsSqsTestEvent,
+      });
+    });
+    it('should return error if sns message is not valid', () => {
+      const snsSqsTestEvent = TestEvents.snsSqsEvent as SQSEvent;
 
-    testEvent.Records.map((record) => {
-      record.Sns.Message = JSON.stringify({
+      snsSqsTestEvent.Records[0].body = JSON.stringify({
         foo: 'bar',
       });
+
+      expect(SnsSqsEnvelope.safeParse(snsSqsTestEvent, TestSchema)).toEqual({
+        success: false,
+        error: expect.any(ZodError),
+        originalEvent: snsSqsTestEvent,
+      });
     });
+    it('should return error if JSON parse fails for record.body', () => {
+      const snsSqsTestEvent = TestEvents.snsSqsEvent as SQSEvent;
 
-    expect(() => snsEnvelope(testEvent, TestSchema)).toThrowError();
+      snsSqsTestEvent.Records[0].body = 'not a json string';
+
+      expect(SnsSqsEnvelope.safeParse(snsSqsTestEvent, TestSchema)).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: snsSqsTestEvent,
+      });
+    });
   });
+});
+describe('SnsEnvelope', () => {
+  describe('parse', () => {
+    it('should parse custom schema in envelope', () => {
+      const testEvent = TestEvents.snsEvent as SNSEvent;
 
-  it('should parse sqs inside sns envelope', () => {
-    const snsSqsTestEvent = TestEvents.snsSqsEvent as SQSEvent;
+      const testRecords = [] as z.infer<typeof TestSchema>[];
 
-    const data = generateMock(TestSchema);
-    const snsEvent = JSON.parse(snsSqsTestEvent.Records[0].body);
-    snsEvent.Message = JSON.stringify(data);
+      testEvent.Records.map((record) => {
+        const value = generateMock(TestSchema);
+        testRecords.push(value);
+        record.Sns.Message = JSON.stringify(value);
+      });
 
-    snsSqsTestEvent.Records[0].body = JSON.stringify(snsEvent);
+      expect(SnsEnvelope.parse(testEvent, TestSchema)).toEqual(testRecords);
+    });
+
+    it('should throw if message does not macht schema', () => {
+      const testEvent = TestEvents.snsEvent as SNSEvent;
+
+      testEvent.Records.map((record) => {
+        record.Sns.Message = JSON.stringify({
+          foo: 'bar',
+        });
+      });
+
+      expect(() => SnsEnvelope.parse(testEvent, TestSchema)).toThrow();
+    });
+    it('should throw if envelope is not valid', () => {
+      expect(() => SnsEnvelope.parse({ foo: 'bar' }, TestSchema)).toThrow();
+    });
+  });
+  describe('safeParse', () => {
+    it('should parse custom schema in envelope', () => {
+      const testEvent = TestEvents.snsEvent as SNSEvent;
+
+      const testRecords = [] as z.infer<typeof TestSchema>[];
+
+      testEvent.Records.map((record) => {
+        const value = generateMock(TestSchema);
+        testRecords.push(value);
+        record.Sns.Message = JSON.stringify(value);
+      });
 
-    expect(snsSqsEnvelope(snsSqsTestEvent, TestSchema)).toEqual([data]);
+      expect(SnsEnvelope.safeParse(testEvent, TestSchema)).toEqual({
+        success: true,
+        data: testRecords,
+      });
+    });
+
+    it('should return error when message does not macht schema', () => {
+      const testEvent = TestEvents.snsEvent as SNSEvent;
+
+      testEvent.Records.map((record) => {
+        record.Sns.Message = JSON.stringify({
+          foo: 'bar',
+        });
+      });
+
+      expect(SnsEnvelope.safeParse(testEvent, TestSchema)).toEqual({
+        success: false,
+        error: expect.any(ZodError),
+        originalEvent: testEvent,
+      });
+    });
+    it('should return error when envelope is not valid', () => {
+      expect(SnsEnvelope.safeParse({ foo: 'bar' }, TestSchema)).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: { foo: 'bar' },
+      });
+    });
   });
 });
diff --git a/packages/parser/tests/unit/envelopes/sqs.test.ts b/packages/parser/tests/unit/envelopes/sqs.test.ts
index 7725e1a992..4d1ab3a419 100644
--- a/packages/parser/tests/unit/envelopes/sqs.test.ts
+++ b/packages/parser/tests/unit/envelopes/sqs.test.ts
@@ -7,39 +7,64 @@
 import { generateMock } from '@anatine/zod-mock';
 import { TestEvents, TestSchema } from '../schema/utils.js';
 import { SQSEvent } from 'aws-lambda';
-import { sqsEnvelope } from '../../../src/envelopes/';
+import { SqsEnvelope } from '../../../src/envelopes/sqs.js';
+import { ZodError } from 'zod';
 
 describe('SqsEnvelope ', () => {
-  it('should parse custom schema in envelope', () => {
-    const mock = generateMock(TestSchema);
+  describe('parse', () => {
+    it('should parse custom schema in envelope', () => {
+      const mock = generateMock(TestSchema);
 
-    const sqsEvent = TestEvents.sqsEvent as SQSEvent;
-    sqsEvent.Records[0].body = JSON.stringify(mock);
-    sqsEvent.Records[1].body = JSON.stringify(mock);
+      const sqsEvent = TestEvents.sqsEvent as SQSEvent;
+      sqsEvent.Records[0].body = JSON.stringify(mock);
+      sqsEvent.Records[1].body = JSON.stringify(mock);
 
-    const resp = sqsEnvelope(sqsEvent, TestSchema);
-    expect(resp).toEqual([mock, mock]);
-  });
+      const resp = SqsEnvelope.parse(sqsEvent, TestSchema);
+      expect(resp).toEqual([mock, mock]);
+    });
+
+    it('should throw error if invalid keys for a schema', () => {
+      expect(() => {
+        SqsEnvelope.parse({ Records: [{ foo: 'bar' }] }, TestSchema);
+      }).toThrow();
+    });
 
-  it('should throw error if invalid keys for a schema', () => {
-    expect(() => {
-      sqsEnvelope({ Records: [{ foo: 'bar' }] }, TestSchema);
-    }).toThrow();
+    it('should throw if invalid envelope', () => {
+      expect(() => {
+        SqsEnvelope.parse({ foo: 'bar' }, TestSchema);
+      }).toThrow();
+    });
   });
+  describe('safeParse', () => {
+    it('should parse custom schema in envelope', () => {
+      const mock = generateMock(TestSchema);
+
+      const sqsEvent = TestEvents.sqsEvent as SQSEvent;
+      sqsEvent.Records[0].body = JSON.stringify(mock);
+      sqsEvent.Records[1].body = JSON.stringify(mock);
+
+      expect(SqsEnvelope.safeParse(sqsEvent, TestSchema)).toEqual({
+        success: true,
+        data: [mock, mock],
+      });
+    });
+
+    it('should return error if event does not match schema', () => {
+      const sqsEvent = TestEvents.sqsEvent as SQSEvent;
+      sqsEvent.Records[0].body = JSON.stringify({ foo: 'bar' });
+      expect(SqsEnvelope.safeParse(sqsEvent, TestSchema)).toEqual({
+        success: false,
+        error: expect.any(ZodError),
+        originalEvent: sqsEvent,
+      });
+    });
 
-  it('should throw error if invalid values for a schema', () => {
-    expect(() => {
-      sqsEnvelope(
-        {
-          Records: [
-            {
-              name: 'foo',
-              age: 17,
-            },
-          ],
-        },
-        TestSchema
-      );
-    }).toThrow();
+    it('should return error if envelope is invalid', () => {
+      expect(SqsEnvelope.safeParse({ foo: 'bar' }, TestSchema)).toEqual({
+        success: false,
+        error: expect.any(ZodError),
+        originalEvent: { foo: 'bar' },
+      });
+    });
   });
 });
diff --git a/packages/parser/tests/unit/envelopes/vpc-lattice.test.ts b/packages/parser/tests/unit/envelopes/vpc-lattice.test.ts
index 7ce781100e..a4282064d2 100644
--- a/packages/parser/tests/unit/envelopes/vpc-lattice.test.ts
+++ b/packages/parser/tests/unit/envelopes/vpc-lattice.test.ts
@@ -6,33 +6,92 @@
 
 import { generateMock } from '@anatine/zod-mock';
 import { TestEvents, TestSchema } from '../schema/utils.js';
-import { VpcLatticeSchema } from '../../../src/schemas/';
-import { z } from 'zod';
-import { vpcLatticeEnvelope } from '../../../src/envelopes/';
+import { VpcLatticeEnvelope } from '../../../src/envelopes/index.js';
+import { VpcLatticeEvent } from '../../../src/types/index.js';
 
-describe('VPC Lattice envelope', () => {
-  it('should parse VPC Lattice event', () => {
-    const mock = generateMock(TestSchema);
-    const testEvent = TestEvents.vpcLatticeEvent as z.infer<
-      typeof VpcLatticeSchema
-    >;
+describe('VpcLatticeEnvelope', () => {
+  describe('parse', () => {
+    it('should parse VPC Lattice event', () => {
+      const mock = generateMock(TestSchema);
+      const testEvent = TestEvents.vpcLatticeEvent as VpcLatticeEvent;
 
-    testEvent.body = JSON.stringify(mock);
+      testEvent.body = JSON.stringify(mock);
 
-    const resp = vpcLatticeEnvelope(testEvent, TestSchema);
+      const resp = VpcLatticeEnvelope.parse(testEvent, TestSchema);
 
-    expect(resp).toEqual(mock);
+      expect(resp).toEqual(mock);
+    });
+
+    it('should parse VPC Lattice event with trailing slash', () => {
+      const mock = generateMock(TestSchema);
+      const testEvent =
+        TestEvents.vpcLatticeEventPathTrailingSlash as VpcLatticeEvent;
+
+      testEvent.body = JSON.stringify(mock);
+
+      const resp = VpcLatticeEnvelope.parse(testEvent, TestSchema);
+      expect(resp).toEqual(mock);
+    });
+
+    it('should throw if event is not a VPC Lattice event', () => {
+      expect(() =>
+        VpcLatticeEnvelope.parse({ foo: 'bar' }, TestSchema)
+      ).toThrow();
+    });
+
+    it('should throw if body does not match schema', () => {
+      const testEvent = TestEvents.vpcLatticeEvent as VpcLatticeEvent;
+
+      testEvent.body = JSON.stringify({ foo: 'bar' });
+
+      expect(() => VpcLatticeEnvelope.parse(testEvent, TestSchema)).toThrow();
+    });
   });
 
-  it('should parse VPC Lattice event with trailing slash', () => {
-    const mock = generateMock(TestSchema);
-    const testEvent = TestEvents.vpcLatticeEventPathTrailingSlash as z.infer<
-      typeof VpcLatticeSchema
-    >;
+  describe('safeParse', () => {
+    it('should parse VPC Lattice event', () => {
+      const mock = generateMock(TestSchema);
+      const testEvent = TestEvents.vpcLatticeEvent as VpcLatticeEvent;
+
+      testEvent.body = JSON.stringify(mock);
+
+      const resp = VpcLatticeEnvelope.safeParse(testEvent, TestSchema);
+
+      expect(resp).toEqual({ success: true, data: mock });
+    });
+
+    it('should parse VPC Lattice event with trailing slash', () => {
+      const mock = generateMock(TestSchema);
+      const testEvent =
+        TestEvents.vpcLatticeEventPathTrailingSlash as VpcLatticeEvent;
+
+      testEvent.body = JSON.stringify(mock);
+
+      const resp = VpcLatticeEnvelope.safeParse(testEvent, TestSchema);
+      expect(resp).toEqual({ success: true, data: mock });
+    });
+
+    it('should return error if event is not a VPC Lattice event', () => {
+      const resp = VpcLatticeEnvelope.safeParse({ foo: 'bar' }, TestSchema);
+
+      expect(resp).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: { foo: 'bar' },
+      });
+    });
+
+    it('should return error if body does not match schema', () => {
+      const testEvent = TestEvents.vpcLatticeEvent as VpcLatticeEvent;
 
-    testEvent.body = JSON.stringify(mock);
+      testEvent.body = JSON.stringify({ foo: 'bar' });
 
-    const resp = vpcLatticeEnvelope(testEvent, TestSchema);
-    expect(resp).toEqual(mock);
+      const resp = VpcLatticeEnvelope.safeParse(testEvent, TestSchema);
+      expect(resp).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: testEvent,
+      });
+    });
   });
 });
diff --git a/packages/parser/tests/unit/envelopes/vpc-latticev2.test.ts b/packages/parser/tests/unit/envelopes/vpc-latticev2.test.ts
index 251382fb87..6f615178ec 100644
--- a/packages/parser/tests/unit/envelopes/vpc-latticev2.test.ts
+++ b/packages/parser/tests/unit/envelopes/vpc-latticev2.test.ts
@@ -5,34 +5,93 @@
  */
 
 import { generateMock } from '@anatine/zod-mock';
-import { VpcLatticeSchema } from '../../../src/schemas/';
-import { z } from 'zod';
 import { TestEvents, TestSchema } from '../schema/utils.js';
-import { vpcLatticeV2Envelope } from '../../../src/envelopes/';
+import { VpcLatticeV2Envelope } from '../../../src/envelopes/index.js';
+import { VpcLatticeEventV2 } from '../../../src/types/index.js';
 
-describe('VPC Lattice envelope', () => {
-  it('should parse VPC Lattice event', () => {
-    const mock = generateMock(TestSchema);
-    const testEvent = TestEvents.vpcLatticeV2Event as z.infer<
-      typeof VpcLatticeSchema
-    >;
+describe('VpcLatticeV2Envelope2', () => {
+  describe('parse', () => {
+    it('should parse VPC Lattice event', () => {
+      const mock = generateMock(TestSchema);
+      const testEvent = TestEvents.vpcLatticeV2Event as VpcLatticeEventV2;
 
-    testEvent.body = JSON.stringify(mock);
+      testEvent.body = JSON.stringify(mock);
 
-    const resp = vpcLatticeV2Envelope(testEvent, TestSchema);
+      const resp = VpcLatticeV2Envelope.parse(testEvent, TestSchema);
 
-    expect(resp).toEqual(mock);
+      expect(resp).toEqual(mock);
+    });
+
+    it('should parse VPC Lattice event with trailing slash', () => {
+      const mock = generateMock(TestSchema);
+      const testEvent =
+        TestEvents.vpcLatticeEventV2PathTrailingSlash as VpcLatticeEventV2;
+
+      testEvent.body = JSON.stringify(mock);
+
+      const resp = VpcLatticeV2Envelope.parse(testEvent, TestSchema);
+      expect(resp).toEqual(mock);
+    });
+
+    it('should throw if event is not a VPC Lattice event', () => {
+      expect(() =>
+        VpcLatticeV2Envelope.parse({ foo: 'bar' }, TestSchema)
+      ).toThrow();
+    });
+
+    it('should throw if body does not match schema', () => {
+      const testEvent = TestEvents.vpcLatticeV2Event as VpcLatticeEventV2;
+
+      testEvent.body = JSON.stringify({ foo: 'bar' });
+
+      expect(() => VpcLatticeV2Envelope.parse(testEvent, TestSchema)).toThrow();
+    });
   });
 
-  it('should parse VPC Lattice event with trailing slash', () => {
-    const mock = generateMock(TestSchema);
-    const testEvent = TestEvents.vpcLatticeEventV2PathTrailingSlash as z.infer<
-      typeof VpcLatticeSchema
-    >;
+  describe('safeParse', () => {
+    it('should parse VPC Lattice event', () => {
+      const mock = generateMock(TestSchema);
+      const testEvent = TestEvents.vpcLatticeV2Event as VpcLatticeEventV2;
+
+      testEvent.body = JSON.stringify(mock);
+
+      const resp = VpcLatticeV2Envelope.safeParse(testEvent, TestSchema);
+
+      expect(resp).toEqual({ success: true, data: mock });
+    });
+
+    it('should parse VPC Lattice event with trailing slash', () => {
+      const mock = generateMock(TestSchema);
+      const testEvent =
+        TestEvents.vpcLatticeEventV2PathTrailingSlash as VpcLatticeEventV2;
+
+      testEvent.body = JSON.stringify(mock);
+
+      const resp = VpcLatticeV2Envelope.safeParse(testEvent, TestSchema);
+      expect(resp).toEqual({ success: true, data: mock });
+    });
+
+    it('should return error if event is not a VPC Lattice event', () => {
+      const resp = VpcLatticeV2Envelope.safeParse({ foo: 'bar' }, TestSchema);
+
+      expect(resp).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: { foo: 'bar' },
+      });
+    });
+
+    it('should return error if body does not match schema', () => {
+      const testEvent = TestEvents.vpcLatticeV2Event as VpcLatticeEventV2;
 
-    testEvent.body = JSON.stringify(mock);
+      testEvent.body = JSON.stringify({ foo: 'bar' });
 
-    const resp = vpcLatticeV2Envelope(testEvent, TestSchema);
-    expect(resp).toEqual(mock);
+      const resp = VpcLatticeV2Envelope.safeParse(testEvent, TestSchema);
+      expect(resp).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: testEvent,
+      });
+    });
   });
 });
diff --git a/packages/parser/tests/unit/parser.decorator.test.ts b/packages/parser/tests/unit/parser.decorator.test.ts
index 11df5d6703..1e05efa510 100644
--- a/packages/parser/tests/unit/parser.decorator.test.ts
+++ b/packages/parser/tests/unit/parser.decorator.test.ts
@@ -5,25 +5,26 @@
  */
 
 import type { LambdaInterface } from '@aws-lambda-powertools/commons/lib/esm/types';
-import { Context, EventBridgeEvent } from 'aws-lambda';
+import { Context } from 'aws-lambda';
 import { parser } from '../../src/index.js';
 import { TestSchema, TestEvents } from './schema/utils';
 import { generateMock } from '@anatine/zod-mock';
-import { eventBridgeEnvelope } from '../../src/envelopes/index.js';
 import { EventBridgeSchema } from '../../src/schemas/index.js';
-import { z } from 'zod';
+import { z, ZodError } from 'zod';
+import { ParsedResult, EventBridgeEvent } from '../../src/types';
+import { EventBridgeEnvelope } from '../../src/envelopes/index.js';
 
 describe('Parser Decorator', () => {
   const customEventBridgeSchema = EventBridgeSchema.extend({
     detail: TestSchema,
   });
 
-  type TestSchema = z.infer<typeof TestSchema>;
+  type TestEvent = z.infer<typeof TestSchema>;
 
   class TestClass implements LambdaInterface {
     @parser({ schema: TestSchema })
     public async handler(
-      event: TestSchema,
+      event: TestEvent,
       _context: Context
     ): Promise<unknown> {
       return event;
@@ -37,23 +38,46 @@ describe('Parser Decorator', () => {
       return event;
     }
 
-    @parser({ schema: TestSchema, envelope: eventBridgeEnvelope })
+    @parser({ schema: TestSchema, envelope: EventBridgeEnvelope })
     public async handlerWithParserCallsAnotherMethod(
-      event: unknown,
+      event: TestEvent,
       _context: Context
     ): Promise<unknown> {
-      return this.anotherMethod(event as TestSchema);
+      return this.anotherMethod(event);
     }
 
-    @parser({ envelope: eventBridgeEnvelope, schema: TestSchema })
+    @parser({ schema: TestSchema, envelope: EventBridgeEnvelope })
     public async handlerWithSchemaAndEnvelope(
-      event: unknown,
+      event: TestEvent,
       _context: Context
     ): Promise<unknown> {
       return event;
     }
 
-    private async anotherMethod(event: TestSchema): Promise<TestSchema> {
+    @parser({
+      schema: TestSchema,
+      safeParse: true,
+    })
+    public async handlerWithSchemaAndSafeParse(
+      event: ParsedResult<TestEvent, TestEvent>,
+      _context: Context
+    ): Promise<ParsedResult> {
+      return event;
+    }
+
+    @parser({
+      schema: TestSchema,
+      envelope: EventBridgeEnvelope,
+      safeParse: true,
+    })
+    public async harndlerWithEnvelopeAndSafeParse(
+      event: ParsedResult<TestEvent, TestEvent>,
+      _context: Context
+    ): Promise<ParsedResult> {
+      return event;
+    }
+
+    private async anotherMethod(event: TestEvent): Promise<TestEvent> {
       return event;
     }
   }
@@ -70,13 +94,12 @@ describe('Parser Decorator', () => {
 
   it('should parse custom schema with envelope event', async () => {
     const customPayload = generateMock(TestSchema);
-    const testEvent = TestEvents.eventBridgeEvent as EventBridgeEvent<
-      string,
-      unknown
-    >;
+    const testEvent = TestEvents.eventBridgeEvent as EventBridgeEvent;
     testEvent.detail = customPayload;
 
     const resp = await lambda.handlerWithSchemaAndEnvelope(
+      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+      // @ts-ignore
       testEvent,
       {} as Context
     );
@@ -102,17 +125,77 @@ describe('Parser Decorator', () => {
 
   it('should parse and call private async method', async () => {
     const customPayload = generateMock(TestSchema);
-    const testEvent = TestEvents.eventBridgeEvent as EventBridgeEvent<
-      string,
-      unknown
-    >;
+    const testEvent = TestEvents.eventBridgeEvent as EventBridgeEvent;
     testEvent.detail = customPayload;
 
     const resp = await lambda.handlerWithParserCallsAnotherMethod(
+      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+      // @ts-ignore
       testEvent,
       {} as Context
     );
 
     expect(resp).toEqual(customPayload);
   });
+
+  it('should parse event with schema and safeParse', async () => {
+    const testEvent = generateMock(TestSchema);
+
+    const resp = await lambda.handlerWithSchemaAndSafeParse(
+      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+      // @ts-ignore
+      testEvent,
+      {} as Context
+    );
+
+    expect(resp).toEqual({
+      success: true,
+      data: testEvent,
+    });
+  });
+
+  it('should parse event with schema and safeParse and return error', async () => {
+    expect(
+      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+      // @ts-ignore
+      await lambda.handlerWithSchemaAndSafeParse({ foo: 'bar' }, {} as Context)
+    ).toEqual({
+      error: expect.any(ZodError),
+      success: false,
+      originalEvent: { foo: 'bar' },
+    });
+  });
+
+  it('should parse event with envelope and safeParse', async () => {
+    const testEvent = generateMock(TestSchema);
+    const event = TestEvents.eventBridgeEvent as EventBridgeEvent;
+    event.detail = testEvent;
+
+    const resp = await lambda.harndlerWithEnvelopeAndSafeParse(
+      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+      // @ts-ignore
+      event,
+      {} as Context
+    );
+
+    expect(resp).toEqual({
+      success: true,
+      data: testEvent,
+    });
+  });
+
+  it('should parse event with envelope and safeParse and return error', async () => {
+    expect(
+      await lambda.harndlerWithEnvelopeAndSafeParse(
+        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+        // @ts-ignore
+        { foo: 'bar' },
+        {} as Context
+      )
+    ).toEqual({
+      error: expect.any(ZodError),
+      success: false,
+      originalEvent: { foo: 'bar' },
+    });
+  });
 });
diff --git a/packages/parser/tests/unit/parser.middy.test.ts b/packages/parser/tests/unit/parser.middy.test.ts
index 369a04d776..d8dd4ae1b1 100644
--- a/packages/parser/tests/unit/parser.middy.test.ts
+++ b/packages/parser/tests/unit/parser.middy.test.ts
@@ -10,8 +10,9 @@ import { parser } from '../../src/middleware/parser.js';
 import { generateMock } from '@anatine/zod-mock';
 import { SqsSchema } from '../../src/schemas/index.js';
 import { z, type ZodSchema } from 'zod';
-import { sqsEnvelope } from '../../src/envelopes/index.js';
-import { TestSchema } from './schema/utils';
+import { SqsEnvelope, EventBridgeEnvelope } from '../../src/envelopes/index.js';
+import { TestSchema, TestEvents } from './schema/utils';
+import { EventBridgeEvent } from '../../src/types/index.js';
 
 describe('Middleware: parser', () => {
   type schema = z.infer<typeof TestSchema>;
@@ -23,13 +24,13 @@ describe('Middleware: parser', () => {
   };
 
   describe(' when envelope is provided ', () => {
-    const middyfiedHandler = middy(handler).use(
-      parser({ schema: TestSchema, envelope: sqsEnvelope })
+    const middyfiedHandlerSchemaEnvelope = middy(handler).use(
+      parser({ schema: TestSchema, envelope: SqsEnvelope })
     );
 
     it('should parse request body with schema and envelope', async () => {
       const bodyMock = generateMock(TestSchema);
-      parser({ schema: TestSchema, envelope: sqsEnvelope });
+      parser({ schema: TestSchema, envelope: SqsEnvelope });
 
       const event = generateMock(SqsSchema, {
         stringMap: {
@@ -37,7 +38,10 @@ describe('Middleware: parser', () => {
         },
       });
 
-      const result = (await middyfiedHandler(event, {} as Context)) as schema[];
+      const result = (await middyfiedHandlerSchemaEnvelope(
+        event,
+        {} as Context
+      )) as schema[];
       result.forEach((item) => {
         expect(item).toEqual(bodyMock);
       });
@@ -45,8 +49,11 @@ describe('Middleware: parser', () => {
 
     it('should throw when envelope does not match', async () => {
       await expect(async () => {
-        await middyfiedHandler({ name: 'John', age: 18 }, {} as Context);
-      }).rejects.toThrowError();
+        await middyfiedHandlerSchemaEnvelope(
+          { name: 'John', age: 18 },
+          {} as Context
+        );
+      }).rejects.toThrow();
     });
 
     it('should throw when schema does not match', async () => {
@@ -56,14 +63,16 @@ describe('Middleware: parser', () => {
         },
       });
 
-      await expect(middyfiedHandler(event, {} as Context)).rejects.toThrow();
+      await expect(
+        middyfiedHandlerSchemaEnvelope(event, {} as Context)
+      ).rejects.toThrow();
     });
     it('should throw when provided schema is invalid', async () => {
       const middyfiedHandler = middy(handler).use(
-        parser({ schema: {} as ZodSchema, envelope: sqsEnvelope })
+        parser({ schema: {} as ZodSchema, envelope: SqsEnvelope })
       );
 
-      await expect(middyfiedHandler(42, {} as Context)).rejects.toThrowError();
+      await expect(middyfiedHandler(42, {} as Context)).rejects.toThrow();
     });
     it('should throw when envelope is correct but schema is invalid', async () => {
       const event = generateMock(SqsSchema, {
@@ -73,12 +82,10 @@ describe('Middleware: parser', () => {
       });
 
       const middyfiedHandler = middy(handler).use(
-        parser({ schema: {} as ZodSchema, envelope: sqsEnvelope })
+        parser({ schema: {} as ZodSchema, envelope: SqsEnvelope })
       );
 
-      await expect(
-        middyfiedHandler(event, {} as Context)
-      ).rejects.toThrowError();
+      await expect(middyfiedHandler(event, {} as Context)).rejects.toThrow();
     });
   });
 
@@ -117,7 +124,69 @@ describe('Middleware: parser', () => {
 
       await expect(
         middyfiedHandler({ foo: 'bar' }, {} as Context)
-      ).rejects.toThrowError();
+      ).rejects.toThrow();
+    });
+
+    it('should return the event when safeParse is true', async () => {
+      const event = { name: 'John', age: 18 };
+      const middyfiedHandler = middy(handler).use(
+        parser({ schema: TestSchema, safeParse: true })
+      );
+
+      expect(await middyfiedHandler(event, {} as Context)).toEqual({
+        success: true,
+        data: event,
+      });
+    });
+
+    it('should return error when safeParse is true and schema does not match', async () => {
+      const middyfiedHandler = middy(handler).use(
+        parser({ schema: TestSchema, safeParse: true })
+      );
+
+      expect(await middyfiedHandler(42, {} as Context)).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: 42,
+      });
+    });
+
+    it('should return event when envelope and safeParse are true', async () => {
+      const detail = generateMock(TestSchema);
+      const event = TestEvents.eventBridgeEvent as EventBridgeEvent;
+
+      event.detail = detail;
+
+      const middyfiedHandler = middy(handler).use(
+        parser({
+          schema: TestSchema,
+          envelope: EventBridgeEnvelope,
+          safeParse: true,
+        })
+      );
+
+      expect(await middyfiedHandler(event, {} as Context)).toEqual({
+        success: true,
+        data: detail,
+      });
+    });
+
+    it('should return error when envelope and safeParse are true and schema does not match', async () => {
+      const event = TestEvents.eventBridgeEvent as EventBridgeEvent;
+
+      const middyfiedHandler = middy(handler).use(
+        parser({
+          schema: TestSchema,
+          envelope: EventBridgeEnvelope,
+          safeParse: true,
+        })
+      );
+
+      expect(await middyfiedHandler(event, {} as Context)).toEqual({
+        success: false,
+        error: expect.any(Error),
+        originalEvent: event,
+      });
     });
   });
 });