asyncapi: '2.6.0'

info:
  title: CPaaS Orchestrator Service - Async API
  version: '1.5.0'
  description: |
    AsyncAPI specification for the **cpaas-orchestrator-svc** Kafka event contracts.

    The CPaaS Orchestrator Service is the centralized Communication Platform as a Service
    layer for the EventZR platform. It orchestrates multi-channel message delivery
    (WhatsApp, SMS, Email, Voice, Push) across multiple providers with intelligent
    failover, cost-based routing, rate limiting, and delivery tracking.

    **Event Format:** CloudEvents 1.0
    **Broker:** Amazon MSK Serverless (Kafka)
    **Topic Pattern:** `eventzr.cpaas.<entity>.<action>.v1`
    **Partition Key:** `tenant_id` (ensures per-tenant ordering)

    **Published Events (5):**
    - `eventzr.cpaas.communication.requested.v1` - Message send requested
    - `eventzr.cpaas.communication.sent.v1` - Message sent to provider
    - `eventzr.cpaas.communication.delivered.v1` - Delivery confirmation received
    - `eventzr.cpaas.communication.failed.v1` - Message delivery failed
    - `eventzr.cpaas.provider.failover.v1` - Provider failover occurred

    **Consumed Events (2):**
    - `eventzr.cpaas.sms.inbound.v1` - Inbound SMS via provider webhook
    - `eventzr.notify.transactional.requested.v1` - Transactional notification from notify-svc
  contact:
    name: EventZR Platform Team
    email: platform@eventzr.com
    url: https://eventzr.com
  license:
    name: Proprietary
    url: https://eventzr.com/legal/license

servers:
  msk-staging:
    url: '{mskBootstrapServers}'
    protocol: kafka-secure
    protocolVersion: '3.6'
    description: Amazon MSK Serverless (staging) - us-east-1
    security:
      - saslAwsMskIam: []
    variables:
      mskBootstrapServers:
        default: boot-gpm4u49z.c2.kafka-serverless.us-east-1.amazonaws.com:9098
        description: MSK Serverless bootstrap servers (IAM auth)
  msk-production:
    url: '{mskBootstrapServers}'
    protocol: kafka-secure
    protocolVersion: '3.6'
    description: Amazon MSK Serverless (production) - us-east-1
    security:
      - saslAwsMskIam: []
    variables:
      mskBootstrapServers:
        description: MSK Serverless bootstrap servers (IAM auth)

defaultContentType: application/json

tags:
  - name: communication-lifecycle
    description: Communication request, send, delivery, and failure events
  - name: provider-operations
    description: Provider failover and health events
  - name: consumed
    description: Events consumed from other services (inbound SMS, notify-svc)

# ── Channels ────────────────────────────────────────────────────────────────────

