ACS Production – Email Delivery Delays and Backlog Processing During Microsoft 365 Outage

Ben Hmida, Syrine 0 Reputation points
2026-11-13T14:52:10.4533333+00:00

Due to outlook outage, we are experiencing issues while sending emails through azure communication service(email)

We're still investigating the ACS Production email delivery delays.

Could you please clarify the following with Microsoft?

How is the ACS email backlog processed during recovery? Is it FIFO, or can newer emails be delivered before older ones?

Are emails marked as OutForDelivery automatically retried until they reach a final delivery status?

How can we identify emails that are still pending and measure the actual delivery delay?

How can we confirm that the entire backlog has been processed and whether any emails need to be resent manually?

We tried correlating OutForDelivery and Delivered events using CorrelationId in Log Analytics, but the query returned no results.

Could Microsoft confirm the correct way to track these events and explain the current queue recovery behavior?

Thanks!

Azure Communication Services
0 comments No comments

1 answer

Sort by: Most helpful
  1. AI answer

    2026-11-13T14:53:27.3233333+00:00

    OutForDelivery confirms only that ACS accepted the send operation and handed the message into the delivery pipeline; track completion by joining message-level and recipient-level records on CorrelationId in ACSEmailStatusUpdateOperational, and do not resend solely because a message remains OutForDelivery.^1^

    Queue recovery and retries

    The public ACS documentation does not specify:

    • FIFO ordering during backlog recovery.
    • Whether newer messages can overtake older messages.
    • The retry interval, retry count, or maximum retry duration for OutForDelivery.

    Therefore, FIFO and indefinite automatic retry should not be assumed. Temporary SMTP failures can permit later delivery attempts, while permanent failures are represented by terminal outcomes such as Bounced or Failed; the documentation does not expose the internal retry schedule.^2^

    The statuses have two different scopes:

    • Message level: Queued, OutForDelivery, or Dropped; RecipientId is empty.
    • Recipient level: Delivered, Expanded, Failed, Quarantined, FilteredSpam, Suppressed, or Bounced; RecipientId is populated.^3^

    Delivered means the message was handed to the recipient’s Mail Transfer Agent, not necessarily placed in the user’s inbox.^4^

    Correct the Log Analytics correlation

    Since the existing correlation query returned no results, avoid joining separate tables unless send-request details are needed. Both the OutForDelivery and recipient delivery records are in ACSEmailStatusUpdateOperational, and CorrelationId contains the ACS MessageId returned by the send request.^5^

    First inspect the actual records and field population:

    ACSEmailStatusUpdateOperational
    | where TimeGenerated >= ago(24h)
    | project TimeGenerated, CorrelationId, RecipientId,
              DeliveryStatus, SmtpStatusCode,
              EnhancedSmtpStatusCode, FailureReason
    | order by TimeGenerated asc
    

    Then produce one row per message and recipient, with the observed delay from OutForDelivery to the recipient status:

    let Start =
        ACSEmailStatusUpdateOperational
        | where TimeGenerated >= ago(7d)
        | where isempty(RecipientId) and DeliveryStatus == "OutForDelivery"
        | summarize OutForDeliveryAt=min(TimeGenerated) by CorrelationId;
    let RecipientUpdates =
        ACSEmailStatusUpdateOperational
        | where TimeGenerated >= ago(7d)
        | where isnotempty(RecipientId)
        | summarize arg_max(TimeGenerated, DeliveryStatus, SmtpStatusCode,
                            EnhancedSmtpStatusCode, FailureReason)
          by CorrelationId, RecipientId;
    Start
    | join kind=leftouter RecipientUpdates on CorrelationId
    | extend Delay = TimeGenerated - OutForDeliveryAt
    | project CorrelationId, RecipientId, OutForDeliveryAt,
              FinalUpdateAt=TimeGenerated, DeliveryStatus, Delay,
              SmtpStatusCode, EnhancedSmtpStatusCode, FailureReason
    | order by OutForDeliveryAt asc
    

    Common reasons for an empty result are:

    1. The query uses CorrelationID instead of the table column CorrelationId.
    2. It expects RecipientId on the OutForDelivery record, although that field is empty for message-level updates.
    3. Email Service Delivery Status Update Logs was not enabled before the affected sends. Diagnostic settings are not retroactive; if logs were not routed to the workspace, the earlier data is unavailable.^3^

    Identify remaining backlog

    This query returns messages for which no recipient-level status has yet appeared:

    let WindowStart = ago(7d);
    ACSEmailStatusUpdateOperational
    | where TimeGenerated >= WindowStart
    | summarize
        OutForDeliveryAt=minif(TimeGenerated,
            isempty(RecipientId) and DeliveryStatus == "OutForDelivery"),
        RecipientUpdates=countif(isnotempty(RecipientId)),
        LatestUpdate=max(TimeGenerated)
      by CorrelationId
    | where isnotnull(OutForDeliveryAt) and RecipientUpdates == 0
    | extend PendingFor=now() - OutForDeliveryAt
    | order by OutForDeliveryAt asc
    

    For multi-recipient sends, compare the observed recipient records with the recipient count stored by the application or in the send-mail operational log. ACS documents one recipient-level event per recipient.^5^

    Treat the backlog as cleared when:

    • No affected message remains without its expected recipient-level record.
    • Each recipient has a terminal delivery status.
    • The count stops decreasing only after allowing for Log Analytics ingestion latency.

    Resend only messages with a terminal failure when the failure is retryable under application policy. Blindly resending unresolved OutForDelivery messages can create duplicates if the original is subsequently delivered. If records remain unresolved after the incident is closed, open an Azure support request with the ACS resource, region, UTC send times, MessageId/CorrelationId, sender domain, and affected recipient domains; public ACS logs do not expose every internal delivery step.^6^

    Incident confirmation and prospective tracking

    Check Microsoft 365 admin center → Health → Service health for the Outlook/Exchange incident and Azure Service Health for resource- or region-specific ACS advisories; the public Azure status page covers broad incidents and may not show narrower resource effects.^7^^8^

    For future sends, store the returned ACS MessageId and subscribe to Communication Services resource → Events → Add Event Subscription → Email Delivery Report Received. Event Grid emits delivery reports when a terminal status is received, including messageId, recipient, status, and delivery-attempt timestamp.^9^


    References

    1. Manually poll for email status when sending email
    2. Understand sender reputation in Azure Communication Services email
    3. Email logs
    4. Azure Communication Services - Email events
    5. ACSEmailStatusUpdateOperational
    6. Azure Communication Services Email remains OutForDelivery and never reaches recipient mail server - Microsoft Q&A
    7. How to check Microsoft 365 service health
    8. Azure Status Overview - Azure Service Health | Microsoft Learn
    9. Handle Email events
    AI-generated content may be incorrect. Read our transparency notes for more information.

    Was this answer helpful?

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.