Subscribe to Events

📘

Buy with Prime API is now available for early access

Sign up for early access to the Buy with Prime API using the 'Sign Up' button below. The API may change as Amazon receives feedback and iterates on it.

Buy with Prime business products publish events when the state of underlying resources change. For example, a RETURN_STARTED event is published when a shopper starts a return, a PACKAGE_DELIVERED event is published when one or more items are delivered to their destination, and so on. You can subscribe to specific types of events and then in your application, you can react when those events occur. For a full list of events, see Event Types.

You subscribe to events by using the Buy with Prime API Event interface. For instructions on how to subscribe to events, see Steps to Subscribe to Buy with Prime Events.

This topic describes event concepts, the structure of Buy with Prime events, and how to process events.

Prerequisites

  • To have events delivered to an Amazon EventBridge event destination, you must have an Amazon Web Services (AWS) account. To create an AWS account, go to the Amazon Web Services home page and then click Create an AWS account.
  • To have events delivered to a webhook endpoint, you must have an HTTPS endpoint that accepts webhook events.

Concepts

The following sections describe event concepts.

Event

A change in a Buy with Prime resource as a result of certain actions. Examples of actions that trigger events include when a customer requests a return, cancels an item, and so on. The term event also refers to the notifications that you can subscribe to receive when such a change occurs.

Each event has a type. Examples of event types are RETURN_STARTED, ITEM_DELIVERED, and so on. The event type is specified in the detail-type field within the event schema. For a full list of Buy with Prime event types, see Event Types.

Thin event

An event that contains minimal information. All Buy with Prime events are thin events. After receiving a thin event, you are typically expected to call the Buy with Prime API to request further information. For example, a REFUND_REQUESTED event specifies the order ID and refund ID, but you must make an API call for further information about the refund.

Resource

The entity, such as a catalog or order, to which an event pertains. Each Buy with Prime event contains a resources array that contains resource identifiers that you can use to make an API call to retrieve more data.

Amazon EventBridge

A serverless event bus that you can configure to receive events from services. In this case, Buy with Prime is the service that publishes the events. With Amazon EventBridge, you set up routing rules to determine where to send the data. For details, see What is Amazon Event Bridge.

Webhook

An HTTPS request that includes an event to be delivered to a specified HTTPS endpoint.

Webhook endpoint

A URL that is configured to receive webhook HTTPS requests.

Partner event source

An entity in Amazon EventBridge that you can associate with a target to receive and process events. When you subscribe to Buy with Prime events, Buy with Prime creates a partner event source for you, which you can then associate with an event bus. For details about partner event sources, see Receiving events from a SaaS partner with Amazon EventBridge.

The Buy with Prime API refers to the partner event source as an event destination.

Business product

A specifically managed selling experience, often a sales channel or store.

Dead letter queue

In Amazon EventBridge, a type of queue that temporarily stores events and messages that weren’t processed due to system errors. You can optionally create a dead letter queue for Buy with Prime events. For details, see Event retry policy and using dead-letter queues.

Event delivery options

You can receive notifications for Buy with Prime events at a webhook endpoint or through Amazon EventBridge.

Amazon EventBridge partner events

Buy with Prime events delivered to a partner event source by Amazon EventBridge are in the following JSON format.

Example event
{
  "version": "0", 
  "id": "example-event-id", 
  "detail-type": "RETURN_COMPLETED", 
  "source": "aws.partner/buywithprime/partner-event-source-name",
  "account": "example-aws-account-id", 
  "time": "2023-10-27T12:34:56Z", 
  "region": "us-east-1", 
  "resources": [
    "businessProduct/example-business-product-id/order/example-order-id/return/example-return-id",
  ], 
  "detail": {}
}
Event fields
KeyData TypeDescription
versionStringAmazon EventBridge event version.
idStringAmazon EventBridge-generated UUID for an event.
detail-typeStringType schema for the detail of the event. Examples include ITEM_CANCELLED, RETURN_STARTED, and so on. For a list of Buy with Prime event types, see Event Types .
sourceStringName of the partner event source in Amazon EventBridge.
accountStringAWS account ID that hosts the partner event source.
timeStringISO 8601 timestamp that indicates when the event was published.
regionStringAWS region from which the event is published.
resourcesArray of stringsArray of identifiers for the resources that triggered the event, in key-value pair format. Each resource identifier is a string that starts with businessProduct/{businessProductId}/, followed by resource types (keys) and resource IDs (values) separated by slashes. For details about how to interpret resources, see How to process events.
detailObjectJSON object that contains details about the event. For most events, this object is empty, and you must call the Buy with Prime API to find further information about the resource(s) specified in the resources array.

