Cart Confirmation Shipping Validation Error effecting webhook provided shipping rates

Suddenly our custom shipping via webhook integration is no longer supported. Our rate request and response works correctly and the user can select custom rates via our API, but after they put in card information the payment fails with the following error code:

{
  "kind": "cart-confirmation",
  "reason": "shipping-validation-failed",
  "technicalReason": {
    "title": null,
    "description": "Selected shipping method doesn't match any shipping method configured inside the account. Review configuration in <a href=\"https://app.snipcart.com/dashboard/carriers\">Store Configuration > Shipping</a>. See developer console for more details."
  },
  "data": {
    "valid": false,
    "message": "Selected shipping method doesn't match any shipping method configured inside the account.",
    "carrierErrors": null
  },
  "code": "snipcart.errors.order_validation.shipping_validation",
  "message": "Shipping method couldn't be validated."
}

This is new, we have not seen this before. We have no shipping methods or carriers set up on the snipcart administration. UserDefinedId has been historically getting passed through to the order.completion webhook successfully.

Is anyone else running shipping via webhooks that can advise on what needs done here? Not ideal, this is effecting production. Ideally we don’t need to create shipping methods because they can change depending on the location of our customer and the usage of our shipping provider aggregation service.

seems fishy

At order confirmation time, snipcart now repeats the rates webhook and checks the IDs of the returned rates. Our aggregation service treats a rate as a quote, so every time our server responds to the call the rates are given a unique ID. This pattern no longer works with snipcart as Snipcart can’t find the originally used rate in the new call.

We had to make some modifications to our endpoint to essentially echo the same rate that was used if one was sent up

The applicable bits of code are:

let replacementEchoRate = null;

if (
    requestBody.content.shippingFees != null &&
    requestBody.content.shippingRateUserDefinedId != null &&
    requestBody.content.shippingMethodComplete
  ) {
    
    replacementEchoRate = {
      cost: requestBody.content.shippingFees,
      description: requestBody.content.shippingMethod,
      userDefinedId: requestBody.content.shippingRateUserDefinedId,
    };
  }

And just before we send our rates back to snipcart we overwrite the ID if we find a match for the previously used quote

const rates = await convertQuotesToSnipcartRates(availableRates);      if (replacementEchoRate !== null) {
        // find the rate with the same description and replace it
        const index = rates.findIndex(
          (r) =>
            r.description === replacementEchoRate.description &&
            r.cost === replacementEchoRate.cost
        );
        if (index !== -1) {
          rates[index].userDefinedId = replacementEchoRate?.userDefinedId;
        }
 }
const responseToSnipcart: SnipcartRateResponse = { rates };
res.send(responseToSnipcart);