Create a list of unavailable emails

Microsoft Outlook logo

In this post, I publish the Visual Basic (VBA) code for Microsoft Outlook. It is used to create a list of unavailable emails. For example, I want to personalize an email to send to all my colleagues. Each email has a different link. For this reason, I use Microsoft Word to send the emails using the Mailing function.

Mail Merge in Microsoft Word

Mail Merge is one of Microsoft Word’s most powerful features. It enables you to personalise documents at scale. This is done by pulling data from a source like Excel. But did you know you can take personalisation a step further by embedding custom hyperlinks for each recipient? This technique is a game-changer for marketing campaigns, surveys, and event invitations.

Prepare Your Data Source

  • Create an Excel sheet (or other supported source) with columns for names, emails, and a dedicated column for URLs.

Set Up Mail Merge in Word

  • Go to Mailings > Start Mail Merge and select your document type (letters, emails, etc.).
  • Connect to your data source via Select Recipients > Use an Existing List.

Insert Custom Hyperlinks

  • Place your cursor where you want the link.
  • Use Insert > Hyperlink.
  • In the “Address” field, insert the merge field for your URL column (e.g., «Link»).
  • The display text can be static (like “Click Here”) or dynamic (e.g., «Name»’s Survey Link).

Preview & Test

  • Use Preview Results to ensure each recipient’s link is correctly embedded.
  • Test a few merged documents to confirm the hyperlinks are active.

After sending emails

After Word finishes to send all the emails, probably some of them are not deliverable. So, I want to have a list of the unavailable emails to remove them from the list. I don’t want to do it manually, so I wrote a script.

What the script does

  1. It scans all the emails in your Inbox one by one.
  2. Finds bounce‑back emails (NDRs)
    • It checks the subject line for typical “failure” phrases like Undeliverable or Delivery Status Notification.
    • These are the messages Outlook/Exchange sends you when an email couldn’t be delivered.
  3. Filters by your original subject
    • It only processes bounce‑backs that mention the subject of the email you care about (you set this in subjectFilter = "Your Subject Here").
    • This way, you only collect failures related to that particular campaign or batch of emails.
  4. Extracts failed addresses
    • It looks inside the body of the bounce message and uses a pattern (RegEx) to find anything that looks like an email address.
    • If it finds one, it saves it.
    • If it doesn’t, it falls back to using the To or SenderEmailAddress fields.
  5. Skips duplicates
    • It uses a dictionary to make sure the same failed address isn’t added twice.
  6. Excludes certain domains
    • You can keep a list of domains you don’t care about (e.g., "exchangelabs.com", "example.com") and the script will skip any addresses ending with those domains.
  7. Optional: Marks the bounce email as read
    • There’s a variable (markAsRead) you can set to True or False.
    • If True, the script will mark each processed bounce message as “Read” so they don’t clutter your Inbox.
  8. Exports results to a CSV file
    • After collecting all the failed addresses, it writes them into a file called UnavailableEmails.csv on your Desktop.
    • The file has one column called FailedRecipient with all the addresses listed.
    • You can open this file directly in Excel.
  9. Shows a completion message
    • When it’s done, it pops up a message box telling you the export is complete and where the file is saved

VBA script

Sub ExportUnavailableEmails()
    Dim ns As Outlook.NameSpace
    Dim inbox As Outlook.Folder
    Dim items As Outlook.items
    Dim itm As Object
    Dim subjectFilter As String
    Dim failedAddresses As Object
    Dim filePath As String
    Dim fso As Object, ts As Object 

    ' === Configure ===
    ' change the subject
    subjectFilter = "URGENT INPUT REQUIRED"
    filePath = Environ("USERPROFILE") & "\Desktop\UnavailableEmails.csv"
 
     ' Set to True if you want to mark emails as read, False to leave them
    Dim markAsRead As Boolean
    maskAsRead = True  

    ' === Setup ===
    Set ns = Application.GetNamespace("MAPI")
    Set inbox = ns.GetDefaultFolder(olFolderInbox)
    Set items = inbox.items
    items.Sort "[ReceivedTime]", False

    Set failedAddresses = CreateObject("Scripting.Dictionary")

    ' === Define excluded domains ===
    Dim excludedDomains As Variant
    excludedDomains = Array("example.com", "testdomain.org")

    ' === Iterate items ===
    Dim i As Long
    For i = items.Count To 1 Step -1
        Set itm = items.item(i)
        If Not itm Is Nothing Then
            If itm.Class = olMail Or itm.Class = olReport Then
                If InStr(1, itm.Subject, "Undeliverable", vbTextCompare) > 0 _
                   Or InStr(1, itm.Subject, "Delivery Status Notification", vbTextCompare) > 0 Then
                   
                    If InStr(1, itm.Body, subjectFilter, vbTextCompare) > 0 Then
                        Dim emails As Collection
                        Set emails = ExtractEmailsFromText(itm.Body)
                    
                        Dim e As Variant
                        For Each e In emails
                            If Not IsExcludedDomain(e, excludedDomains) Then
                                If Not failedAddresses.Exists(LCase$(e)) Then
                                    failedAddresses.Add LCase$(e), True
                                End If
                            End If
                        Next e
                    End If

                    ' Mark as read if variable is True
                    If maskAsRead Then
                        itm.UnRead = False
                    End If
                End If
            End If
        End If
    Next i

    ' === Write CSV ===
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set ts = fso.CreateTextFile(filePath, True)
    ts.WriteLine "FailedRecipient"

    Dim key As Variant
    For Each key In failedAddresses.Keys
        ts.WriteLine key
    Next key
  
    ts.Close
    MsgBox "Export complete! Saved to: " & filePath
End Sub

' Helper: check if email ends with any excluded domain
Private Function IsExcludedDomain(ByVal email As String, ByVal domains As Variant) As Boolean
    Dim d As Variant
    Dim addr As String
    addr = LCase$(Trim$(email))

    For Each d In domains
        If Right$(addr, Len(d)) = LCase$(d) Then
            IsExcludedDomain = True
            Exit Function
        End If
    Next d
    IsExcludedDomain = False
End Function

' Helper: extract emails with RegEx
Private Function ExtractEmailsFromText(ByVal text As String) As Collection
    Dim re As Object, matches As Object, m As Object
    Set ExtractEmailsFromText = New Collection

    Set re = CreateObject("VBScript.RegExp")
    re.Pattern = "([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})"
    re.IgnoreCase = True
    re.Global = True
    If re.Test(text) Then
        Set matches = re.Execute(text)
        For Each m In matches
            ExtractEmailsFromText.Add m.Value
        Next m
    End If
End Function

Happy coding!

Related posts

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.