Webhook events

Buy with Prime events delivered to a webhook endpoint are in the following format.

Example event header
"x-amzn-signature": "MGUCMQDjXT1/u2yl2xQyXvWHMpxkD3CmdTZhlNC6FtUM/lBF/fcRGm9WqCx0XqihRtRAw4YCMBDsAiXNFMt9WbSR8tIY+fJq8Cch1SuonYyfc1Kboq4OmbnUOYI2tQq1YvVDugNyWg=="
"x-amzn-kid": "webhooks-7euYmT9H9aEeQ5kYfqrG1IzzT247/iV003IgbXDnPtSpw="
Event header fields
KeyData TypeDescription
x-amzn-signatureStringA digital signature you can use to verify that the event originated from Buy with Prime and that it wasn’t tampered with in transit. For details, see Verify that webhook events originated from Buy with Prime.
x-amzn-kidStringThe key ID for the public key that you can use to retrieve the public key used for signature validation. For details, see Verify that webhook events originated from Buy with Prime.

Buy with Prime business products use Amazon EventBridge to deliver these events to your webhook endpoint, so they also include the default headers described in Headers in requests to API destinations in the Amazon EventBridge User Guide.

Example event body
{
    "idempotencyKey": "MzY0MWI0NjMtNWU2Yi00ZGJlLTk5MmYtMjlhYmUwMmE5YTEyI2NkM2M0MTQ1LWZkZjgtNWQ1Ny03NmMzLTU3YjEyZGZlNzc1NQ=="
    "eventDescriptor": "INVENTORY_CHANGED",
    "resources": ["businessProduct/bp-123/inventory-item/ITEM-ID"],
    "eventId": "cd3c4145-fdf8-5d57-76c3-57b12dfe7755",
    "apiVersion": "2024-01-01",
    "subscriptionId": "3641b463-5e6b-4dbe-992f-29abe02a9a12",
    "eventTime": "2024-07-10T19:51:05Z",
    "data": {}
}
Event body fields
KeyData TypeDescription
idempotencyKeyStringA string used to detect duplicate events.
eventDescriptorStringThe event type you set when you started the subscription, for example DELIVERY_CANCELLED. For a list of possible event descriptors, see Event Types.
resourcesArray of stringsAn array of identifiers for the resources that triggered the event, in key-value pair format. Each resource identifier is a string that starts with businessProduct/{businessProductId}/, followed by resource types (keys) and resource IDs (values) separated by slashes. For details, see How to handle events.
eventIdStringA UUID for an event. If you have multiple subscriptions for the same event that are delivered to different destinations, this ID remains the same.
apiVersionStringThe version of the Buy with Prime API that the subscription is for.
subscriptionIdStringA unique identifier for an event subscription.
eventTimeStringA timestamp in ISO 8601 format that indicates when the event happened.
dataObjectA JSON object that contains details about the event. If the object is empty, call the Buy with Prime API using the apiVersion and resources to find further information.

Webhook event delivery retry strategy

An event delivery is retried if:

  • The webhook endpoint times out without a 200 response code. If the endpoint takes longer than 5 seconds to respond, the request times out.
  • The webhook endpoint responds with an HTTP 409 (conflict), 429 (too many requests), or 5xx (server error) code.

By default, retries use an exponential backoff strategy.

  • To set a longer duration to wait before retrying, in the response from your webhook endpoint, set the duration in the Retry-After HTTP header. The retry strategy then uses the longer duration.
  • To stop retries of a particular event, in the response from your webhook endpoint, set the Retry-After HTTP header to a negative number.

An event delivery stops retrying if any of the following are true:

  • The number of retry attempts is more than 185.
  • The event happened more than 24 hours ago.
  • The webhook endpoint returns an HTTP code other than 409, 429, or 5xx.
  • The webhook endpoint returns a negative Retry-After header value.

📘

Important

If the webhook endpoint returns a 401 or 407 HTTP response code, your event subscriptions associated with that endpoint will be deactivated. To reactivate, reach out to your solutions architect.