channels:
  # ── Published: Communication Lifecycle ─────────────────────────────────────

  eventzr.cpaas.communication.requested.v1:
    description: |
      Published when a message send is requested by the orchestration service.
      This is the first event in the communication lifecycle, emitted after
      validation, opt-out checks, and rate limit verification pass.
      Consumed by: analytics-svc, audit-svc
    bindings:
      kafka:
        partitions: 12
        replicas: 3
        topicConfiguration:
          retention.ms: 604800000
          cleanup.policy:
            - delete
    publish:
      operationId: publishCommunicationRequested
      summary: Communication send requested
      tags:
        - name: communication-lifecycle
      bindings:
        kafka:
          groupId:
            type: string
            enum:
              - analytics-svc-consumer
              - audit-svc-consumer
          clientId:
            type: string
            enum:
              - cpaas-orchestrator-svc
          bindingVersion: '0.5.0'
      message:
        $ref: '#/components/messages/CommunicationRequested'

  eventzr.cpaas.communication.sent.v1:
    description: |
      Published when a message is successfully sent to the provider (e.g. Twilio,
      Meta WhatsApp, AWS SNS/SES). This does NOT confirm delivery to the end
      recipient; it confirms the provider accepted the message for delivery.
      Consumed by: analytics-svc, notify-svc, webhook-dispatcher
    bindings:
      kafka:
        partitions: 12
        replicas: 3
        topicConfiguration:
          retention.ms: 604800000
          cleanup.policy:
            - delete
    publish:
      operationId: publishCommunicationSent
      summary: Communication sent to provider
      tags:
        - name: communication-lifecycle
      bindings:
        kafka:
          groupId:
            type: string
            enum:
              - analytics-svc-consumer
              - notify-svc-consumer
              - cpaas-webhook-dispatcher
          clientId:
            type: string
            enum:
              - cpaas-orchestrator-svc
          bindingVersion: '0.5.0'
      message:
        $ref: '#/components/messages/CommunicationSent'

  eventzr.cpaas.communication.delivered.v1:
    description: |
      Published when a delivery confirmation is received from the provider via
      webhook callback. Indicates the message reached the recipient device.
      Consumed by: analytics-svc, notify-svc, webhook-dispatcher
    bindings:
      kafka:
        partitions: 12
        replicas: 3
        topicConfiguration:
          retention.ms: 604800000
          cleanup.policy:
            - delete
    publish:
      operationId: publishCommunicationDelivered
      summary: Communication delivery confirmed
      tags:
        - name: communication-lifecycle
      bindings:
        kafka:
          groupId:
            type: string
            enum:
              - analytics-svc-consumer
              - notify-svc-consumer
              - cpaas-webhook-dispatcher
          clientId:
            type: string
            enum:
              - cpaas-orchestrator-svc
          bindingVersion: '0.5.0'
      message:
        $ref: '#/components/messages/CommunicationDelivered'

  eventzr.cpaas.communication.failed.v1:
    description: |
      Published when message delivery fails. Includes retry count and whether the
      service will automatically retry. After max retries are exhausted, will_retry
      is set to false and the message is moved to the dead-letter queue.
      Consumed by: analytics-svc, notify-svc, webhook-dispatcher
    bindings:
      kafka:
        partitions: 12
        replicas: 3
        topicConfiguration:
          retention.ms: 604800000
          cleanup.policy:
            - delete
    publish:
      operationId: publishCommunicationFailed
      summary: Communication delivery failed
      tags:
        - name: communication-lifecycle
      bindings:
        kafka:
          groupId:
            type: string
            enum:
              - analytics-svc-consumer
              - notify-svc-consumer
              - cpaas-webhook-dispatcher
          clientId:
            type: string
            enum:
              - cpaas-orchestrator-svc
          bindingVersion: '0.5.0'
      message:
        $ref: '#/components/messages/CommunicationFailed'

  # ── Published: Provider Operations ─────────────────────────────────────────

  eventzr.cpaas.provider.failover.v1:
    description: |
      Published when a provider failover occurs. The orchestrator automatically
      switches to a backup provider when the primary provider is unhealthy,
      rate-limited, or returns errors. Consumed by: analytics-svc, admin alerts
    bindings:
      kafka:
        partitions: 12
        replicas: 3
        topicConfiguration:
          retention.ms: 604800000
          cleanup.policy:
            - delete
    publish:
      operationId: publishProviderFailover
      summary: Provider failover occurred
      tags:
        - name: provider-operations
      bindings:
        kafka:
          groupId:
            type: string
            enum:
              - analytics-svc-consumer
              - admin-alert-consumer
          clientId:
            type: string
            enum:
              - cpaas-orchestrator-svc
          bindingVersion: '0.5.0'
      message:
        $ref: '#/components/messages/ProviderFailover'

  # ── Consumed Channels ──────────────────────────────────────────────────────

  eventzr.cpaas.sms.inbound.v1:
    description: |
      Consumed when an inbound SMS is received via a provider webhook
      (e.g. Twilio, MessageBird). The consumer checks for opt-out keywords
      (STOP, UNSUBSCRIBE, etc.) and records opt-out status. The tenant is
      determined by matching the receiving phone number against provider configs.
    subscribe:
      operationId: consumeInboundSms
      summary: Handle inbound SMS received via provider webhook
      tags:
        - name: consumed
      bindings:
        kafka:
          groupId:
            type: string
            enum:
              - cpaas-orchestrator-svc-consumer
          bindingVersion: '0.5.0'
      message:
        $ref: '#/components/messages/SmsInbound'

  eventzr.notify.transactional.requested.v1:
    description: |
      Consumed from notify-svc. Represents a transactional notification request
      (e.g. order confirmation, password reset, booking reminder). The CPaaS
      orchestrator resolves the template, selects the optimal provider and channel,
      then dispatches the message.
    subscribe:
      operationId: consumeTransactionalNotificationRequested
      summary: Handle transactional notification request from notify-svc
      tags:
        - name: consumed
      bindings:
        kafka:
          groupId:
            type: string
            enum:
              - cpaas-orchestrator-svc-consumer
          bindingVersion: '0.5.0'
      message:
        $ref: '#/components/messages/TransactionalNotificationRequested'

