Get push notifications for changes to your account data

You can use the Merchant Notifications API to get push notifications for changes to account data. For example, if you subscribe to product status change notifications, you can be notified in real time when a product is disapproved. You can subscribe to notifications for any of your sub-accounts or other linked accounts.

This guide provides examples of how to manage notifications for product status changes. You can use these examples to manage notifications for other events by changing the value of the registeredEvent field in your requests. For a full list of the event types, see the Merchant Notifications API reference.

Subscribe

To receive notifications, you need a callBackUri. Your callback URI must meet the following requirements:

  • Must be a publicly accessible HTTPS address with a valid SSL certificate, signed by a certificate authority.
  • Must accept HTTP POST requests with the Content-Typeheader and application/json value.
  • Must return one of the following response codes to acknowledge that the notification was received.
    • 102
    • 200
    • 201
    • 202
    • 204

You can use the same callback URI for multiple subscriptions. We recommend using a unique callback URI per advanced account, per event type to minimize the load of push requests to a single URI.

Here's a sample request to subscribe to notifications about product status changes for a specific merchant account.

POST https://merchantapi.googleapis.com/notifications/v1beta/accounts/MERCHANT_ID/notificationsubscriptions/
{
  "registeredEvent": "PRODUCT_STATUS_CHANGE",
  "targetAccount": "accounts/TARGETMERCHANT_ID",
  "callBackUri": "https://example.com"
}

Replace the following:

  • MERCHANT_ID: The unique identifier of the account that should receive the notifications.
  • TARGETMERCHANT_ID: The unique identifier of the account about which you want to receive notifications.

If your Merchant Center account is a standalone account with no linked accounts, and you want to receive notifications for your own account, replace both these variables with your account ID.

Successful calls return a name for your subscription, including a subscription ID:

{
  "name":"accounts/MERCHANT_ID/notificationsubscriptions/subscriptionId",
  "registeredEvent": "PRODUCT_STATUS_CHANGE",
  "targetAccount": "accounts/TARGETMERCHANT_ID",
  "callBackUri": "https://example.com"
}

You can use this name to GET and DELETE individual subscriptions.

You can subscribe to notifications for product status changes for all of your linked accounts by including "allManagedAccounts": trueinstead of a targetAccount in your request:

POST https://merchantapi.googleapis.com/notifications/v1beta/accounts/MERCHANT_ID/notificationsubscriptions/

{
  "registeredEvent": "PRODUCT_STATUS_CHANGE",
  "allManagedAccounts": true,
  "callBackUri": "https://example.com"
}

To update an existing subscription, use PATCH with an update_mask to specify the fields you want to update, and the new values in the JSON body:

PATCH https://merchantapi.googleapis.com/notifications/v1beta/accounts/MERCHANT_ID/notificationsubscriptions/SUBSCRIPTION_ID?update_mask=callBackUri

{
  "​callBackUri": "https://my-own-personal-domain.com"
}

Decode notifications

After you create a subscription, you receive notifications to the specified callBackUri in the following format:

{"message":{"data":"{base64_encoded_string}"}}

The notification data is encoded. You can decode the data to a readable text format to use in your implementation. Here's a sample spring boot controller you might use to process the encoded data:

@RestController
public class ExampleController {
@RequestMapping(value = "/push",
  method = RequestMethod.POST,
  consumes = {"application/json"},
  produces = {"text/plain"})
  @ResponseStatus(HttpStatus.OK)
  public void doStuff(@RequestBody String message) {
        //wrapped message
        JSONObject jsonObject = new JSONObject(message);
        JSONObject jsonMessage = jsonObject.getJSONObject("message");
        message = jsonMessage.getString("data");
        byte[] decodedBytes = Base64.getDecoder().decode(message);
        String decodedMessage = new String(decodedBytes);
        // Implement your business logic here
  }
}

Here's an example of a base64_encoded_string that has been decoded:

{
  "account": "accounts/TARGETMERCHANT_ID",
  "managingAccount": "accounts/MERCHANT_ID",
  "resourceType": "PRODUCT",
  "attribute": "STATUS",
  "changes": [{
    "oldValue": "approved",
    "newValue": "disapproved",
    "regionCode": "US",
    "reportingContext": "SHOPPING_ADS"
  }, {
    "oldValue": "approved",
    "newValue": "disapproved",
    "regionCode": "JP",
    "reportingContext": "SHOPPING_ADS"
  },{
    "oldValue": "approved",
    "newValue": "disapproved",
    "regionCode": "GE",
    "reportingContext": "SHOPPING_ADS"
  }],
  "resourceId": "ONLINE~en~US~1234",
  "resource": "accounts/TARGETMERCHANT_ID/products/ONLINE~en~US~1234",
  "expirationTime": "2024-10-22T02:43:47.461464Z",
  "eventTime": "2024-03-21T02:43:47.461464Z"
}

If there's no oldValue field in the notification, a new product was added to your account. If there's no newValue field, the product was deleted from your account.

The expirationTime field won't exist in case the product was deleted.

The reportingContext field supports only (SHOPPING_ADS, LOCAL_INVENTORY_ADS, YOUTUBE_SHOPPING, YOUTUBE_CHECKOUT, YOUTUBE_AFFILIATE) from the enum value ReportingContextEnum.

For product status change event, oldValue and newValue fields will have one of these values : (approved, pending, disapproved, ``).

The eventTime field holds the creation time of the event itself, if you want to do message ordering you should rely on the value in that field and don't rely on the order of receiving the messages.

Follow the ProductStatusChangeMessage format for more details.

Test your implementation

Here's a sample notification you can use to test your callback URI and decoding:

curl --header "Content-Type: application/json" --header "Accept: text/plain" --request POST --data '{"message":{"data":
"ewogICJhY2NvdW50IjogImFjY291bnRzLzEyMzQiLAogICJtYW5hZ2luZ0FjY291bnQiOiAiYWNjb3VudHMvNTY3OCIsCiAgInJlc291cmNlVHlwZSI6ICJQUk9EVUNUIiwKICAiYXR0cmlidXRlIjogIlNUQVRVUyIsCiAgImNoYW5nZXMiOiBbewogICAgIm9sZFZhbHVlIjogImFwcHJvdmVkIiwKICAgICJyZWdpb25Db2RlIjogIlVTIiwKICAgICJyZXBvcnRpbmdDb250ZXh0IjogIlNIT1BQSU5HX0FEUyIKICB9XSwKICAicmVzb3VyY2VJZCI6ICJPTkxJTkV+ZW5+VVN+MDAwMDAwMDAwMDAwIiwKICAicmVzb3VyY2UiOiAiYWNjb3VudHMvMTIzNC9wcm9kdWN0cy9PTkxJTkV+ZW5+VVN+MDAwMDAwMDAwMDAwIiwKICAiZXhwaXJhdGlvblRpbWUiOiAiMjAyNC0xMC0yMlQwMjo0Mzo0Ny40NjE0NjRaIiwKICAiZXZlbnRUaW1lIjogIjIwMjQtMDMtMjFUMDI6NDM6NDcuNDYxNDY0WiIKfQ=="}}' https://{callBackUri}

In response to this call, your callback URI should return a successful response code. The decoded message should have the following value:

{
  "account": "accounts/1234",
  "managingAccount": "accounts/5678",
  "resourceType": "PRODUCT",
  "attribute": "STATUS",
  "changes": [{
    "oldValue": "approved",
    "regionCode": "US",
    "reportingContext": "SHOPPING_ADS"
  }],
  "resourceId": "ONLINE~en~US~000000000000",
  "resource": "accounts/1234/products/ONLINE~en~US~000000000000",
  "expirationTime": "2024-10-22T02:43:47.461464Z",
  "eventTime": "2024-03-21T02:43:47.461464Z"
}