Filter out duplicate webhook events

If you receive duplicate event notifications due to retries, you can filter them out by using the idempotencyKey field.

Verify that webhook events originated from Buy with Prime

Every request from Buy With Prime to your webhook endpoint includes a digital signature in the x-amzn-signature header. You can use the signature to verify that the request originated from Buy With Prime and wasn’t tampered with in transit.

The signature is created using an asymmetric key pair and the ECDSA_SHA_384 algorithm. The public key is stored in a JSON Web Key Set (JWKS) at https://api.buywithprime.amazon.com/jwks.json. When you request this URL, you receive a list of JSON objects, each representing a JSON Web Key (JWK). To optimize performance and reduce the need for repeated HTTP requests, we recommend that you cache the public key.

To validate the signature of an event, check your cache for the public key corresponding to the kid. Verify that the x-amzn-kid received in the event header matches the cached kid. If it matches, use the stored public key. If it doesn't match, fetch a new public key from the JWKS endpoint.

Sample JWKS response:

{
    "keys": [{
        "kty": "EC",
        "crv": "P-384",
        "x": "7waNN4Z1WsxA-xB3HJQlWCZLMEXUM8HW5IaB61jjYsxDWcM7n6pajKgWX5cEtCT2",
        "y": "m44gzdkX65u5A0IPKbjAQUsB3BBes1HFebJHouDV8LVsTDSWmt4cNhrrVMNhQUUt",
        "kid": "webhooks-7euYmT9H9aEeQ5kYfqrG1IzzT247/iV0IgbXDnPtSpw=",
        "use": "sig",
        "alg": "ES384"
    }]
}

The following examples show how to fetch a key and validate the signature.

Example: Fetch the key and validate the signature (Java)
// Required libraries
`import`` java``.``io``.``IOException``;`
`import`` java``.``net``.``URL``;`
import java.nio.charset.StandardCharsets;
import java.security.Signature;
import java.security.interfaces.ECPublicKey;
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.ECKey;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.Base64;
import java.util.HashMap;
import java.util.Map;


/**
 * This class provides utility methods for verifying webhook signatures using JSON Web Keys (JWKs).
 */
public class WebhookSignatureVerifierUtility {

    private static final String JWKS_URL = "https://api.buywithprime.amazon.com/jwks.json"; // URL to fetch the JSON Web Key Set (JWKS)
    private static final Map<String, String> kidToPublicKeyMap = new HashMap<>(); // Map to cache public keys, reducing the need for repeated JWKS fetches

    /**
     * Retrieves the JWK (JSON Web Key) for the given key ID (kid) from the JWKS endpoint.
     *
     * @param kid The key ID (kid) of the JWK to retrieve
     * @return The JWK as a JsonNode
     * @throws IOException If an error occurs while fetching the JWK
     */
    private static JsonNode getJwkByKid(String kid) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode jwks = objectMapper.readTree(new URL(JWKS_URL));

        JsonNode keys = jwks.get("keys");
        for (JsonNode key : keys) {
            if (kid.equals(key.get("kid").asText())) {
                return key;
            }
        }

