asyncapi: 2.6.0
info:
  title: reviews-svc Kafka Events
  version: 1.0.0
  description: |
    AsyncAPI specification for all Kafka events produced by reviews-svc.
    Events are published using the transactional outbox pattern (CloudEvents 1.0).
    The OutboxPublisherService polls the review_outbox table and publishes to Kafka.
  contact:
    name: EventZR Team
    url: https://docs.eventzr.com

defaultContentType: application/json

servers:
  staging:
    url: eventzr-staging-msk.kafka.us-east-1.amazonaws.com:9098
    protocol: kafka-secure
    description: Amazon MSK Serverless (staging)

channels:
  eventzr.review.created.v1:
    description: Emitted when a new review is created
    publish:
      operationId: emitReviewCreated
      message:
        $ref: '#/components/messages/ReviewCreated'

  eventzr.review.published.v1:
    description: Emitted when a review status changes to published
    publish:
      operationId: emitReviewPublished
      message:
        $ref: '#/components/messages/ReviewPublished'

  eventzr.review.updated.v1:
    description: Emitted when a review is updated
    publish:
      operationId: emitReviewUpdated
      message:
        $ref: '#/components/messages/ReviewUpdated'

  eventzr.review.deleted.v1:
    description: Emitted when a review is deleted
    publish:
      operationId: emitReviewDeleted
      message:
        $ref: '#/components/messages/ReviewDeleted'

  eventzr.review.sentiment.computed.v1:
    description: Emitted when AI sentiment analysis completes for a review
    publish:
      operationId: emitReviewSentimentComputed
      message:
        $ref: '#/components/messages/ReviewSentimentComputed'

  eventzr.review.flagged.v1:
    description: Emitted when a review is flagged for moderation
    publish:
      operationId: emitReviewFlagged
      message:
        $ref: '#/components/messages/ReviewFlagged'

  eventzr.review.reply.created.v1:
    description: Emitted when a reply is created for a review
    publish:
      operationId: emitReviewReplyCreated
      message:
        $ref: '#/components/messages/ReviewReplyCreated'

  eventzr.review.reply.updated.v1:
    description: Emitted when a reply to a review is updated
    publish:
      operationId: emitReviewReplyUpdated
      message:
        $ref: '#/components/messages/ReviewReplyUpdated'

  eventzr.review.shared.v1:
    description: Emitted when a review is shared on social media
    publish:
      operationId: emitReviewShared
      message:
        $ref: '#/components/messages/ReviewShared'

  eventzr.review.imported.v1:
    description: Emitted when a review is imported from an external source
    publish:
      operationId: emitReviewImported
      message:
        $ref: '#/components/messages/ReviewImported'

  eventzr.review.nps.recorded.v1:
    description: Emitted when an NPS score is recorded
    publish:
      operationId: emitReviewNpsRecorded
      message:
        $ref: '#/components/messages/ReviewNpsRecorded'

  eventzr.review.csat.recorded.v1:
    description: Emitted when a CSAT score is recorded
    publish:
      operationId: emitReviewCsatRecorded
      message:
        $ref: '#/components/messages/ReviewCsatRecorded'

  eventzr.review.moderated.v1:
    description: Emitted when a review moderation action is taken
    publish:
      operationId: emitReviewModerated
      message:
        $ref: '#/components/messages/ReviewModerated'

  eventzr.review.dispute.created.v1:
    description: Emitted when a dispute is filed against a review
    publish:
      operationId: emitDisputeCreated
      message:
        $ref: '#/components/messages/ReviewDisputeCreated'

  eventzr.review.dispute.resolved.v1:
    description: Emitted when a dispute against a review is resolved
    publish:
      operationId: emitDisputeResolved
      message:
        $ref: '#/components/messages/ReviewDisputeResolved'

