diff --git a/CHANGELOG.md b/CHANGELOG.md
index 869c91cbb..e99c7cea8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,8 @@ Note: For changes to the API, see https://shopify.dev/changelog?filter=api
 
 ## Unreleased
 
+- [#979](https://github.com/Shopify/shopify_api/pull/979) Update `ShopifyAPI::Context.setup` to take `old_api_secret_key` to support API credentials rotation
+
 ## Version 10.1.0
 
 - [#933](https://github.com/Shopify/shopify_api/pull/933) Fix syntax of GraphQL query in `Webhooks.get_webhook_id` method by removing extra curly brace
diff --git a/lib/shopify_api/auth/jwt_payload.rb b/lib/shopify_api/auth/jwt_payload.rb
index 867baa690..643357c7f 100644
--- a/lib/shopify_api/auth/jwt_payload.rb
+++ b/lib/shopify_api/auth/jwt_payload.rb
@@ -16,11 +16,12 @@ class JwtPayload
 
       sig { params(token: String).void }
       def initialize(token)
-        begin
-          payload_hash = JWT.decode(token, Context.api_secret_key, true,
-            { exp_leeway: JWT_EXPIRATION_LEEWAY, algorithm: "HS256" })[0]
-        rescue
-          raise ShopifyAPI::Errors::InvalidJwtTokenError, "Failed to parse session token '#{token}'"
+        payload_hash = begin
+          decode_token(token, Context.api_secret_key)
+        rescue ShopifyAPI::Errors::InvalidJwtTokenError
+          raise unless Context.old_api_secret_key
+
+          decode_token(token, T.must(Context.old_api_secret_key))
         end
 
         @iss = T.let(payload_hash["iss"], String)
@@ -67,6 +68,16 @@ def ==(other)
           jti == other.jti &&
           sid == other.sid
       end
+
+      private
+
+      sig { params(token: String, api_secret_key: String).returns(T::Hash[String, T.untyped]) }
+      def decode_token(token, api_secret_key)
+        JWT.decode(token, api_secret_key, true,
+          { exp_leeway: JWT_EXPIRATION_LEEWAY, algorithm: "HS256" })[0]
+      rescue
+        raise ShopifyAPI::Errors::InvalidJwtTokenError, "Failed to parse session token '#{token}'"
+      end
     end
   end
 end
diff --git a/lib/shopify_api/context.rb b/lib/shopify_api/context.rb
index 6c95d63c5..b3282b507 100644
--- a/lib/shopify_api/context.rb
+++ b/lib/shopify_api/context.rb
@@ -18,6 +18,7 @@ class Context
     @notified_missing_resources_folder = T.let({}, T::Hash[String, T::Boolean])
     @active_session = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
     @user_agent_prefix = T.let(nil, T.nilable(String))
+    @old_api_secret_key = T.let(nil, T.nilable(String))
 
     @rest_resource_loader = T.let(nil, T.nilable(Zeitwerk::Loader))
 
@@ -37,6 +38,7 @@ class << self
           logger: Logger,
           private_shop: T.nilable(String),
           user_agent_prefix: T.nilable(String),
+          old_api_secret_key: T.nilable(String),
         ).void
       end
       def setup(
@@ -50,7 +52,8 @@ def setup(
         session_storage:,
         logger: Logger.new($stdout),
         private_shop: nil,
-        user_agent_prefix: nil
+        user_agent_prefix: nil,
+        old_api_secret_key: nil
       )
         unless ShopifyAPI::AdminVersions::SUPPORTED_ADMIN_VERSIONS.include?(api_version)
           raise Errors::UnsupportedVersionError,
@@ -68,6 +71,7 @@ def setup(
         @logger = logger
         @private_shop = private_shop
         @user_agent_prefix = user_agent_prefix
+        @old_api_secret_key = old_api_secret_key
 
         load_rest_resources(api_version: api_version)
       end
@@ -118,7 +122,7 @@ def private?
       end
 
       sig { returns(T.nilable(String)) }
-      attr_reader :private_shop, :user_agent_prefix
+      attr_reader :private_shop, :user_agent_prefix, :old_api_secret_key
 
       sig { returns(T::Boolean) }
       def embedded?
diff --git a/test/context_test.rb b/test/context_test.rb
index a7f311090..f0626486a 100644
--- a/test/context_test.rb
+++ b/test/context_test.rb
@@ -18,7 +18,8 @@ def setup
         session_storage: ShopifyAPI::Auth::FileSessionStorage.new,
         logger: Logger.new(writer),
         private_shop: "privateshop.myshopify.com",
-        user_agent_prefix: "user_agent_prefix1"
+        user_agent_prefix: "user_agent_prefix1",
+        old_api_secret_key: "old_secret"
       )
     end
 
@@ -40,6 +41,7 @@ def test_setup
       assert_match(/test log/, @reader.gets)
       assert_equal("privateshop.myshopify.com", ShopifyAPI::Context.private_shop)
       assert_equal("user_agent_prefix1", ShopifyAPI::Context.user_agent_prefix)
+      assert_equal("old_secret", ShopifyAPI::Context.old_api_secret_key)
     end
 
     def test_active_session_is_thread_safe
@@ -125,7 +127,8 @@ def clear_context
         is_private: false,
         is_embedded: true,
         session_storage: ShopifyAPI::Auth::FileSessionStorage.new,
-        user_agent_prefix: nil
+        user_agent_prefix: nil,
+        old_api_secret_key: nil
       )
     end
   end
diff --git a/test/test_helper.rb b/test/test_helper.rb
index daa24fcc7..a56140e37 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -31,7 +31,8 @@ def setup
           is_private: false,
           is_embedded: false,
           session_storage: TestHelpers::FakeSessionStorage.new,
-          user_agent_prefix: nil
+          user_agent_prefix: nil,
+          old_api_secret_key: nil
         )
       end
 