        throw new `RuntimeException`("Failed to fetch JWKS");
    }

    /**
     * Verifies the signature of a webhook payload using the provided public key and signature.
     *
     * `@param publicKeyJwk The JWK public key as a JSON string`
     * @param webhookPayload The webhook payload
     * @param webhookSignature The base64-encoded signature of the webhook payload
     * @throws Exception If an error occurs during signature verification
     */
    private static void isWebhookSignatureValid(String `publicKeyJwk`, String webhookPayload, String webhookSignature) throws Exception {
        JWK jwk = JWK.parse(publicKeyJwk);
        ECPublicKey publicKey = ((ECKey) jwk).toECPublicKey();

        // Encode the webhook payload as a byte array
        final byte[] webhookPayloadBytes = webhookPayload.getBytes(StandardCharsets.UTF_8);

        // Base64 decode the webhook signature into a byte array
        final byte[] webhookSignatureBytes = Base64.getDecoder().decode(webhookSignature);

        final Signature signature = Signature.getInstance("SHA384withECDSA");
        signature.initVerify(publicKey);
        signature.update(webhookPayloadBytes);

       if (!signature.verify(webhookSignatureBytes)) {
            throw new RuntimeException("Invalid Signature");
       }
    }

    /**
     * Utility function to verify the signature of a webhook payload.
     *
     * @param kid The key ID (kid) of the JWK
     * @param webhookPayload The webhook payload
     * @param webhookSignature The base64-encoded signature of the webhook payload
     * @throws Exception If an error occurs during signature verification
     */
    public static void verifySignature(String kid, String webhookPayload, String webhookSignature) throws Exception {
        // Fetching of the key from JWKS end-point
        String publicKeyJwk;
        if (kidToPublicKeyMap.containsKey(kid)) {
            publicKeyJwk = kidToPublicKeyMap.get(kid);
            System.out.printf("JWK found for kid: %s is %s%n", kid, publicKeyJwk);
        } else {
            JsonNode jwkNode = getJwkByKid(kid);
            publicKeyJwk = jwkNode.toString();
            kidToPublicKeyMap.put(kid, publicKeyJwk);
            System.out.printf("JWK found for kid: %s is %s%n", kid, publicKeyJwk);
        }
        isWebhookSignatureValid(publicKeyJwk, webhookPayload, webhookSignature);
    }

    public static void main(String[] args) {
        // KeyId
        String kid = "webhooks-7euYmT9H9aEeQ5kYfqrG1IzzT247/iV0IgbXDnPtSpw="; // received in event header x-amzn-kid
        // The received webhook payload/body
        final String webhookPayload = "{\"idempotencyKey\":\"MzE3M2YxNTQtZjc1ZS00ZDcxLTg5ZWQtNjI2NTcwMzc2ODk0IzU2Yjg4ZTRmLTk2NTgtYWRmOC1mMWZhLTQ1MjY2MjA0Mjk4Yg==\",\"eventDescriptor\":\"ITEM_IN_TRANSIT\",\"resources\":[\"businessProduct/bp-test-id/order/order_id/delivery/id\"],\"eventId\":\"56b88e4f-9658-adf8-f1fa-45266204298b\",\"apiVersion\":\"2024-01-01\",\"subscriptionId\":\"3173f154-f75e-4d71-89ed-626570376894\",\"eventTime\":\"2024-07-19T15:48:28Z\",\"data\":{}}";
        // The header received in the webhook from the x-amzn-signature key is base64-encoded
        final String webhookSignature = "MGQCMEzt1Ei6H9qtalFokD7uqTKF43tYGdBev84NNO8jB5+JTsJwN5MUiF66m4lUtn5bkQIwYZKwQhRgPTIGDtiDXsBmQbe0gPJZXdmzq/dJxOM34By+98Yhhpj4wVSTGI7V4YkI";
        try {
            verifySignature(kid, webhookPayload, webhookSignature);
        } catch (Exception e) {
            throw new RuntimeException("Error validating signature", e);
        }
    }
}
Example: Fetch the key and validate the signature (Javascript)
// Required libraries
const axios = require('axios');
const crypto = require('crypto');

// URL to fetch the JSON Web Key Set (JWKS)
const JWKS_URL = 'https://api.buywithprime.amazon.com/jwks.json';

// Map to cache public keys, reducing the need for repeated JWKS fetches
const kidToPublicKeyMap = new Map();


/**
 * Fetches the JSON Web Key (JWK) for a given key ID (kid) from the JWKS endpoint.
 *
 * @param {string} kid - The key ID to look for in the JWKS
 * @returns {Promise<Object>} - The JWK object corresponding to the given kid
 * @throws {Error} If no matching key is found or if there's an error fetching the JWKS
 */
async function getJwkByKid(kid) {
    try {
        // Fetch the JWKS from the specified URL
        const response = await axios.get(JWKS_URL);
        const jwks = response.data;

        if (jwks && jwks.keys) {
            return jwks.keys.find(key => key.kid === kid);
        }
        throw new Error('Failed to fetch JWKS');
    } catch (error) {
        console.error('Error fetching JWKS:', error.message);
        throw error;
    }
}

/**
 * Verifies the signature of the webhook payload using a JWK public key.
 *
 * @param {Object} publicKeyJWK - The JWK-formatted public key object.
 * @param {string} webhookPayload - The webhook payload string.
 * @param {string} webhookSignature - The base64-encoded signature string.
 */