# ── Components ──────────────────────────────────────────────────────────────────

components:
  securitySchemes:
    saslAwsMskIam:
      type: scramSha512
      description: AWS MSK IAM authentication via SASL OAUTHBEARER

  messageTraits:
    CloudEventEnvelope:
      headers:
        type: object
        required:
          - ce_specversion
          - ce_type
          - ce_source
          - ce_id
          - ce_time
          - ce_tenantid
        properties:
          ce_specversion:
            type: string
            const: '1.0'
          ce_type:
            type: string
            description: 'CloudEvents type (e.g., com.eventzr.cpaas.communication.sent)'
          ce_source:
            type: string
            const: 'https://eventzr.com/cpaas-orchestrator-svc'
          ce_id:
            type: string
            format: uuid
          ce_time:
            type: string
            format: date-time
          ce_subject:
            type: string
            description: 'e.g., communication/<messageId>'
          ce_datacontenttype:
            type: string
            const: application/json
          ce_tenantid:
            type: string
            format: uuid
            description: Tenant ID (also Kafka partition key)
          ce_correlationid:
            type: string
            format: uuid
            description: Correlation ID for distributed tracing
      bindings:
        kafka:
          key:
            type: string
            format: uuid
            description: Kafka message key is always tenant_id
          bindingVersion: '0.5.0'

  schemas:
    # ── Shared / Reusable Schemas ──────────────────────────────────────────

    TenantId:
      type: string
      format: uuid
      description: Multi-tenant identifier (UUID v4)
      example: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'

    MessageId:
      type: string
      format: uuid
      description: Unique message identifier (UUID v4)
      example: 'f47ac10b-58cc-4372-a567-0e02b2c3d479'

    ChannelType:
      type: string
      enum:
        - WHATSAPP
        - SMS
        - EMAIL
        - VOICE
        - PUSH
      description: Communication channel type

    ProviderName:
      type: string
      enum:
        - META_WHATSAPP
        - TWILIO
        - AWS_SNS
        - AWS_SES
        - SENDGRID
        - MESSAGEBIRD
        - MAILGUN
        - CUSTOM
        - NONE
      description: CPaaS provider identifier

    Priority:
      type: string
      enum:
        - critical
        - high
        - normal
        - low
      description: Message delivery priority
      default: normal

    IsoTimestamp:
      type: string
      format: date-time
      description: ISO 8601 timestamp
      example: '2026-02-21T00:00:00.000Z'

    CloudEventBase:
      type: object
      required:
        - specversion
        - type
        - source
        - id
        - time
        - datacontenttype
        - tenantid
        - data
      properties:
        specversion:
          type: string
          const: '1.0'
        type:
          type: string
          description: 'CloudEvents type (e.g., com.eventzr.cpaas.communication.sent)'
        source:
          type: string
          const: 'cpaas-orchestrator-svc'
        subject:
          type: string
          description: 'Resource subject (e.g., communication/<messageId>)'
        id:
          type: string
          format: uuid
        time:
          $ref: '#/components/schemas/IsoTimestamp'
        datacontenttype:
          type: string
          const: application/json
        tenantid:
          $ref: '#/components/schemas/TenantId'
        correlationid:
          type: string
          format: uuid
          description: Correlation ID for distributed tracing
        data:
          type: object

    # ── Published Event Payloads ───────────────────────────────────────────

    CommunicationRequestedData:
      type: object
      required:
        - message_id
        - tenantId
        - channel
        - message_body
        - is_transactional
      properties:
        message_id:
          $ref: '#/components/schemas/MessageId'
        tenantId:
          $ref: '#/components/schemas/TenantId'
        channel:
          $ref: '#/components/schemas/ChannelType'
        recipient_phone:
          type: string
          description: 'Recipient phone number in E.164 format (for SMS, WhatsApp, Voice)'
          example: '+27821234567'
        recipient_email:
          type: string
          format: email
          description: 'Recipient email address (for Email channel)'
          example: 'user@example.com'
        message_body:
          type: string
          description: Message content (plain text or template-rendered body)
          maxLength: 10000
        is_transactional:
          type: boolean
          description: Whether this is a transactional (non-marketing) message
        metadata:
          type: object
          description: Arbitrary key-value metadata attached to the message
          additionalProperties: true

    CommunicationSentData:
      type: object
      required:
        - message_id
        - tenantId
        - channel
        - provider_name
        - provider_message_id
        - sent_at
      properties:
        message_id:
          $ref: '#/components/schemas/MessageId'
        tenantId:
          $ref: '#/components/schemas/TenantId'
        channel:
          $ref: '#/components/schemas/ChannelType'
        provider_name:
          $ref: '#/components/schemas/ProviderName'
        provider_message_id:
          type: string
          description: Provider-assigned message ID for tracking
          example: 'SM1234567890abcdef'
        cost_usd:
          type: number
          format: double
          description: Estimated cost in USD for the sent message
          example: 0.0075
        sent_at:
          $ref: '#/components/schemas/IsoTimestamp'

    CommunicationDeliveredData:
      type: object
      required:
        - message_id
        - tenantId
        - channel
        - provider_name
        - delivered_at
      properties:
        message_id:
          $ref: '#/components/schemas/MessageId'
        tenantId:
          $ref: '#/components/schemas/TenantId'
        channel:
          $ref: '#/components/schemas/ChannelType'
        provider_name:
          $ref: '#/components/schemas/ProviderName'
        delivered_at:
          $ref: '#/components/schemas/IsoTimestamp'
        recipient_phone:
          type: string
          description: 'Recipient phone number in E.164 format'
          example: '+27821234567'
        recipient_email:
          type: string
          format: email
          description: 'Recipient email address'
          example: 'user@example.com'

    CommunicationFailedData:
      type: object
      required:
        - message_id
        - tenantId
        - channel
        - provider_name
        - error_message
        - retry_count
        - failed_at
        - will_retry
      properties:
        message_id:
          $ref: '#/components/schemas/MessageId'
        tenantId:
          $ref: '#/components/schemas/TenantId'
        channel:
          $ref: '#/components/schemas/ChannelType'
        provider_name:
          $ref: '#/components/schemas/ProviderName'
        error_message:
          type: string
          description: Human-readable error description
          example: 'Provider returned 429 Too Many Requests'
        error_code:
          type: string
          description: Machine-readable error code from provider
          example: 'ERR_CPAAS_RATE_LIMITED'
        retry_count:
          type: integer
          minimum: 0
          description: Number of retry attempts made so far
          example: 3
        failed_at:
          $ref: '#/components/schemas/IsoTimestamp'
        will_retry:
          type: boolean
          description: Whether the service will automatically retry delivery

    ProviderFailoverData:
      type: object
      required:
        - message_id
        - tenantId
        - channel
        - failed_provider
        - failover_provider
        - reason
        - failover_at
      properties:
        message_id:
          $ref: '#/components/schemas/MessageId'
        tenantId:
          $ref: '#/components/schemas/TenantId'
        channel:
          $ref: '#/components/schemas/ChannelType'
        failed_provider:
          $ref: '#/components/schemas/ProviderName'
        failover_provider:
          $ref: '#/components/schemas/ProviderName'
        reason:
          type: string
          description: Reason the failover was triggered
          example: 'Primary provider TWILIO returned 503 Service Unavailable'
        failover_at:
          $ref: '#/components/schemas/IsoTimestamp'

    # ── Consumed Event Payloads ────────────────────────────────────────────

    SmsInboundData:
      type: object
      required:
        - from_phone
        - to_phone
        - message_body
        - provider_name
        - received_at
        - provider_message_id
      properties:
        from_phone:
          type: string
          description: 'Sender phone number in E.164 format'
          example: '+27821234567'
        to_phone:
          type: string
          description: 'Receiving phone number in E.164 format (mapped to tenant)'
          example: '+12025551234'
        message_body:
          type: string
          description: SMS message body text
          example: 'STOP'
        provider_name:
          $ref: '#/components/schemas/ProviderName'
        received_at:
          $ref: '#/components/schemas/IsoTimestamp'
        provider_message_id:
          type: string
          description: Provider-assigned inbound message ID
          example: 'SM0987654321fedcba'

    TransactionalNotificationRequestedData:
      type: object
      required:
        - tenantId
        - channel
        - recipient
        - templateId
        - priority
      properties:
        tenantId:
          $ref: '#/components/schemas/TenantId'
        channel:
          $ref: '#/components/schemas/ChannelType'
        recipient:
          type: string
          description: 'Recipient identifier (phone in E.164 or email address)'
          example: '+27821234567'
        templateId:
          type: string
          format: uuid
          description: Template ID used to render the message content
        variables:
          type: object
          description: Template variable substitutions
          additionalProperties:
            type: string
          example:
            name: 'John Doe'
            event_name: 'EventZR Launch Party'
            date: '2026-03-15'
        priority:
          $ref: '#/components/schemas/Priority'

  # ── Messages ────────────────────────────────────────────────────────────────

  messages:
    # ── Published Messages ─────────────────────────────────────────────────

    CommunicationRequested:
      name: CommunicationRequested
      title: Communication Requested
      summary: A message send has been requested through the CPaaS orchestrator
      contentType: application/json
      traits:
        - $ref: '#/components/messageTraits/CloudEventEnvelope'
      payload:
        allOf:
          - $ref: '#/components/schemas/CloudEventBase'
          - type: object
            properties:
              type:
                const: com.eventzr.cpaas.communication.requested
              data:
                $ref: '#/components/schemas/CommunicationRequestedData'

    CommunicationSent:
      name: CommunicationSent
      title: Communication Sent
      summary: A message was successfully sent to the provider
      contentType: application/json
      traits:
        - $ref: '#/components/messageTraits/CloudEventEnvelope'
      payload:
        allOf:
          - $ref: '#/components/schemas/CloudEventBase'
          - type: object
            properties:
              type:
                const: com.eventzr.cpaas.communication.sent
              data:
                $ref: '#/components/schemas/CommunicationSentData'

    CommunicationDelivered:
      name: CommunicationDelivered
      title: Communication Delivered
      summary: Delivery confirmation received from the provider
      contentType: application/json
      traits:
        - $ref: '#/components/messageTraits/CloudEventEnvelope'
      payload:
        allOf:
          - $ref: '#/components/schemas/CloudEventBase'
          - type: object
            properties:
              type:
                const: com.eventzr.cpaas.communication.delivered
              data:
                $ref: '#/components/schemas/CommunicationDeliveredData'

    CommunicationFailed:
      name: CommunicationFailed
      title: Communication Failed
      summary: Message delivery failed after provider returned an error
      contentType: application/json
      traits:
        - $ref: '#/components/messageTraits/CloudEventEnvelope'
      payload:
        allOf:
          - $ref: '#/components/schemas/CloudEventBase'
          - type: object
            properties:
              type:
                const: com.eventzr.cpaas.communication.failed
              data:
                $ref: '#/components/schemas/CommunicationFailedData'

    ProviderFailover:
      name: ProviderFailover
      title: Provider Failover
      summary: The orchestrator failed over from one provider to another
      contentType: application/json
      traits:
        - $ref: '#/components/messageTraits/CloudEventEnvelope'
      payload:
        allOf:
          - $ref: '#/components/schemas/CloudEventBase'
          - type: object
            properties:
              type:
                const: com.eventzr.cpaas.provider.failover
              data:
                $ref: '#/components/schemas/ProviderFailoverData'

    # ── Consumed Messages ──────────────────────────────────────────────────

    SmsInbound:
      name: SmsInbound
      title: Inbound SMS Received
      summary: An inbound SMS was received via a provider webhook
      contentType: application/json
      traits:
        - $ref: '#/components/messageTraits/CloudEventEnvelope'
      payload:
        allOf:
          - $ref: '#/components/schemas/CloudEventBase'
          - type: object
            properties:
              type:
                const: com.eventzr.cpaas.sms.inbound
              source:
                const: 'integration-hub-svc'
              data:
                $ref: '#/components/schemas/SmsInboundData'

    TransactionalNotificationRequested:
      name: TransactionalNotificationRequested
      title: Transactional Notification Requested (from notify-svc)
      summary: A transactional notification request from notify-svc
      contentType: application/json
      traits:
        - $ref: '#/components/messageTraits/CloudEventEnvelope'
      payload:
        allOf:
          - $ref: '#/components/schemas/CloudEventBase'
          - type: object
            properties:
              type:
                const: com.eventzr.notify.transactional.requested
              source:
                const: 'notify-svc'
              data:
                $ref: '#/components/schemas/TransactionalNotificationRequestedData'
