ChannelsAzure

Azure Event Grid events for channel deliveries

Which BlobCreated events fire when a channel delivers a file to your Azure storage account, and how to trigger Data Factory, Auto Loader, Snowpipe or your own Event Grid subscriber only on completed files.
Updated September 3, 2026

Many pipelines react to files arriving in Azure Storage by subscribing to the Microsoft.Storage.BlobCreated event - directly through an Event Grid subscription, or indirectly through Azure Data Factory storage event triggers, Databricks Auto Loader in file notification mode, or Snowflake Snowpipe auto-ingest. Which events your account emits, and when, depends on whether the storage account has hierarchical namespace (Data Lake Storage Gen2) enabled.

This guide covers files delivered by a channel's Azure Blob Storage destination. There is a companion guide for files written by a Simple FTP Service with an Azure backend - the event behaviour is the same, because both write the way Azure's own tooling expects.

You always pick the one Azure Blob Storage destination when building the channel - DocEvent detects whether the target account has hierarchical namespace enabled and delivers through the right API automatically. Nothing in the endpoint configuration selects this; it follows from the storage account itself.

Flat namespace accounts - one event per delivery

On a storage account without hierarchical namespace, the channel delivers through the Blob API and commits the file with a single block-list commit. Azure emits one BlobCreated event, at commit time, with data.api set to PutBlockList. When you receive it, the file is complete - there is nothing to filter.

Data Lake Storage Gen2 accounts - two events per delivery

On an account with hierarchical namespace enabled, the channel delivers through the Data Lake (DFS) API: the file is created, data is appended, and the file is finally flushed with close to mark it complete. Azure Event Grid only announces two of those stages - CreateFile and FlushWithClose - so one completed delivery produces two BlobCreated events:

  1. data.api: "CreateFile" - the file now exists in the namespace, with zero bytes of content
  2. data.api: "FlushWithClose" - the delivery is complete and the content is final

The CreateFile event cannot be suppressed - file creation is a namespace operation on Data Lake Storage, and Azure always announces it. The two-event pattern is the ecosystem convention: it is what the Spark/Hadoop ABFS driver used by Databricks, Synapse and Fabric produces too.

Act only on FlushWithClose

If your subscriber must only see completed files, filter on data.api. This is Microsoft's own recommendation, and the managed integrations already follow it: Data Factory storage event triggers, Auto Loader file notification mode and Snowpipe auto-ingest all ignore CreateFile and act on FlushWithClose.

For your own Event Grid subscription, add an advanced filter on data.api. Including the Blob API commit values as well makes one filter correct for both account kinds - and for files written to the same container by other tools:

"advancedFilters": [
  {
    "operatorType": "StringIn",
    "key": "data.api",
    "values": ["FlushWithClose", "PutBlockList", "PutBlob"]
  }
]

With this filter in place, every event you receive represents a file whose content is complete.

Interrupted deliveries never fire your trigger

This filtering also protects you from partial files. If a delivery is interrupted - a network failure mid-transfer, for example - no FlushWithClose (or PutBlockList) event fires for it: on a Data Lake account the only trace is the zero-byte CreateFile, and on a flat account nothing appears at all. The channel retries the delivery, and your subscriber fires exactly once, when a complete file is finally in place. A consumer that filters on the completion events therefore never processes a half-delivered file.

If delivered files never trigger downstream processing

If files land in your container but a FlushWithClose-filtered consumer never fires, first check the account kind - on a flat account the completion event is PutBlockList, so a filter listing only FlushWithClose matches nothing there. The combined filter above covers both. For the broader missing-close trap with other writers in your pipeline, see the companion guide.