Order status and delivery updates are shared via webhooks.

Webhook configuration

To configure your webhook endpoint:

  1. Head over to https://console.rye.com/account
  2. Enter a valid callback endpoint for your backend under the “Webhook section”, and click save

That’s it! On every order update, a webhook will be fired at your designated endpoint. Currently, webhooks are not retried on failure so please ensure they succeed on the first try.


Webhook verification

For added security, Rye includes a signature header on each webhook request sent to your endpoint. This header can be used to verify the authenticity of the request. Note that depending on your webserver configuration, the header key may come through as lowercase.

Rye-Hmac-Signature-V1 header

The Rye-Hmac-Signature-V1 header will contain an HMAC signature of the request body. This signature is generated using the SHA-256 algorithm and your account’s unique HMAC secret key, and then base64-encoded. Your HMAC secret key can be found in the Rye Console on the Account page, within the Webhooks section.

Here’s an example of how you might verify the request in Node.js using express:

const crypto = require('crypto');

// Your HMAC secret key
const SECRET_KEY = process.env.RYE_HMAC_SECRET_KEY;

// Inside your POST handler
app.post('/', (req, res) => {
  // Create a SHA-256 HMAC with the shared secret key
  const hmac = crypto.createHmac('sha256', SECRET_KEY);

  // Update the HMAC with the request body
  // req.body represents the POST body as a string, assuming that it hasn't been parsed to JSON
  hmac.update(req.body);

  // Compare the base64 HMAC digest against the signature passed in the header
  if (hmac.digest('base64') !== req.headers['rye-hmac-signature-v1']) {
    // The request is not authentic
    return res.status(401).send('Unauthorized');
  }
});

Webhook payload examples

The requestId field present in the webhook payload is what will be returned by the submitCart mutation. This can interpreted as the “order ID”.

{
  "id": "hg9b8xyc-a128-4659-9c84-e3b376607977",
  "requestId": "d222c412-fbc8-4e59-8cef-b57696bxyz11",
  "developerId": "xDasf23Jk4LKlxOq",
  "createdAt": "2023-06-27T12:00:00Z",
  "type": "PAYMENT_SUCCEEDED",
  "data": {
    "marketplace": "shopify",
    "amount": 100
  }
}

{
  "id": "hg9b8xyc-a128-4659-9c84-e3b376607977",
  "requestId": "d222c412-fbc8-4e59-8cef-b57696bxyz11",
  "developerId": "xDasf23Jk4LKlxOq",
  "createdAt": "2023-06-27T12:00:03Z",
  "type": "ORDER_SUBMISSION_STARTED",
  "data": {
    "marketplace": "shopify"
  }
}
{
  "id": "hg9b8xyc-a128-4659-9c84-e3b376607977",
  "requestId": "d222c412-fbc8-4e59-8cef-b57696bxyz11",
  "developerId": "xDasf23Jk4LKlxOq",
  "createdAt": "2023-06-27T12:00:03Z",
  "type": "ORDER_CANCEL_STARTED",
  "data": {
    "marketplace": "amazon",
    "startedAt": "2023-09-07T17:49:07.267Z"
  }
}

{
  "id": "hg9b8xyc-a128-4659-9c84-e3b376607977",
  "requestId": "d222c412-fbc8-4e59-8cef-b57696bxyz11",
  "developerId": "xDasf23Jk4LKlxOq",
  "createdAt": "2023-06-27T12:00:06Z",
  "type": "ORDER_PLACED",
  "data": {
    "marketplace": "shopify",
    "order": {
      "currency": "USD",
      "shippingCents": 500,
      "subtotalCents": 2000,
      "taxCents": 200,
      "totalCents": 2700,
      "orderEmailOverride": "[[email protected]](/cdn-cgi/l/email-protection)"
    }
  }
}

{
  "id": "hg9b8xyc-a128-4659-9c84-e3b376607977",
  "requestId": "d222c412-fbc8-4e59-8cef-b57696bxyz11",
  "developerId": "xDasf23Jk4LKlxOq",
  "createdAt": "2023-06-27T12:00:09Z",
  "type": "TRACKING_OBTAINED",
  "data": {
    "marketplace": "shopify",
    "order": {
      "fulfillments": [
        {
          "products": [
            {
              "quantity": 1,
              "variantId": "41160207204557"
            }
          ],
          "trackingDetails": [
            {
              "trackingNumber": "1Z87F765679738",
              "courierUrl": "https://www.ups.com/WebTracking?loc=en_US&requester=ST&trackNums=1Z87F765679738",
              "courierName": "UPS"
            }
          ]
        }
      ]
    }
  }
}