@@ -47,7 +48,8 @@ def setup
           session_storage: T.nilable(ShopifyAPI::Auth::SessionStorage),
           logger: T.nilable(Logger),
           private_shop: T.nilable(String),
-          user_agent_prefix: T.nilable(String)
+          user_agent_prefix: T.nilable(String),
+          old_api_secret_key: T.nilable(String)
         ).void
       end
       def modify_context(
@@ -61,7 +63,8 @@ def modify_context(
         session_storage: nil,
         logger: nil,
         private_shop: "do-not-set",
-        user_agent_prefix: nil
+        user_agent_prefix: nil,
+        old_api_secret_key: nil
       )
         ShopifyAPI::Context.setup(
           api_key: api_key ? api_key : ShopifyAPI::Context.api_key,
@@ -74,7 +77,8 @@ def modify_context(
           session_storage: session_storage ? session_storage : ShopifyAPI::Context.session_storage,
           logger: logger ? logger : ShopifyAPI::Context.logger,
           private_shop: private_shop != "do-not-set" ? private_shop : ShopifyAPI::Context.private_shop,
-          user_agent_prefix: user_agent_prefix ? user_agent_prefix : ShopifyAPI::Context.user_agent_prefix
+          user_agent_prefix: user_agent_prefix ? user_agent_prefix : ShopifyAPI::Context.user_agent_prefix,
+          old_api_secret_key: old_api_secret_key ? old_api_secret_key : ShopifyAPI::Context.old_api_secret_key
         )
       end
     end
diff --git a/test/utils/session_utils_test.rb b/test/utils/session_utils_test.rb
index d80165e98..f5622ccf6 100644
--- a/test/utils/session_utils_test.rb
+++ b/test/utils/session_utils_test.rb
@@ -84,6 +84,31 @@ def test_fails_if_authorization_header_bearer_token_is_invalid
         end
       end
 
+      def test_fails_if_authorization_header_be
+        modify_context(is_embedded: true)
+        jwt_header = create_jwt_header("UNKNOWN_API_SECRET_KEY")
+        assert_raises(ShopifyAPI::Errors::InvalidJwtTokenError) do
+          ShopifyAPI::Utils::SessionUtils.load_current_session(auth_header: jwt_header, is_online: true)
+        end
+      end
+
+      def test_decodes_jwt_token_signed_with_old_secret
+        modify_context(is_embedded: true)
+        modify_context(old_api_secret_key: "OLD_API_SECRET_KEY")
+        jwt_header = create_jwt_header(ShopifyAPI::Context.old_api_secret_key)
+        loaded_session = ShopifyAPI::Utils::SessionUtils.load_current_session(auth_header: jwt_header, is_online: true)
+        assert_equal(@online_embedded_session, loaded_session)
+      end
+
+      def test_fails_if_old_api_secret_key_is_invalid
+        modify_context(is_embedded: true)
+        modify_context(old_api_secret_key: "OLD_API_SECRET_KEY")
+        jwt_header = create_jwt_header("UNKNOWN_OLD_API_SECRET_KEY")
+        assert_raises(ShopifyAPI::Errors::InvalidJwtTokenError) do
+          ShopifyAPI::Utils::SessionUtils.load_current_session(auth_header: jwt_header, is_online: true)
+        end
+      end
+
       def test_fails_if_authorization_header_is_not_a_bearer_token
         modify_context(is_embedded: true)
         assert_raises(ShopifyAPI::Errors::MissingJwtTokenError) do
@@ -212,6 +237,11 @@ def add_session(is_online:)
         another_session = ShopifyAPI::Auth::Session.new(shop: @shop, is_online: is_online)
         ShopifyAPI::Context.session_storage.store_session(another_session)
       end
+
+      def create_jwt_header(api_secret_key)
+        jwt_token = JWT.encode(@jwt_payload, api_secret_key, "HS256")
+        "Bearer #{jwt_token}"
+      end
     end
   end
 end