Create and Filter Notification Subscriptions

Use the Notifications API to create and filter subscriptions with CEL-based filter expressions.

Learn how to use the Notifications API to create subscriptions with filter expressions that control which event notifications you receive. The filterExpression field accepts a Common Expression Language (CEL) expression that evaluates against each event's payload and returns only the events that evaluate to true.

When an event is generated, the filter expression is evaluated against the event payload, and only events that satisfy the filter criteria are forwarded to your endpoint.

Prerequisites

To complete this tutorial, you need:

Eligible notification types

The following notification types support filterExpression:

Notification typefilterExpression supported
ACCOUNT_STATUS_CHANGEDYes
ANY_OFFER_CHANGEDYes
B2B_ANY_OFFER_CHANGEDYes
DETAIL_PAGE_TRAFFIC_EVENTYes
EXTERNAL_FULFILLMENT_SHIPMENT_STATUS_CHANGEYes
FBA_INVENTORY_AVAILABILITY_CHANGESYes
FBA_OUTBOUND_SHIPMENT_STATUSYes
FEED_PROCESSING_FINISHEDYes
FEE_PROMOTIONYes
FULFILLMENT_ORDER_STATUSYes
ITEM_INVENTORY_EVENT_CHANGEYes
ITEM_SALES_EVENT_CHANGEYes
ORDER_CHANGEYes
PRICING_HEALTHYes
REPORT_PROCESSING_FINISHEDYes
TRANSACTION_UPDATEYes
LISTINGS_ITEM_ISSUES_CHANGENo
LISTINGS_ITEM_MFN_QUANTITY_CHANGENo
LISTINGS_ITEM_STATUS_CHANGENo
PRODUCT_TYPE_DEFINITIONS_CHANGENo
BRANDED_ITEM_CONTENT_CHANGENo
ITEM_PRODUCT_TYPE_CHANGENo

📘

Note

You cannot use filterExpression and eventFilter together. Use one or the other. Use filter expressions to replace existing eventFilter conditions under processingDirective. If you need both aggregation and filtering, continue to use eventFilter.

Supported CEL operators

The following table lists the CEL operators that you can use in a filterExpression:

Operator typeSupportedNot supported
Logical! (NOT), && (AND), || (OR), ? : (ternary)N/A
Comparison==, !=, <, <=, >, >=, inN/A
Comprehension macroshas(e.f), e.all(x, p), e.exists(x, p), e.exists_one(x, p)e.filter(x, p), e.map(x, t)
String functionscontains(), startsWith(), endsWith(), matches(), size()N/A
List/Map operationsin, size()[] (indexing)
ArithmeticN/A+, -, *, /, %

Step 1. Identify the notification payload fields

Before you create a filter expression, review the payload structure of the notification type you want to filter.

📘

Note

Filter expressions can only reference fields within the notification payload. The filter returns only events that evaluate to true. You can write filter expressions on optional fields. However, you must check whether the field is present before you use it, or the filter fails at runtime. Use the has() macro for field presence checks.

Step 2. Write the CEL filter expression

Write a CEL expression that targets the payload fields you identified in Step 1.

Filtering on optional fields with has()

Some notification payloads contain optional fields that are only present under certain conditions. If your filterExpression references a field that is absent from the payload, the filter evaluation fails at runtime and the event is dropped. To safely filter on optional fields, use the has() macro to check for field presence before accessing the field.

📘

Note

If has() is not used as in the example below, the request will fail when FulfillmentShipment is not present:

FulfillmentOrderStatusNotification.FulfillmentShipment.FulfillmentShipmentStatus == "Shipped"

The FulfillmentShipment field in the FULFILLMENT_ORDER_STATUS notification is optional. It is only present when the notification contains shipment information. If you want to filter on a field inside FulfillmentShipment, you must first check that it exists.

has(FulfillmentOrderStatusNotification.FulfillmentShipment) && FulfillmentOrderStatusNotification.FulfillmentShipment.FulfillmentShipmentStatus == "Shipped"

Step 3. Create the subscription with a filter expression

Call the createSubscription operation and include the filterExpression field in the processingDirective object.

The following examples show sample notification payloads and the corresponding createSubscription requests with filterExpression.

FULFILLMENT_ORDER_STATUS