components:
  messages:
    ReviewCreated:
      name: ReviewCreated
      title: Review Created
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ReviewCreatedEnvelope'

    ReviewPublished:
      name: ReviewPublished
      title: Review Published
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ReviewPublishedEnvelope'

    ReviewUpdated:
      name: ReviewUpdated
      title: Review Updated
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ReviewUpdatedEnvelope'

    ReviewDeleted:
      name: ReviewDeleted
      title: Review Deleted
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ReviewDeletedEnvelope'

    ReviewSentimentComputed:
      name: ReviewSentimentComputed
      title: Review Sentiment Computed
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ReviewSentimentComputedEnvelope'

    ReviewFlagged:
      name: ReviewFlagged
      title: Review Flagged
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ReviewFlaggedEnvelope'

    ReviewReplyCreated:
      name: ReviewReplyCreated
      title: Review Reply Created
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ReviewReplyCreatedEnvelope'

    ReviewReplyUpdated:
      name: ReviewReplyUpdated
      title: Review Reply Updated
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ReviewReplyUpdatedEnvelope'

    ReviewShared:
      name: ReviewShared
      title: Review Shared
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ReviewSharedEnvelope'

    ReviewImported:
      name: ReviewImported
      title: Review Imported
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ReviewImportedEnvelope'

    ReviewNpsRecorded:
      name: ReviewNpsRecorded
      title: NPS Score Recorded
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ReviewNpsRecordedEnvelope'

    ReviewCsatRecorded:
      name: ReviewCsatRecorded
      title: CSAT Score Recorded
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ReviewCsatRecordedEnvelope'

    ReviewModerated:
      name: ReviewModerated
      title: Review Moderated
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ReviewModeratedEnvelope'

    ReviewDisputeCreated:
      name: ReviewDisputeCreated
      title: Dispute Created
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ReviewDisputeCreatedEnvelope'

    ReviewDisputeResolved:
      name: ReviewDisputeResolved
      title: Dispute Resolved
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ReviewDisputeResolvedEnvelope'

  schemas:
    # CloudEvents envelope (shared structure)
    CloudEventBase:
      type: object
      required:
        - specversion
        - type
        - source
        - id
        - time
        - datacontenttype
      properties:
        specversion:
          type: string
          const: '1.0'
        source:
          type: string
          const: reviews-svc
        id:
          type: string
          format: uuid
        time:
          type: string
          format: date-time
        datacontenttype:
          type: string
          const: application/json

    # Event envelopes
    ReviewCreatedEnvelope:
      allOf:
        - $ref: '#/components/schemas/CloudEventBase'
        - type: object
          properties:
            type:
              type: string
              const: eventzr.review.created.v1
            data:
              $ref: '#/components/schemas/ReviewCreatedData'

    ReviewPublishedEnvelope:
      allOf:
        - $ref: '#/components/schemas/CloudEventBase'
        - type: object
          properties:
            type:
              type: string
              const: eventzr.review.published.v1
            data:
              $ref: '#/components/schemas/ReviewPublishedData'

    ReviewUpdatedEnvelope:
      allOf:
        - $ref: '#/components/schemas/CloudEventBase'
        - type: object
          properties:
            type:
              type: string
              const: eventzr.review.updated.v1
            data:
              $ref: '#/components/schemas/ReviewUpdatedData'

    ReviewDeletedEnvelope:
      allOf:
        - $ref: '#/components/schemas/CloudEventBase'
        - type: object
          properties:
            type:
              type: string
              const: eventzr.review.deleted.v1
            data:
              $ref: '#/components/schemas/ReviewDeletedData'

    ReviewSentimentComputedEnvelope:
      allOf:
        - $ref: '#/components/schemas/CloudEventBase'
        - type: object
          properties:
            type:
              type: string
              const: eventzr.review.sentiment.computed.v1
            data:
              $ref: '#/components/schemas/ReviewSentimentComputedData'

    ReviewFlaggedEnvelope:
      allOf:
        - $ref: '#/components/schemas/CloudEventBase'
        - type: object
          properties:
            type:
              type: string
              const: eventzr.review.flagged.v1
            data:
              $ref: '#/components/schemas/ReviewFlaggedData'

    ReviewReplyCreatedEnvelope:
      allOf:
        - $ref: '#/components/schemas/CloudEventBase'
        - type: object
          properties:
            type:
              type: string
              const: eventzr.review.reply.created.v1
            data:
              $ref: '#/components/schemas/ReviewReplyCreatedData'

    ReviewReplyUpdatedEnvelope:
      allOf:
        - $ref: '#/components/schemas/CloudEventBase'
        - type: object
          properties:
            type:
              type: string
              const: eventzr.review.reply.updated.v1
            data:
              $ref: '#/components/schemas/ReviewReplyUpdatedData'

    ReviewSharedEnvelope:
      allOf:
        - $ref: '#/components/schemas/CloudEventBase'
        - type: object
          properties:
            type:
              type: string
              const: eventzr.review.shared.v1
            data:
              $ref: '#/components/schemas/ReviewSharedData'

    ReviewImportedEnvelope:
      allOf:
        - $ref: '#/components/schemas/CloudEventBase'
        - type: object
          properties:
            type:
              type: string
              const: eventzr.review.imported.v1
            data:
              $ref: '#/components/schemas/ReviewImportedData'

    ReviewNpsRecordedEnvelope:
      allOf:
        - $ref: '#/components/schemas/CloudEventBase'
        - type: object
          properties:
            type:
              type: string
              const: eventzr.review.nps.recorded.v1
            data:
              $ref: '#/components/schemas/ReviewNpsRecordedData'

    ReviewCsatRecordedEnvelope:
      allOf:
        - $ref: '#/components/schemas/CloudEventBase'
        - type: object
          properties:
            type:
              type: string
              const: eventzr.review.csat.recorded.v1
            data:
              $ref: '#/components/schemas/ReviewCsatRecordedData'

    ReviewModeratedEnvelope:
      allOf:
        - $ref: '#/components/schemas/CloudEventBase'
        - type: object
          properties:
            type:
              type: string
              const: eventzr.review.moderated.v1
            data:
              $ref: '#/components/schemas/ReviewModeratedData'

    ReviewDisputeCreatedEnvelope:
      allOf:
        - $ref: '#/components/schemas/CloudEventBase'
        - type: object
          properties:
            type:
              type: string
              const: eventzr.review.dispute.created.v1
            data:
              $ref: '#/components/schemas/ReviewDisputeCreatedData'

    ReviewDisputeResolvedEnvelope:
      allOf:
        - $ref: '#/components/schemas/CloudEventBase'
        - type: object
          properties:
            type:
              type: string
              const: eventzr.review.dispute.resolved.v1
            data:
              $ref: '#/components/schemas/ReviewDisputeResolvedData'

    # Data schemas
    ReviewCreatedData:
      type: object
      required:
        - reviewId
        - tenantId
        - userId
        - entityType
        - entityId
        - rating
        - title
        - content
        - isVerifiedPurchase
        - isAnonymous
        - status
        - createdAt
      properties:
        reviewId:
          type: string
        tenantId:
          type: string
        userId:
          type: string
        entityType:
          type: string
          enum: [event, speaker, vendor, venue, session]
        entityId:
          type: string
        rating:
          type: integer
          minimum: 1
          maximum: 5
        title:
          type: string
        content:
          type: string
        sentiment:
          type: string
          enum: [positive, neutral, negative]
        sentimentScore:
          type: number
          minimum: -1
          maximum: 1
        isVerifiedPurchase:
          type: boolean
        isAnonymous:
          type: boolean
        status:
          type: string
          enum: [draft, pending, published, rejected]
        mediaUrls:
          type: array
          items:
            type: string
        tags:
          type: array
          items:
            type: string
        createdAt:
          type: string
          format: date-time

    ReviewPublishedData:
      type: object
      required:
        - reviewId
        - tenantId
        - publishedAt
      properties:
        reviewId:
          type: string
        tenantId:
          type: string
        publishedAt:
          type: string
          format: date-time

    ReviewUpdatedData:
      type: object
      required:
        - reviewId
        - tenantId
        - updatedFields
        - updatedAt
      properties:
        reviewId:
          type: string
        tenantId:
          type: string
        updatedFields:
          type: array
          items:
            type: string
        updatedAt:
          type: string
          format: date-time

    ReviewDeletedData:
      type: object
      required:
        - reviewId
        - tenantId
        - deletedBy
        - deletedAt
      properties:
        reviewId:
          type: string
        tenantId:
          type: string
        deletedBy:
          type: string
        deletedAt:
          type: string
          format: date-time
        reason:
          type: string

    ReviewSentimentComputedData:
      type: object
      required:
        - reviewId
        - tenantId
        - sentiment
        - sentimentScore
      properties:
        reviewId:
          type: string
        tenantId:
          type: string
        sentiment:
          type: string
          enum: [positive, neutral, negative, mixed]
        sentimentScore:
          type: number
          minimum: -1
          maximum: 1
        keywords:
          type: array
          items:
            type: string
        computedAt:
          type: string
          format: date-time

    ReviewFlaggedData:
      type: object
      required:
        - reviewId
        - tenantId
        - flagReason
        - flaggedBy
      properties:
        reviewId:
          type: string
        tenantId:
          type: string
        flagReason:
          type: string
          enum: [spam, offensive, fake, inappropriate, other]
        flaggedBy:
          type: string
        flaggedAt:
          type: string
          format: date-time

    ReviewReplyCreatedData:
      type: object
      required:
        - replyId
        - reviewId
        - tenantId
        - authorId
        - content
      properties:
        replyId:
          type: string
        reviewId:
          type: string
        tenantId:
          type: string
        authorId:
          type: string
        content:
          type: string
        createdAt:
          type: string
          format: date-time

    ReviewReplyUpdatedData:
      type: object
      required:
        - replyId
        - reviewId
        - tenantId
      properties:
        replyId:
          type: string
        reviewId:
          type: string
        tenantId:
          type: string
        content:
          type: string
        updatedAt:
          type: string
          format: date-time

    ReviewSharedData:
      type: object
      required:
        - shareId
        - reviewId
        - tenantId
        - platform
      properties:
        shareId:
          type: string
        reviewId:
          type: string
        tenantId:
          type: string
        platform:
          type: string
          enum: [twitter, facebook, linkedin, whatsapp, email, copy_link]
        sharedBy:
          type: string
        sharedAt:
          type: string
          format: date-time

    ReviewImportedData:
      type: object
      required:
        - reviewId
        - tenantId
        - sourcePlatform
        - externalId
      properties:
        reviewId:
          type: string
        tenantId:
          type: string
        sourcePlatform:
          type: string
          enum: [google, yelp, tripadvisor, facebook, trustpilot, other]
        externalId:
          type: string
        importedAt:
          type: string
          format: date-time

    ReviewNpsRecordedData:
      type: object
      required:
        - feedbackId
        - tenantId
        - campaignId
        - score
      properties:
        feedbackId:
          type: string
        tenantId:
          type: string
        campaignId:
          type: string
        score:
          type: integer
          minimum: 0
          maximum: 10
        comment:
          type: string
        respondentId:
          type: string
        recordedAt:
          type: string
          format: date-time

    ReviewCsatRecordedData:
      type: object
      required:
        - feedbackId
        - tenantId
        - score
      properties:
        feedbackId:
          type: string
        tenantId:
          type: string
        score:
          type: integer
          minimum: 1
          maximum: 5
        comment:
          type: string
        respondentId:
          type: string
        recordedAt:
          type: string
          format: date-time

    ReviewModeratedData:
      type: object
      required:
        - reviewId
        - tenantId
        - action
        - moderatedBy
      properties:
        reviewId:
          type: string
        tenantId:
          type: string
        action:
          type: string
          enum: [approved, rejected, flagged]
        moderatedBy:
          type: string
        reason:
          type: string
        moderatedAt:
          type: string
          format: date-time

    ReviewDisputeCreatedData:
      type: object
      required:
        - disputeId
        - reviewId
        - tenantId
        - filedBy
        - reason
      properties:
        disputeId:
          type: string
        reviewId:
          type: string
        tenantId:
          type: string
        filedBy:
          type: string
        reason:
          type: string
          enum:
            [
              factual_inaccuracy,
              not_a_customer,
              inappropriate_content,
              spam,
              conflict_of_interest,
              other,
            ]
        description:
          type: string
        createdAt:
          type: string
          format: date-time

    ReviewDisputeResolvedData:
      type: object
      required:
        - disputeId
        - reviewId
        - tenantId
        - decision
        - resolvedBy
      properties:
        disputeId:
          type: string
        reviewId:
          type: string
        tenantId:
          type: string
        decision:
          type: string
          enum: [accepted, rejected]
        resolvedBy:
          type: string
        notes:
          type: string
        resolvedAt:
          type: string
          format: date-time