function isWebhookSignatureValid(publicKeyJWK, webhookPayload, webhookSignature) {
    const webhookPayloadBuffer = Buffer.from(webhookPayload, 'utf8'); // encode the webhook payload string in binary
    const webhookSignatureBuffer = Buffer.from(webhookSignature, 'base64'); // decode the base64-encoded signature then encode it in binary
    const publicKey = loadPublicKey(publicKeyJWK);

    const verifier = crypto.createVerify('sha384'); // https://nodejs.org/api/crypto.html#cryptocreateverifyalgorithm-options
    verifier.update(webhookPayloadBuffer); // https://nodejs.org/api/crypto.html#verifyupdatedata-inputencoding

    // https://nodejs.org/api/crypto.html#verifyverifyobject-signature-signatureencoding
    if (!verifier.verify(publicKey, webhookSignatureBuffer)) {
        throw new Error(`Invalid Signature`);
    }
}

/**
 * Loads the public key from a JWK-formatted object.
 *
 * @param {Object} publicKeyJWK - The JWK-formatted public key object.
 * @returns {crypto.KeyObject} - The public key object.
 */
function loadPublicKey(publicKeyJWK) {
    return crypto.createPublicKey({key: publicKeyJWK, format: 'jwk'});
}

/**
 * Utility function to verify the signature of a webhook payload.
 *
 * @param {string} kid - The key ID (kid) of the JWK
 * @param {string} webhookPayload - The webhook payload
 * @param {string} webhookSignature - The base64-encoded signature of the webhook payload
 */
async function verifySignature(kid, webhookPayload, webhookSignature) {
    let publicKeyJwk;
    if (kidToPublicKeyMap.has(kid)) {
        publicKeyJwk = kidToPublicKeyMap.get(kid);
        console.log(`JWK found for kid: ${kid} is ${JSON.stringify(publicKeyJwk)}`);
    } else {
        const jwkNode = await getJwkByKid(kid);
        if (jwkNode) {
            publicKeyJwk = jwkNode;
            kidToPublicKeyMap.set(kid, publicKeyJwk);
            console.log(`JWK found for kid: ${kid} is ${JSON.stringify(publicKeyJwk)}`);
        } else {
            throw new Error(`No JWK found for kid: ${kid}`);
        }
    }
    return isWebhookSignatureValid(publicKeyJwk, webhookPayload, webhookSignature);
}

// Main function
async function main() {
    const kid = 'webhooks-7euYmT9H9aEeQ5kYfqrG1IzzT247/iV0IgbXDnPtSpw='; // received in event header x-amzn-kid
    // The received webhook payload/body
    const webhookPayload = '{"idempotencyKey":"MzE3M2YxNTQtZjc1ZS00ZDcxLTg5ZWQtNjI2NTcwMzc2ODk0IzU2Yjg4ZTRmLTk2NTgtYWRmOC1mMWZhLTQ1MjY2MjA0Mjk4Yg==","eventDescriptor":"ITEM_IN_TRANSIT","resources":["businessProduct/bp-test-id/order/order_id/delivery/id"],"eventId":"56b88e4f-9658-adf8-f1fa-45266204298b","apiVersion":"2024-01-01","subscriptionId":"3173f154-f75e-4d71-89ed-626570376894","eventTime":"2024-07-19T15:48:28Z","data":{}}';
    // The header received in the webhook from the x-amzn-signature key is base64-encoded
    const webhookSignature = 'MGQCMEzt1Ei6H9qtalFokD7uqTKF43tYGdBev84NNO8jB5+JTsJwN5MUiF66m4lUtn5bkQIwYZKwQhRgPTIGDtiDXsBmQbe0gPJZXdmzq/dJxOM34By+98Yhhpj4wVSTGI7V4YkI';

    try {
        await verifySignature(kid, webhookPayload, webhookSignature);
    } catch (error) {
        console.error('Error validating signature:', error.message);
    }
}

main();

API operations

You use the following mutations and queries to subscribe to events. To access these mutations and queries, your API credentials must have at least full permission (create, view, and delete) to event subscriptions. You choose permission scopes when you generate your API credentials. For details, see Authenticate to the Buy with Prime API.

Mutations

NameDescription
createEventSubscriptionCreates a subscription to receive one or more types of events.
deleteEventSubscriptionDeletes a subscription so that you stop receiving a certain event type.

Queries

NameDescription
eventSubscriptionGet event subscription details by subscription ID.
eventSubscriptionsGet a list of subscription IDs for a given client ID.

Related topics