{
    "NotificationVersion": "1.0",
    "NotificationType": "FULFILLMENT_ORDER_STATUS",
    "PayloadVersion": "1.0",
    "EventTime": "2020-01-11T00:09:53.109Z",
    "Payload": {
        "FulfillmentOrderStatusNotification": {
            "SellerId": "A3TH9S8BH6GOGM",
            "EventType": "Shipment",
            "StatusUpdatedDateTime": "2020-01-11T00:09:53.109Z",
            "SellerFulfillmentOrderId": "ext-order-id-12345",
            "FulfillmentOrderStatus": "Complete",
            "FulfillmentShipment": {
                "FulfillmentShipmentStatus": "Shipped",
                "AmazonShipmentId": "DZRSmwG2N",
                "EstimatedArrivalDateTime": "2020-01-14T22:59:59Z",
                "FulfillmentShipmentPackages": [{
                    "PackageNumber": 1,
                    "CarrierCode": "UPS",
                    "TrackingNumber": "1Z999AA10123456784"
                }]
            }
        }
    },
    "NotificationMetadata": {
        "ApplicationId": "amzn1.sellerapps.app.f1234566-aaec-55a6-b123-bcb752069ec5",
        "SubscriptionId": "7d78cc50-95c8-4641-add7-10af4b1fedc9",
        "PublishTime": "2020-01-11T00:02:50.501Z",
        "NotificationId": "2012e8e5-b365-4cb1-9fd8-be9dfc6d5eaf"
    }
}

Sample createSubscription request with filterExpression

{
    "payloadVersion": "1.0",
    "destinationId": "sampleId",
    "processingDirective": {
        "filterExpression": "FulfillmentOrderStatusNotification.EventType == \"ORDER\" && FulfillmentOrderStatusNotification.FulfillmentOrderStatus == \"COMPLETE\""
    }
}

REPORT_PROCESSING_FINISHED

{
    "notificationVersion": "2020-09-04",
    "notificationType": "REPORT_PROCESSING_FINISHED",
    "payloadVersion": "2020-09-04",
    "eventTime": "2020-07-14T03:35:13.214Z",
    "payload": {
        "reportProcessingFinishedNotification": {
            "sellerId": "A3TH9S8BH6GOGM",
            "accountId": "amzn1.merchant.o.A3TH9S8BH6GOGM",
            "reportId": "54517018502",
            "reportType": "GET_FLAT_FILE_ACTIONABLE_ORDER_DATA",
            "processingStatus": "CANCELLED",
            "reportDocumentId": "amzn1.tortuga.3.edbcd0d8-3434-8222-1234-52ad8ade1208.REP4567URI9BMZ"
        }
    },
    "notificationMetadata": {
        "applicationId": "amzn1.sellerapps.app.aaccczf-4455-4b7c-4422-664ecacdd336",
        "subscriptionId": "subscription-id-d0e9e693-c3ad-4373-979f-ed4ec98dd746",
        "publishTime": "2020-07-13T19:45:04.284Z",
        "notificationId": "d0e9e693-c3ad-4373-979f-ed4ec98dd746"
    }
}

Sample createSubscription request with filterExpression

{
    "payloadVersion": "1.0",
    "destinationId": "sampleId",
    "processingDirective": {
        "filterExpression": "reportProcessingFinishedNotification.processingStatus == \"CANCELLED\" && reportProcessingFinishedNotification.reportType == \"GET_FLAT_FILE_ACTIONABLE_ORDER_DATA\""
    }
}

Migrate existing subscriptions to filter expressions

This section explains how to add filtering to your existing subscriptions and how to migrate from eventFilter to filterExpression.

❗️

Important

  • You cannot update an existing subscription to add a filterExpression. You must delete the existing subscription and create a new one.
  • You cannot use eventFilter and filterExpression together in the same subscription.
  • Existing subscriptions without a filterExpression continue to receive all events. There is no change to current behavior unless you explicitly create a new subscription with a filterExpression.

Migration approach

To avoid missing events during migration, run your new filtered subscription in parallel with your existing subscription before switching over.

  1. Create a new subscription with filterExpression alongside your existing subscription (use a separate destination or the same destination if your processor can handle both).

  2. Receive and process filtered notifications in parallel but do not use them for business logic yet.

  3. Confirm that filtered notifications match expectations: correct events are delivered, no unexpected gaps, and your processor handles them correctly. Run for 1–2 weeks.

  4. Once validated, delete your old subscription using the deleteSubscription operation.

📘

Note

If you cannot run two subscriptions in parallel, delete the old subscription and create the new one. Be aware there may be a brief gap in notifications during the switch.

Scenario 1: Add filtering to an existing subscription (no prior filtering)

Use this approach when you currently receive all events for a notification type and want to filter to only specific events.

Use case: You subscribe to ANY_OFFER_CHANGED and receive notifications for all offer changes. You want to receive only notifications triggered by a Featured Offer change.

Sample ANY_OFFER_CHANGED notification payload