{
  "id": "hg9b8xyc-a128-4659-9c84-e3b376607977",
  "requestId": "d222c412-fbc8-4e59-8cef-b57696bxyz11",
  "developerId": "xDasf23Jk4LKlxOq",
  "createdAt": "2023-06-27T12:00:09Z",
  "type": "RETURN_REQUESTED",
  "data": {
    "marketplace": "shopify",
    "returnId": "123456",
    "requestedAt": "2023-06-27T12:00:09Z",
    "requestedLineItems": [
      {
        "variantId": "123456"
      }
    ]
  }
}

{
  "id": "hg9b8xyc-a128-4659-9c84-e3b376607977",
  "requestId": "d222c412-fbc8-4e59-8cef-b57696bxyz11",
  "developerId": "xDasf23Jk4LKlxOq",
  "createdAt": "2023-06-27T12:00:09Z",
  "type": "RETURN_REQUESTED",
  "data": {
    "marketplace": "amazon",
    "returnId": "123456",
    "requestedAt": "2023-06-27T12:00:09Z",
    "requestedLineItems": [
      {
        "productId": "123456",
        "status": "RETURN_REQUESTED",
      }
    ]
  }
}

{
  "id": "hg9b8xyc-a128-4659-9c84-e3b376607977",
  "developerId": "xDasf23Jk4LKlxOq",
  "createdAt": "2023-06-27T12:00:09Z",
  "type": "SHOPIFY_APP_CONNECTED",
  "data": {
    "shopDomain": "rye.myshopify.com",
  }
}

Webhook types

The webhook body is JSON formatted. If your project is Typescript based you can use these types.

/**
 * How public events are represented on data transfer
 */
