diff --git a/src/constants.ts b/src/constants.ts
index 4218714ca..e52868f98 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -14,6 +14,7 @@ export const xParserOriginalTraits = 'x-parser-original-traits';
 
 export const xParserCircular = 'x-parser-circular';
 export const xParserCircularProps = 'x-parser-circular-props';
+export const xParserObjectUniqueId = 'x-parser-unique-object-id';
 
 export const EXTENSION_REGEX = /^x-[\w\d.\-_]+$/;
 
diff --git a/src/custom-operations/apply-traits.ts b/src/custom-operations/apply-traits.ts
index 990aca601..6d99453ff 100644
--- a/src/custom-operations/apply-traits.ts
+++ b/src/custom-operations/apply-traits.ts
@@ -51,12 +51,17 @@ function applyTraitsToObjectV2(value: Record<string, unknown>) {
 const v3TraitPaths = [
   // operations
   '$.operations.*',
+  '$.operations.*.channel.*',
+  '$.operations.*.channel.messages.*',
+  '$.operations.*.messages.*',
   '$.components.operations.*',
-  // messages
+  '$.components.operations.*.channel.*',
+  '$.components.operations.*.channel.messages.*',
+  '$.components.operations.*.messages.*',
+  // Channels
   '$.channels.*.messages.*',
-  '$.operations.*.messages.*',
   '$.components.channels.*.messages.*',
-  '$.components.operations.*.messages.*',
+  // messages
   '$.components.messages.*',
 ];
 
@@ -100,4 +105,4 @@ function applyTraitsToObjectV3(value: Record<string, unknown>) {
       value[String(key)] = mergePatch(value[String(key)], trait[String(key)]);
     }
   }
-}
\ No newline at end of file
+}
diff --git a/src/custom-operations/apply-unique-ids.ts b/src/custom-operations/apply-unique-ids.ts
new file mode 100644
index 000000000..d9804235a
--- /dev/null
+++ b/src/custom-operations/apply-unique-ids.ts
@@ -0,0 +1,25 @@
+import { xParserObjectUniqueId } from '../constants';
+
+/**
+ * This function applies unique ids for objects whose key's function as ids, ensuring that the key is part of the value.
+ * 
+ * For v3; Apply unique ids to channel's, and message's
+ */
+export function applyUniqueIds(structure: any) {
+  const asyncapiVersion = structure.asyncapi.charAt(0);
+  switch (asyncapiVersion) {
+  case '3':
+    if (structure.channels) {
+      for (const [channelId, channel] of Object.entries(structure.channels as Record<string, any>)) {
+        channel[xParserObjectUniqueId] = channelId;
+        if (channel.messages) {
+          for (const [messageId, message] of Object.entries(channel.messages as Record<string, any>)) {
+            message[xParserObjectUniqueId] = messageId; 
+          }
+        }
+      }
+    }
+    break;
+  }
+}
+  
\ No newline at end of file
diff --git a/src/custom-operations/index.ts b/src/custom-operations/index.ts
index 28754663a..70e923ff9 100644
--- a/src/custom-operations/index.ts
+++ b/src/custom-operations/index.ts
@@ -2,6 +2,7 @@ import { applyTraitsV2, applyTraitsV3 } from './apply-traits';
 import { resolveCircularRefs } from './resolve-circular-refs';
 import { parseSchemasV2, parseSchemasV3 } from './parse-schema';
 import { anonymousNaming } from './anonymous-naming';
+import { checkCircularRefs } from './check-circular-refs';
 
 import type { RulesetFunctionContext } from '@stoplight/spectral-core';
 import type { Parser } from '../parser';
@@ -9,8 +10,8 @@ import type { ParseOptions } from '../parse';
 import type { AsyncAPIDocumentInterface } from '../models';
 import type { DetailedAsyncAPI } from '../types';
 import type { v2, v3 } from '../spec-types';
-import { checkCircularRefs } from './check-circular-refs';
 