{
    "NotificationVersion": "1.0",
    "NotificationType": "ANY_OFFER_CHANGED",
    "PayloadVersion": "1.0",
    "EventTime": "2020-07-13T19:42:04.284Z",
    "Payload": {
        "AnyOfferChangedNotification": {
            "SellerId": "A3TH9S8BH6GOGM",
            "OfferChangeTrigger": {
                "MarketplaceId": "ATVPDKIKX0DER",
                "ASIN": "B08N5WRWNW",
                "ItemCondition": "New",
                "TimeOfOfferChange": "2020-07-13T19:42:04.284Z",
                "OfferChangeType": "FeaturedOffer"
            },
            "Summary": {
                "NumberOfOffers": [
                    {
                        "Condition": "new",
                        "FulfillmentChannel": "Merchant",
                        "OfferCount": 10
                    }
                ],
                "TotalBuyBoxEligibleOffers": 100
            },
            "Offers": [
                {
                    "SellerId": "A3TH9S8BH6GOGM",
                    "SubCondition": "New",
                    "IsFulfilledByAmazon": true,
                    "IsFeaturedMerchant": true
                }
            ]
        }
    },
    "NotificationMetadata": {
        "ApplicationId": "amzn1.sellerapps.app.f1234566-aaec-55a6-b123-bcb752069ec5",
        "SubscriptionId": "7d78cc50-95c8-4641-add7-10af4b1fedc9",
        "PublishTime": "2020-07-13T19:42:04.284Z",
        "NotificationId": "d0e9e693-c3ad-4373-979f-ed4ec98dd746"
    }
}
Step 1. Create a new subscription with a filter expression

Call the createSubscription operation with a filterExpression in the processingDirective.

{
    "payloadVersion": "your-payload-version",
    "destinationId": "your-destination-id",
    "processingDirective": {
        "filterExpression": "AnyOfferChangedNotification.OfferChangeTrigger.OfferChangeType == \"FeaturedOffer\""
    }
}
Step 2. Run in shadow mode and validate

Run your new filtered subscription in parallel with your existing unfiltered subscription. Process the filtered notifications but do not use them for business logic yet. Confirm that the expected events are delivered, no events are unexpectedly missing, and your processor handles the filtered payload correctly.

Step 3. Delete your old subscription

After validation is complete, call the deleteSubscription operation to remove your old subscription.

DELETE /notifications/v1/subscriptions/ANY_OFFER_CHANGED

You now receive ANY_OFFER_CHANGED notifications only when the offer change is triggered by a Featured Offer change. All other offer change types (Internal, External) are filtered out.

Scenario 2: Migrate from eventFilter to filterExpression (ANY_OFFER_CHANGED)

Use this approach when you currently use eventFilter and want to migrate to filterExpression for more flexible filtering.

Use case: You currently use eventFilter to filter ANY_OFFER_CHANGED notifications to a specific marketplace. You want to migrate to filterExpression to also filter by offer change type.

Your current subscription (with eventFilter):

{
    "payloadVersion": "your-payload-version",
    "destinationId": "your-destination-id",
    "processingDirective": {
        "eventFilter": {
            "eventFilterType": "ANY_OFFER_CHANGED",
            "marketplaceIds": ["ATVPDKIKX0DER"]
        }
    }
}
Step 1. Create a new subscription with filterExpression

Convert your eventFilter marketplace filter to a CEL expression:

{
    "payloadVersion": "your-payload-version",
    "destinationId": "your-destination-id",
    "processingDirective": {
        "filterExpression": "AnyOfferChangedNotification.OfferChangeTrigger.MarketplaceId == \"ATVPDKIKX0DER\""
    }
}

To combine marketplace filtering with offer change type filtering:

{
    "payloadVersion": "your-payload-version",
    "destinationId": "your-destination-id",
    "processingDirective": {
        "filterExpression": "AnyOfferChangedNotification.OfferChangeTrigger.MarketplaceId == \"ATVPDKIKX0DER\" && AnyOfferChangedNotification.OfferChangeTrigger.OfferChangeType == \"FeaturedOffer\""
    }
}
Step 2. Run in shadow mode and validate

Run your new filtered subscription in parallel with your existing eventFilter subscription. Process the filtered notifications but do not use them for business logic yet. Confirm that the expected events are delivered, no events are unexpectedly missing, and your processor handles the filtered payload correctly.

Step 3. Delete your old subscription

After validation is complete, delete your old subscription:

DELETE /notifications/v1/subscriptions/ANY_OFFER_CHANGED

You now receive ANY_OFFER_CHANGED notifications only for the US marketplace (ATVPDKIKX0DER) where the change was triggered by a Featured Offer update.

📘

Note

If you currently rely on aggregationSettings in your eventFilter (for example, to limit notification frequency), you cannot replicate this behavior with filterExpression. Aggregation is only available with eventFilter. If you need both aggregation and filtering, continue to use eventFilter.