export type RyeWebhook = PaymentSucceededWebhook | PaymentFailedWebhook | PaymentRefundedWebhook | OrderSubmissionStartedWebhook | OrderSubmissionSucceededWebhook | OrderPlacedWebhook | OrderFailedWebhook | PublicOrderCancelledWebhook | PublicOrderCancelFailedWebhook | PublicOrderCancelStartedWebhook | PublicOrderCancelRequestedWebhook | ReturnApprovedWebhook | ReturnDeniedWebhook | ReturnCancelledWebhook | ReturnRequestedWebhook | ReturnRequestAcceptedWebhook | ReturnClosedWebhook | TrackingObtainedWebhook | ShopifyProductUpdatedWebhook;
export type PaymentSucceededWebhook = BaseWebhook & {
    type: WebhookType.PaymentSucceeded;
    data: {
        marketplace: Marketplace;
        amount: number;
    };
};
export type BaseWebhook = {
    // Unique identifier for the event
    id: string;
    // Unique ID of the request. This is used to group events together.
    requestId: string;
    // ID of developer who initiated the request
    developerId: string;
    // Creation time of the event
    createdAt: string;
};
export type Marketplace = AmazonMarketplace | ShopifyMarketplace;
export enum WebhookType {
    PaymentSucceeded = 'PAYMENT_SUCCEEDED',
    PaymentFailed = 'PAYMENT_FAILED',
    PaymentRefunded = 'PAYMENT_REFUNDED',
    OrderSubmissionStarted = 'ORDER_SUBMISSION_STARTED',
    OrderSubmissionSucceeded = 'ORDER_SUBMISSION_SUCCEEDED',
    OrderPlaced = 'ORDER_PLACED',
    OrderFailed = 'ORDER_FAILED',
    OrderCancelFailed = 'ORDER_CANCEL_FAILED',
    OrderCancelStarted = 'ORDER_CANCEL_STARTED',
    OrderCancelRequested = 'ORDER_CANCEL_REQUESTED',
    OrderCancelSucceeded = 'ORDER_CANCEL_SUCCEEDED',
    TrackingObtained = 'TRACKING_OBTAINED',
    ReturnRequested = 'RETURN_REQUESTED',
    ReturnRequestAccepted = 'RETURN_REQUEST_ACCEPTED',
    ReturnApproved = 'RETURN_APPROVED',
    ReturnClosed = 'RETURN_CLOSED',
    ReturnDenied = 'RETURN_DENIED',
    ReturnCancelled = 'RETURN_CANCELLED',
    ShopifyProductUpdated = 'SHOPIFY_PRODUCT_UPDATED'
}
export type PaymentFailedWebhook = BaseWebhook & {
    type: WebhookType.PaymentFailed;
    data: {
        marketplace: Marketplace;
    };
};
export type PaymentRefundedWebhook = BaseWebhook & {
    type: WebhookType.PaymentRefunded;
    data: Record<string, never>;
};
export type OrderSubmissionStartedWebhook = BaseWebhook & {
    type: WebhookType.OrderSubmissionStarted;
    data: {
        marketplace: Marketplace;
    };
};
export type OrderSubmissionSucceededWebhook = BaseWebhook & {
    type: WebhookType.OrderSubmissionSucceeded;
    data: {
        marketplace: Marketplace;
    };
};
export type OrderPlacedWebhook = BaseWebhook & {
    type: WebhookType.OrderPlaced;
    data: AmazonOrderPlacedData | ShopifyOrderPlacedData;
};
type AmazonOrderPlacedData = {
    marketplace: AmazonMarketplace;
    order: BasePlacedOrderDetails & {
        amazonOrders: Pick<BaseAmazonOrder, 'amazonOrderId' | 'products'>[];
    };
};
export type AmazonMarketplace = 'amazon';
export type BasePlacedOrderDetails = {
    currency: string;
    shippingCents: number;
    subtotalCents: number;
    taxCents: number;
    totalCents: number;
    discountCents: number;
};
export type BaseAmazonOrder = {
    amazonOrderId: string;
    // returnDetails?: {
    //   status:
    //     | EventType.ReturnRequested
    //     | EventType.ReturnRequestAccepted
    //     | EventType.ReturnApproved
    //     | EventType.ReturnDenied
    //     | EventType.ReturnClosed;
    //   date: Timestamp;
    //   reason?: string; //use this for failed status
    //   requestedLineItems?: AmazonReturnLineItem[];
    //   refundedLineItems?: AmazonReturnLineItem[];
    // };
    cancellation?: {
        status: WebhookType.OrderCancelFailed | WebhookType.OrderCancelSucceeded | WebhookType.OrderCancelStarted | WebhookType.OrderCancelRequested;
        isManuallyCancelled?: boolean;
        isOrderFailure?: boolean;
        date: string;
        reason?: string; //use this for failed status
    };
    products: AmazonOrderRequestProduct[];
};
export type AmazonOrderRequestProduct = {
    quantity: number;
    productId: string;
};
type ShopifyOrderPlacedData = {
    marketplace: ShopifyMarketplace;
    order: BasePlacedOrderDetails & {
    };
    orderEmailOverride?: string;
};
export type ShopifyMarketplace = 'shopify';
export type OrderFailedWebhook = BaseWebhook & {
    type: WebhookType.OrderFailed;
    data: {
        marketplace: Marketplace;
        refund?: StripeRefund;
        reasonCode?: OrderFailedReasonCode;
        reason?: string;
    };
};
type StripeRefund = {
    /**
     * Unique identifier for the object.
     */
    id: string;
    /**
     * String representing the object's type. Objects of the same type share the same value.
     */
    object: 'refund';
    /**
     * Amount, in %s.
     */
    amount: number;
    /**
     * Time at which the object was created. Measured in seconds since the Unix epoch.
     */
    created: number;
    /**
     * Three-letter [ISO currency code](https://www.iso.org/iso-4217-currency-codes.html), in lowercase. Must be a [supported currency](https://stripe.com/docs/currencies).
     */
    currency: string;
    /**
     * An arbitrary string attached to the object. Often useful for displaying to users. (Available on non-card refunds only)
     */
    description?: string;
    /**
     * This is the transaction number that appears on email receipts sent for this refund.
     */
    receipt_number: string | null;
    /**
     * Status of the refund. For credit card refunds, this can be `pending`, `succeeded`, or `failed`. For other types of refunds, it can be `pending`, `requires_action`, `succeeded`, `failed`, or `canceled`. Refer to our [refunds](https://stripe.com/docs/refunds#failed-refunds) documentation for more details.
     */
    status: string | null;
};
export type OrderFailedReasonCode = 'INTERNAL_ERROR' | 'OUT_OF_STOCK' | 'INVALID_REQUEST' | 'UNAUTHENTICATED' | 'PERMISSION_DENIED' | 'USER_REQUESTED' | 'DENIED_BY_MARKETPLACE' | 'CANCELED' | 'SHIPPING_ADDRESS_BLACKLISTED' | 'SHIPPING_ADDRESS_UNAVAILABLE' | 'EXPIRED_PRODUCT_ID' | 'INSUFFICIENT_VARIANTS' | 'INVALID_QUANTITY' | 'INVALID_SHIPPING_METHOD' | 'INVALID_VARIANT' | 'PRODUCT_UNAVAILABLE' | 'SHIPPING_ADDRESS_REFUSED' | 'SHIPPING_METHOD_UNAVAILABLE' | 'UNSUPPORTED_PRODUCT_ID' | 'PAYMENT_FAILED';
export type PublicOrderCancelledWebhook = BaseWebhook & {
    type: WebhookType.OrderCancelSucceeded;
    data: {
        marketplace: Marketplace;
        cancelledAt: string;
        refund?: StripeRefund;
    };
};
export type PublicOrderCancelFailedWebhook = BaseWebhook & {
    type: WebhookType.OrderCancelFailed;
    data: {
        marketplace: Marketplace;
        reason: string;
    };
};
export type PublicOrderCancelStartedWebhook = BaseWebhook & {
    type: WebhookType.OrderCancelStarted;
    data: {
        marketplace: Marketplace;
        startedAt: string;
    };
};
export type PublicOrderCancelRequestedWebhook = BaseWebhook & {
    type: WebhookType.OrderCancelRequested;
    data: {
        marketplace: Marketplace;
        requestedAt: string;
        marketplaceOrderId: string;
    };
};
export type ReturnApprovedWebhook = BaseWebhook & {
    type: WebhookType.ReturnApproved;
    data: AmazonReturnApprovedWebhookData | ShopifyReturnApprovedWebhookData;
};
export type AmazonReturnApprovedWebhookData = BaseReturnApprovedWebhookData & {
    marketplace: AmazonMarketplace;
    requestedLineItems: AmazonReturnLineItem[];
    returnId: string;
    shippingLabelUrl: string;
    refund: RefundBreakdown;
};
export type BaseReturnApprovedWebhookData = {
    approvedAt: string;
    returnId: string;
    shippingLabelUrl: string;
};
export type AmazonReturnLineItem = BaseReturnLineItem & {
    productId: string;
    status: LineItemReturnStatus;
};
export type BaseReturnLineItem = {
    quantity: number;
    reason?: string;
    productName?: string;
    productImageUrl?: string;
    refundAmount?: number;
};
export type LineItemReturnStatus = WebhookType.ReturnRequested | WebhookType.ReturnRequestAccepted | WebhookType.ReturnApproved | WebhookType.ReturnClosed | WebhookType.ReturnDenied | WebhookType.ReturnCancelled;
export type RefundBreakdown = {
    subtotalCents: number;
    taxCents: number;
    totalCents: number;
};
export type ShopifyReturnApprovedWebhookData = BaseReturnApprovedWebhookData & {
    marketplace: ShopifyMarketplace;
    requestedLineItems: ShopifyReturnLineItem[];
    returnId: string;
    shippingLabelUrl: string;
    refundAmount: number;
};
export type ShopifyReturnLineItem = BaseReturnLineItem & {
    variantId: string;
    price: string;
    currencyCode: string;
};
export type ReturnDeniedWebhook = BaseWebhook & {
    type: WebhookType.ReturnDenied;
    data: AmazonReturnDeniedWebhookData | ShopifyReturnDeniedWebhookData;
};
export type AmazonReturnDeniedWebhookData = BaseReturnDeniedWebhookData & {
    marketplace: AmazonMarketplace;
    requestedLineItems: AmazonReturnLineItem[];
    returnId: string;
};
export type BaseReturnDeniedWebhookData = {
    deniedAt: string;
    returnId: string;
};
export type ShopifyReturnDeniedWebhookData = BaseReturnDeniedWebhookData & {
    marketplace: ShopifyMarketplace;
    requestedLineItems: ShopifyReturnLineItem[];
    returnId: string;
};
export type ReturnCancelledWebhook = BaseWebhook & {
    type: WebhookType.ReturnCancelled;
    data: {
        marketplace: Marketplace;
        returnId: string;
        cancelledAt: string;
    };
};
export type ReturnRequestedWebhook = BaseWebhook & {
    type: WebhookType.ReturnRequested;
    data: AmazonReturnRequestedWebhookData | ShopifyReturnRequestedWebhookData;
};
export type AmazonReturnRequestedWebhookData = BaseReturnRequestedWebhookData & {
    marketplace: AmazonMarketplace;
    requestedLineItems: AmazonReturnLineItem[];
    returnId: string;
};
export type BaseReturnRequestedWebhookData = {
    requestedAt: string;
    returnId: string;
};
export type ShopifyReturnRequestedWebhookData = BaseReturnRequestedWebhookData & {
    marketplace: ShopifyMarketplace;
    requestedLineItems: ShopifyReturnLineItem[];
    returnId: string;
};
export type ReturnRequestAcceptedWebhook = BaseWebhook & {
    type: WebhookType.ReturnRequestAccepted;
    data: AmazonReturnRequestAcceptedWebhookData | ShopifyReturnRequestAcceptedWebhookData;
};
export type AmazonReturnRequestAcceptedWebhookData = BaseReturnRequestAcceptedWebhookData & {
    marketplace: AmazonMarketplace;
    acceptedAt: string;
    returnId: string;
};
export type BaseReturnRequestAcceptedWebhookData = {
    acceptedAt: string;
    returnId: string;
};
export type ShopifyReturnRequestAcceptedWebhookData = BaseReturnRequestAcceptedWebhookData & {
    marketplace: ShopifyMarketplace;
    acceptedAt: string;
};
export type ReturnClosedWebhook = BaseWebhook & {
    type: WebhookType.ReturnClosed;
    data: AmazonReturnClosedWebhookData | ShopifyReturnClosedWebhookData;
};
export type AmazonReturnClosedWebhookData = BaseReturnClosedWebhookData & {
    marketplace: AmazonMarketplace;
    refundedLineItems: AmazonReturnLineItem[];
    returnId: string;
};
export type BaseReturnClosedWebhookData = {
    closedAt: string;
    returnId: string;
};
export type ShopifyReturnClosedWebhookData = BaseReturnClosedWebhookData & {
    marketplace: ShopifyMarketplace;
    refundedLineItems: ShopifyReturnLineItem[];
    returnId: string;
};
export type TrackingObtainedWebhook = BaseWebhook & {
    type: WebhookType.TrackingObtained;
    data: AmazonTrackingObtainedData | ShopifyTrackingObtainedData;
};
type AmazonTrackingObtainedData = {
    marketplace: AmazonMarketplace;
    order: BasePlacedOrderDetails & {
        amazonOrders: AmazonOrderWithTrackingObtained[];
    };
};
type AmazonOrderWithTrackingObtained = Pick<BaseAmazonOrder, 'amazonOrderId' | 'products'> & AmazonTrackedOrderDetails & {
    shipments?: AmazonShipment[];
};
export type AmazonTrackedOrderDetails = TrackedOrderDetails & {
    retailTrackingNumber?: string;
};
export type TrackedOrderDetails = {
    trackingNumber: string;
    courierUrl: string;
    courierName: string;
};
/** The shipment sent to the customer. */
export type AmazonShipment = {
    /**
     * Date when the item was shipped.
     * @format Firestore Timestamp
     */
    shipmentDate: string;
    /** The current status of the shipment. */
    shipmentStatus: 'SHIPPED' | null;
    /** The tracking number from the shipment carrier. */
    carrierTracking: string | null;
    /** The delivery status and estimated delivery date. */
    deliveryInformation: DeliveryInformation;
    /** The total number of items for shipping. */
    shipmentQuantity: number;
    /** The name of the shipment carrier. */
    carrierName: string;
    /** Tracking url for the shipment carrier */
    carrierTrackingUrl: string;
};
/** The delivery status and estimated delivery date. */
export type DeliveryInformation = {
    /**
     * The expected delivery date of the shipment.
     * @format Firestore Timestamp
     */
    expectedDeliveryDate: string;
    /** The current delivery status of the shipment. */
    deliveryStatus: 'DELIVERED' | 'NOT_DELIVERED' | 'NOT_AVAILABLE';
};
export type ShopifyTrackingObtainedData = {
    marketplace?: ShopifyMarketplace;
    order: {
        fulfillments: BaseShopifyOrderFulfillment[];
    };
};
export type BaseShopifyOrderFulfillment = {
    products: ShopifyOrderRequestProduct[];
    trackingDetails: TrackedOrderDetails[];
};
export type ShopifyOrderRequestProduct = {
    quantity: number;
    variantId: string;
};
export type ShopifyProductUpdatedWebhook = BaseWebhook & {
    type: WebhookType.ShopifyProductUpdated;
    data: {
        marketplace: ShopifyMarketplace;
        product: BaseShopifyProduct;
    };
};
export type BaseShopifyProduct = {
    id: number;
    variants: Array<BaseShopifyVariant>;
};
export type BaseShopifyVariant = {
    id: number;
    quantityAvailable: number;
};