+export {applyUniqueIds} from './apply-unique-ids';
 export async function customOperations(parser: Parser, document: AsyncAPIDocumentInterface, detailed: DetailedAsyncAPI, inventory: RulesetFunctionContext['documentInventory'], options: ParseOptions): Promise<void> {
   switch (detailed.semver.major) {
   case 2: return operationsV2(parser, document, detailed, inventory, options);
diff --git a/src/models/v3/channel.ts b/src/models/v3/channel.ts
index 0734d2b35..9c9c14749 100644
--- a/src/models/v3/channel.ts
+++ b/src/models/v3/channel.ts
@@ -6,9 +6,8 @@ import { Operations } from './operations';
 import { Operation } from './operation';
 import { Servers } from './servers';
 import { Server } from './server';
-
+import { xParserObjectUniqueId } from '../../constants';
 import { CoreModel } from './mixins';
-
 import type { ChannelInterface } from '../channel';
 import type { ChannelParametersInterface } from '../channel-parameters';
 import type { MessagesInterface } from '../messages';
@@ -16,7 +15,6 @@ import type { OperationsInterface } from '../operations';
 import type { OperationInterface } from '../operation';
 import type { ServersInterface } from '../servers';
 import type { ServerInterface } from '../server';
-
 import type { v3 } from '../../spec-types';
 
 export class Channel extends CoreModel<v3.ChannelObject, { id: string }> implements ChannelInterface {
@@ -30,8 +28,8 @@ export class Channel extends CoreModel<v3.ChannelObject, { id: string }> impleme
 
   servers(): ServersInterface {
     const servers: ServerInterface[] = [];
-    const allowedServers = this._json.servers || [];
-    Object.entries(this._meta.asyncapi?.parsed.servers || {}).forEach(([serverName, server]) => {
+    const allowedServers = this._json.servers ?? [];
+    Object.entries(this._meta.asyncapi?.parsed.servers ?? {}).forEach(([serverName, server]) => {
       if (allowedServers.length === 0 || allowedServers.includes(server)) {
         servers.push(this.createModel(Server, server, { id: serverName, pointer: `/servers/${serverName}` }));
       }
@@ -41,8 +39,10 @@ export class Channel extends CoreModel<v3.ChannelObject, { id: string }> impleme
 
   operations(): OperationsInterface {
     const operations: OperationInterface[] = [];
-    Object.entries(((this._meta.asyncapi?.parsed as v3.AsyncAPIObject)?.operations || {})).forEach(([operationId, operation]) => {
-      if ((operation as v3.OperationObject).channel === this._json) {
+    Object.entries(((this._meta.asyncapi?.parsed as v3.AsyncAPIObject)?.operations ?? {} as v3.OperationsObject)).forEach(([operationId, operation]) => {
+      const operationChannelId = ((operation as v3.OperationObject).channel as any)[xParserObjectUniqueId];
+      const channelId = (this._json as any)[xParserObjectUniqueId];
+      if (operationChannelId === channelId) {
         operations.push(
           this.createModel(Operation, operation as v3.OperationObject, { id: operationId, pointer: `/operations/${operationId}` }),
         );
@@ -53,7 +53,7 @@ export class Channel extends CoreModel<v3.ChannelObject, { id: string }> impleme
 
   messages(): MessagesInterface {
     return new Messages(
-      Object.entries(this._json.messages || {}).map(([messageName, message]) => {
+      Object.entries(this._json.messages ?? {}).map(([messageName, message]) => {
         return this.createModel(Message, message as v3.MessageObject, { id: messageName, pointer: this.jsonPath(`messages/${messageName}`) });
       })
     );
@@ -61,7 +61,7 @@ export class Channel extends CoreModel<v3.ChannelObject, { id: string }> impleme
 
   parameters(): ChannelParametersInterface {
     return new ChannelParameters(
-      Object.entries(this._json.parameters || {}).map(([channelParameterName, channelParameter]) => {
+      Object.entries(this._json.parameters ?? {}).map(([channelParameterName, channelParameter]) => {
         return this.createModel(ChannelParameter, channelParameter as v3.ParameterObject, {
           id: channelParameterName,
           pointer: this.jsonPath(`parameters/${channelParameterName}`),
diff --git a/src/models/v3/message.ts b/src/models/v3/message.ts
index 123079927..3fe3f05e8 100644
--- a/src/models/v3/message.ts
+++ b/src/models/v3/message.ts
@@ -6,7 +6,7 @@ import { MessageTraits } from './message-traits';
 import { MessageTrait } from './message-trait';
 import { Servers } from './servers';
 import { Schema } from './schema';
-
+import { xParserObjectUniqueId } from '../../constants';
 import type { ChannelsInterface } from '../channels';
 import type { ChannelInterface } from '../channel';
 import type { MessageInterface } from '../message';
@@ -16,7 +16,6 @@ import type { OperationInterface } from '../operation';
 import type { ServersInterface } from '../servers';
 import type { ServerInterface } from '../server';
 import type { SchemaInterface } from '../schema';
-
 import type { v3 } from '../../spec-types';
 
 export class Message extends MessageTrait<v3.MessageObject> implements MessageInterface {
@@ -58,6 +57,7 @@ export class Message extends MessageTrait<v3.MessageObject> implements MessageIn
   }
 
   channels(): ChannelsInterface {
+    const thisMessageId = (this._json)[xParserObjectUniqueId];
     const channels: ChannelInterface[] = [];
     const channelsData: any[] = [];
     this.operations().forEach(operation => {
@@ -73,7 +73,10 @@ export class Message extends MessageTrait<v3.MessageObject> implements MessageIn
 
     Object.entries((this._meta.asyncapi?.parsed as v3.AsyncAPIObject)?.channels || {}).forEach(([channelId, channelData]) => {
       const channelModel = this.createModel(Channel, channelData as v3.ChannelObject, { id: channelId, pointer: `/channels/${channelId}` });
-      if (!channelsData.includes(channelData) && channelModel.messages().some(m => m.json() === this._json)) {
+      if (!channelsData.includes(channelData) && channelModel.messages().some(m => {
+        const messageId = (m as any)[xParserObjectUniqueId];
+        return messageId === thisMessageId;
+      })) {
         channelsData.push(channelData);
         channels.push(channelModel);
       }
@@ -83,10 +86,15 @@ export class Message extends MessageTrait<v3.MessageObject> implements MessageIn
   }
 
   operations(): OperationsInterface {
+    const thisMessageId = (this._json)[xParserObjectUniqueId];
     const operations: OperationInterface[] = [];
     Object.entries((this._meta.asyncapi?.parsed as v3.AsyncAPIObject)?.operations || {}).forEach(([operationId, operation]) => {
       const operationModel = this.createModel(Operation, operation as v3.OperationObject, { id: operationId, pointer: `/operations/${operationId}` });
-      if (operationModel.messages().some(m => m.json() === this._json)) {
+      const operationHasMessage = operationModel.messages().some(m => {
+        const messageId = (m as any)[xParserObjectUniqueId];
+        return messageId === thisMessageId;
+      });
+      if (operationHasMessage) {
         operations.push(operationModel);
       }
     });
diff --git a/src/models/v3/operation-reply.ts b/src/models/v3/operation-reply.ts
index 93427d388..ec6f9d4b8 100644
--- a/src/models/v3/operation-reply.ts
+++ b/src/models/v3/operation-reply.ts
@@ -4,14 +4,12 @@ import { Message } from './message';
 import { Messages } from './messages';
 import { MessagesInterface } from '../messages';
 import { OperationReplyAddress } from './operation-reply-address';
-
 import { extensions } from './mixins';
-
+import { xParserObjectUniqueId } from '../../constants';
 import type { ExtensionsInterface } from '../extensions';
 import type { OperationReplyInterface } from '../operation-reply';
 import type { OperationReplyAddressInterface } from '../operation-reply-address';
 import type { ChannelInterface } from '../channel';
-
 import type { v3 } from '../../spec-types';
 
 export class OperationReply extends BaseModel<v3.OperationReplyObject, { id?: string }> implements OperationReplyInterface {
@@ -35,14 +33,17 @@ export class OperationReply extends BaseModel<v3.OperationReplyObject, { id?: st
 
   channel(): ChannelInterface | undefined {
     if (this._json.channel) {
-      return this.createModel(Channel, this._json.channel as v3.ChannelObject, { id: '', pointer: this.jsonPath('channel') });
+      const channelId = (this._json.channel as any)[xParserObjectUniqueId];
+      return this.createModel(Channel, this._json.channel as v3.ChannelObject, { id: channelId, pointer: this.jsonPath('channel') });
     }
     return this._json.channel;
   }
+  
   messages(): MessagesInterface {
     return new Messages(
-      Object.entries(this._json.messages || {}).map(([messageName, message]) => {
-        return this.createModel(Message, message as v3.MessageObject, { id: messageName, pointer: this.jsonPath(`messages/${messageName}`) });
+      Object.values(this._json.messages ?? {}).map((message) => {
+        const messageId = (message as any)[xParserObjectUniqueId];
+        return this.createModel(Message, message as v3.MessageObject, { id: messageId, pointer: this.jsonPath(`messages/${messageId}`) });
       })
     );
   }
diff --git a/src/models/v3/operation.ts b/src/models/v3/operation.ts
index 412fbb4c7..f18753134 100644
--- a/src/models/v3/operation.ts
+++ b/src/models/v3/operation.ts
@@ -17,6 +17,7 @@ import type { ServersInterface } from '../servers';
 import type { ServerInterface } from '../server';
 
 import type { v3 } from '../../spec-types';
+import { xParserObjectUniqueId } from '../../constants';
 
 export class Operation extends OperationTrait<v3.OperationObject> implements OperationInterface {
   action(): OperationAction {
@@ -48,23 +49,21 @@ export class Operation extends OperationTrait<v3.OperationObject> implements Ope
 
   channels(): ChannelsInterface {
     if (this._json.channel) {
-      for (const [channelName, channel] of Object.entries(this._meta.asyncapi?.parsed.channels || {})) {
-        if (channel === this._json.channel) {
-          return new Channels([
-            this.createModel(Channel, this._json.channel as v3.ChannelObject, { id: channelName, pointer: `/channels/${channelName}` })
-          ]);
-        }
-      }
+      const operationChannelId = (this._json.channel as any)[xParserObjectUniqueId];
+      return new Channels([
+        this.createModel(Channel, this._json.channel as v3.ChannelObject, { id: operationChannelId, pointer: `/channels/${operationChannelId}` })
+      ]);
     }
     return new Channels([]);
   }
-
+  
   messages(): MessagesInterface {
     const messages: MessageInterface[] = [];
     if (Array.isArray(this._json.messages)) {
       this._json.messages.forEach((message, index) => {
+        const messageId = (message as any)[xParserObjectUniqueId];
         messages.push(
-          this.createModel(Message, message as v3.MessageObject, { id: '', pointer: this.jsonPath(`messages/${index}`) })
+          this.createModel(Message, message as v3.MessageObject, { id: messageId, pointer: this.jsonPath(`messages/${index}`) })
         );
       });
       return new Messages(messages);
diff --git a/src/parse.ts b/src/parse.ts
index 988f9ae51..479bd9dfb 100644
--- a/src/parse.ts
+++ b/src/parse.ts
@@ -1,6 +1,6 @@
 import { AsyncAPIDocumentInterface, ParserAPIVersion } from './models';
 
-import { customOperations } from './custom-operations';
+import { applyUniqueIds, customOperations } from './custom-operations';
 import { validate } from './validate';
 import { copy } from './stringify';
 import { createAsyncAPIDocument } from './document';
@@ -38,13 +38,26 @@ const defaultOptions: ParseOptions = {
   validateOptions: {},
   __unstable: {},
 };
-
+import yaml from 'js-yaml';
 export async function parse(parser: Parser, spectral: Spectral, asyncapi: Input, options: ParseOptions = {}): Promise<ParseOutput> {
   let spectralDocument: Document | undefined;
 
   try {
     options = mergePatch<ParseOptions>(defaultOptions, options);
-    const { validated, diagnostics, extras } = await validate(parser, spectral, asyncapi, { ...options.validateOptions, source: options.source, __unstable: options.__unstable });
+    // Normalize input to always be JSON 
+    let loadedObj;
+    if (typeof asyncapi === 'string') {
+      try {
+        loadedObj = yaml.load(asyncapi);
+      } catch (e) {
+        loadedObj = JSON.parse(asyncapi);
+      }
+    } else {
+      loadedObj = asyncapi;
+    }
+    // Apply unique ids before resolving references
+    applyUniqueIds(loadedObj);
+    const { validated, diagnostics, extras } = await validate(parser, spectral, loadedObj, { ...options.validateOptions, source: options.source, __unstable: options.__unstable });
     if (validated === undefined) {
       return {
         document: undefined,
@@ -58,12 +71,12 @@ export async function parse(parser: Parser, spectral: Spectral, asyncapi: Input,
   
     // unfreeze the object - Spectral makes resolved document "freezed" 
     const validatedDoc = copy(validated as Record<string, any>);
-    const detailed = createDetailedAsyncAPI(validatedDoc, asyncapi as DetailedAsyncAPI['input'], options.source);
+    const detailed = createDetailedAsyncAPI(validatedDoc, loadedObj as DetailedAsyncAPI['input'], options.source);
     const document = createAsyncAPIDocument(detailed);
     setExtension(xParserSpecParsed, true, document);
     setExtension(xParserApiVersion, ParserAPIVersion, document);
     await customOperations(parser, document, detailed, inventory, options);
-  
+    
     return { 
       document,
       diagnostics,
diff --git a/src/spec-types/v3.ts b/src/spec-types/v3.ts
index e24da3bb6..dfad71f7f 100644
--- a/src/spec-types/v3.ts
+++ b/src/spec-types/v3.ts
@@ -143,7 +143,7 @@ export interface OperationTraitObject extends SpecificationExtensions {
 
 export interface OperationReplyObject extends SpecificationExtensions {
   channel?: ChannelObject | ReferenceObject;
-  messages?: MessagesObject;
+  messages?: (MessageObject | ReferenceObject)[];
   address?: OperationReplyAddressObject | ReferenceObject;
 }
 
diff --git a/test/custom-operations/apply-traits-v3.spec.ts b/test/custom-operations/apply-traits-v3.spec.ts
index 61ff909ec..1561b783a 100644
--- a/test/custom-operations/apply-traits-v3.spec.ts
+++ b/test/custom-operations/apply-traits-v3.spec.ts
@@ -1,3 +1,4 @@
+import { xParserObjectUniqueId } from '../../src/constants';
 import { AsyncAPIDocumentV3 } from '../../src/models';
 import { Parser } from '../../src/parser';
 
@@ -56,11 +57,11 @@ describe('custom operations - apply traits v3', function() {
 
     const someOperation1 = v3Document?.json()?.operations?.someOperation1;
     delete someOperation1?.traits;
-    expect(someOperation1).toEqual({ action: 'send', channel: {}, description: 'another description' });
+    expect(someOperation1).toEqual({ action: 'send', channel: { 'x-parser-unique-object-id': 'channel1' }, description: 'another description' });
 
     const someOperation2 = v3Document?.json()?.operations?.someOperation2;
     delete someOperation2?.traits;
-    expect(someOperation2).toEqual({ action: 'send', channel: {}, description: 'root description' });
+    expect(someOperation2).toEqual({ action: 'send', channel: { 'x-parser-unique-object-id': 'channel1' }, description: 'root description' });
   });
 
   it('should apply traits to messages (channels)', async function() {
@@ -112,11 +113,11 @@ describe('custom operations - apply traits v3', function() {
 
     const message1 = v3Document?.json()?.channels?.someChannel1?.messages?.someMessage;
     delete (message1 as v3.MessageObject)?.traits;
-    expect(message1).toEqual({ summary: 'some summary', description: 'another description', 'x-parser-message-name': 'someMessage' });
+    expect(message1).toEqual({ summary: 'some summary', description: 'another description', 'x-parser-message-name': 'someMessage', 'x-parser-unique-object-id': 'someMessage' });
 
     const message2 = v3Document?.json()?.channels?.someChannel2?.messages?.someMessage;
     delete (message2 as v3.MessageObject)?.traits;
-    expect(message2).toEqual({ summary: 'root summary', description: 'root description', 'x-parser-message-name': 'someMessage' });
+    expect(message2).toEqual({ summary: 'root summary', description: 'root description', 'x-parser-message-name': 'someMessage', 'x-parser-unique-object-id': 'someMessage' });
   });
 
   it('should apply traits to messages (components)', async function() {
@@ -168,4 +169,153 @@ describe('custom operations - apply traits v3', function() {
     delete (message2 as v3.MessageObject)?.traits;
     expect(message2).toEqual({ summary: 'root summary', description: 'root description', 'x-parser-message-name': 'someMessage2' });
   });
+
+  it('iterative functions should still work after traits have been applied', async function() {
+    const documentRaw = {
+      asyncapi: '3.0.0',
+      info: {
+        title: 'Streetlights Kafka API',
+        version: '1.0.0',
+        description: 'The Smartylighting Streetlights API allows you to remotely manage the city lights.\n\n### Check out its awesome features:\n\n* Turn a specific streetlight on/off 🌃\n* Dim a specific streetlight 😎\n* Receive real-time information about environmental lighting conditions 📈\n',
+        license: {
+          name: 'Apache 2.0',
+          url: 'https://www.apache.org/licenses/LICENSE-2.0'
+        }
+      },
+      defaultContentType: 'application/json',
+      channels: {
+        'smartylighting.streetlights.1.0.event.{streetlightId}.lighting.measured': {
+          address: 'smartylighting.streetlights.1.0.event.{streetlightId}.lighting.measured',
+          messages: {
+            'receiveLightMeasurement.message': {
+              $ref: '#/components/messages/lightMeasured'
+            }
+          },
+          description: 'The topic on which measured values may be produced and consumed.',
+          parameters: {
+            streetlightId: {
+              $ref: '#/components/parameters/streetlightId'
+            }
+          }
+        }
+      },
+      operations: {
+        receiveLightMeasurement: {
+          action: 'receive',
+          channel: {
+            $ref: '#/channels/smartylighting.streetlights.1.0.event.{streetlightId}.lighting.measured'
+          },
+          summary: 'Inform about environmental lighting conditions of a particular streetlight.',
+          traits: [
+            {
+              $ref: '#/components/operationTraits/kafka'
+            }
+          ],
+          messages: [
+            {
+              $ref: '#/components/messages/lightMeasured'
+            }
+          ]
+        }
+      },
+      components: {
+        messages: {
+          lightMeasured: {
+            name: 'lightMeasured',
+            title: 'Light measured',
+            summary: 'Inform about environmental lighting conditions of a particular streetlight.',
+            contentType: 'application/json',
+            traits: [
+              {
+                $ref: '#/components/messageTraits/commonHeaders'
+              }
+            ],
+            payload: {
+              $ref: '#/components/schemas/lightMeasuredPayload'
+            }
+          }
+        },
+        schemas: {
+          lightMeasuredPayload: {
+            type: 'object',
+            properties: {
+              lumens: {
+                type: 'integer',
+                minimum: 0,
+                description: 'Light intensity measured in lumens.'
+              },
+              sentAt: {
+                $ref: '#/components/schemas/sentAt'
+              }
+            }
+          },
+          sentAt: {
+            type: 'string',
+            format: 'date-time',
+            description: 'Date and time when the message was sent.'
+          }
+        },
+        parameters: {
+          streetlightId: {
+            description: 'The ID of the streetlight.'
+          }
+        },
+        messageTraits: {
+          commonHeaders: {
+            headers: {
+              type: 'object',
+              properties: {
+                'my-app-header': {
+                  type: 'integer',
+                  minimum: 0,
+                  maximum: 100
+                }
+              }
+            }
+          }
+        },
+        operationTraits: {
+          kafka: {
+            bindings: {
+              kafka: {
+                clientId: {
+                  type: 'string',
+                  enum: [
+                    'my-app-id'
+                  ]
+                }
+              }
+            }
+          }
+        }
+      }
+    };
+    const { document } = await parser.parse(documentRaw);
+    
+    const v3Document = document as AsyncAPIDocumentV3;
+    expect(v3Document).toBeInstanceOf(AsyncAPIDocumentV3);
+    const expectedOperationId = 'receiveLightMeasurement';
+    const expectedChannelId = 'smartylighting.streetlights.1.0.event.{streetlightId}.lighting.measured';
+    const operations = v3Document.operations();
+    expect(operations.length).toEqual(1);
+    const operation = operations[0];
+    expect(operation.id()).toEqual(expectedOperationId);
+    const operationChannels = operation.channels().all();
+    expect(operationChannels.length).toEqual(1);
+    const lightMeasuredChannel = operationChannels[0];
+    expect(lightMeasuredChannel.json()[xParserObjectUniqueId]).toEqual(expectedChannelId);
+    const channelOperations = lightMeasuredChannel.operations().all();
+    expect(channelOperations.length).toEqual(1);
+    const circularOperation = channelOperations[0];
+    expect(circularOperation.id()).toEqual(expectedOperationId);
+
+    const channels = v3Document.channels();
+    expect(channels.length).toEqual(1);
+    const channel = channels[0];
+    expect(channel.json()[xParserObjectUniqueId]).toEqual(expectedChannelId);
+    const channelOperations2 = channel.operations().all();
+    expect(channelOperations2.length).toEqual(1);
+    const operation2 = channelOperations2[0];
+    expect(operation2.id()).toEqual(expectedOperationId);
+  });
 });
diff --git a/test/custom-operations/apply-unique-ids.spec.ts b/test/custom-operations/apply-unique-ids.spec.ts
new file mode 100644
index 000000000..2fc32012b
--- /dev/null
+++ b/test/custom-operations/apply-unique-ids.spec.ts
@@ -0,0 +1,30 @@
+import { applyUniqueIds } from '../../src/custom-operations';
+
+describe('applying unique ids', function() {
+  it('should not do anything for v2 inputs', async function() {
+    const input = {asyncapi: '2.0.0'};
+    const output = {...input};
+    applyUniqueIds(output);
+    expect(input).toEqual(output);
+  });
+  describe('for v3', function() {
+    it('should work with no channels input', async function() {
+      const input = {asyncapi: '3.0.0'};
+      const output = {...input};
+      applyUniqueIds(output);
+      expect(input).toEqual(output);
+    });
+    it('should set unique id when input has channels', async function() {
+      const input = {asyncapi: '3.0.0', channels: {testChannel: {}}};
+      const output = {...input, channels: {testChannel: {'x-parser-unique-object-id': 'testChannel'}}};
+      applyUniqueIds(input);
+      expect(input).toEqual(output);
+    });
+    it('should set unique id when input has messages in channels', async function() {
+      const input = {asyncapi: '3.0.0', channels: {testChannel: {messages: {testMessage: {}}}}};
+      const output = {...input, channels: {testChannel: {'x-parser-unique-object-id': 'testChannel', messages: {testMessage: {'x-parser-unique-object-id': 'testMessage'}}}}};
+      applyUniqueIds(input);
+      expect(input).toEqual(output);
+    });
+  });
+});
diff --git a/test/models/v3/operation-reply.spec.ts b/test/models/v3/operation-reply.spec.ts
index 3f065d5a1..c56cd46bb 100644
--- a/test/models/v3/operation-reply.spec.ts
+++ b/test/models/v3/operation-reply.spec.ts
@@ -64,20 +64,21 @@ describe('OperationReply model', function() {
   
   describe('.messages()', function() {
     it('should return collection of messages - single message', function() {
-      const d = new OperationReply({ messages: { someMessage: {} } });
+      const d = new OperationReply({ messages: [{'x-parser-unique-object-id': 'testMessage'}] });
       expect(d.messages()).toBeInstanceOf(Messages);
       expect(d.messages().all()).toHaveLength(1);
       expect(d.messages().all()[0]).toBeInstanceOf(Message);
+      expect(d.messages().all()[0].id()).toEqual('testMessage');
     });
 
     it('should return collection of messages - more than one messages', function() {
-      const d = new OperationReply({ messages: { someMessage1: {}, someMessage2: {} } });
+      const d = new OperationReply({ messages: [{'x-parser-unique-object-id': 'testMessage1'}, {'x-parser-unique-object-id': 'testMessage2'}] });
       expect(d.messages()).toBeInstanceOf(Messages);
       expect(d.messages().all()).toHaveLength(2);
       expect(d.messages().all()[0]).toBeInstanceOf(Message);
-      expect(d.messages().all()[0].id()).toEqual('someMessage1');
+      expect(d.messages().all()[0].id()).toEqual('testMessage1');
       expect(d.messages().all()[1]).toBeInstanceOf(Message);
-      expect(d.messages().all()[1].id()).toEqual('someMessage2');
+      expect(d.messages().all()[1].id()).toEqual('testMessage2');
     });
 
     it('should return undefined if address is not present', function() {
diff --git a/test/models/v3/server.spec.ts b/test/models/v3/server.spec.ts
index 9059fac8c..2669bc8dd 100644
--- a/test/models/v3/server.spec.ts
+++ b/test/models/v3/server.spec.ts
@@ -13,6 +13,7 @@ import { SecurityRequirement } from '../../../src/models/v3/security-requirement
 import { serializeInput, assertCoreModel } from './utils';
 
 import type { v3 } from '../../../src/spec-types';
+import { xParserObjectUniqueId } from '../../../src/constants';
 
 const doc = {
   production: {
@@ -157,6 +158,7 @@ describe('Server Model', function () {
     it('should return collection of operations - server available only in particular channel', function() {
       const doc = serializeInput<v3.ServerObject>({});
       const channel = { servers: [doc] };
+      channel[xParserObjectUniqueId] = 'unique-id';
       const d = new Server(doc, { asyncapi: { parsed: { channels: { someChannel: channel }, operations: { someOperation1: { channel }, someOperation2: { channel: { servers: [{}] } } } } } as any, pointer: '', id: 'production' });
       expect(d.operations()).toBeInstanceOf(Operations);
       expect(d.operations().all()).toHaveLength(1);