diff --git a/lib/ci/job_token/jwt.rb b/lib/ci/job_token/jwt.rb
index ab973de0ba79261428c69fdd27ee74313a9882d2..09a7c11e89b984a997161972fd86d483e54782b9 100644
--- a/lib/ci/job_token/jwt.rb
+++ b/lib/ci/job_token/jwt.rb
@@ -49,15 +49,19 @@ def decode(token)
         end
 
         def build_payload(job)
-          base_payload = { cell_id: Gitlab.config.cell.id }
-          base_payload.merge(extra_payload(job)).compact_blank
+          base_payload = { scoped_user_id: job.scoped_user&.id }.compact_blank
+          base_payload.merge(routable_payload(job))
         end
 
-        def extra_payload(job)
+        # Creating routing information for routable tokens https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cells/routable_tokens/
+        def routable_payload(job)
           {
-            scoped_user_id: job.scoped_user&.id,
-            organization_id: job.project.organization_id
-          }
+            c: Gitlab.config.cell.id,
+            o: job.project.organization_id,
+            u: job.user_id,
+            p: job.project_id,
+            g: job.project.group&.id
+          }.compact_blank.transform_values { |id| id.to_s(36) }
         end
 
         def token_prefix
@@ -101,14 +105,30 @@ def scoped_user
       strong_memoize_attr :scoped_user
 
       def cell_id
-        @jwt.payload['cell_id']
+        decode(@jwt.payload['c'])
       end
-      strong_memoize_attr :cell_id
 
-      def organization
-        job&.project&.organization
+      def organization_id
+        decode(@jwt.payload['o'])
+      end
+
+      def project_id
+        decode(@jwt.payload['p'])
+      end
+
+      def user_id
+        decode(@jwt.payload['u'])
+      end
+
+      def group_id
+        decode(@jwt.payload['g'])
+      end
+
+      private
+
+      def decode(encoded_value)
+        encoded_value&.to_i(36)
       end
-      strong_memoize_attr :organization
     end
   end
 end
diff --git a/spec/lib/ci/job_token/jwt_spec.rb b/spec/lib/ci/job_token/jwt_spec.rb
index 77eb0d7f3b49a5942498552cb0cca1b6c1410b6b..99319389fbd634bb3dbfec5b62abc8cfeeabd227 100644
--- a/spec/lib/ci/job_token/jwt_spec.rb
+++ b/spec/lib/ci/job_token/jwt_spec.rb
@@ -6,6 +6,7 @@
   let_it_be(:rsa_key) { OpenSSL::PKey::RSA.generate(2048) }
   let_it_be(:user) { create(:user) }
   let_it_be(:job) { create(:ci_build, user: user) }
+  let(:cell_id) { 1 }
 
   before do
     allow(Gitlab::CurrentSettings)
@@ -61,11 +62,38 @@
 
     subject(:decoded_token) { described_class.decode(encoded_token) }
 
+    before do
+      allow(Gitlab.config.cell).to receive(:id).and_return(cell_id)
+    end
+
     context 'with a valid token' do
+      let(:decoded_payload) { decoded_token.instance_variable_get(:@jwt).payload }
+      let(:expected_payload) do
+        {
+          "c" => cell_id.to_s(36),
+          "o" => job.project.organization_id.to_s(36),
+          "u" => user.id.to_s(36),
+          "p" => job.project_id.to_s(36)
+        }
+      end
+
       it 'successfully decodes the token with subject' do
         expect(decoded_token).to be_present
         expect(decoded_token.job).to eq(job)
       end
+
+      it 'successfully decodes the token with routable payload' do
+        expect(decoded_payload).to match(a_hash_including(expected_payload))
+      end
+
+      context 'when project belongs to a group' do
+        let_it_be(:job) { create(:ci_build, user: user, project: create(:project, :in_group)) }
+
+        it 'includes group id in routable payload' do
+          expect(decoded_payload)
+            .to match(a_hash_including(expected_payload.merge("g" => job.project.group.id.to_s(36))))
+        end
+      end
     end
 
     context 'when signing key is not available' do
@@ -184,17 +212,58 @@
     let(:encoded_token) { described_class.encode(job) }
     let(:decoded_token) { described_class.decode(encoded_token) }
 
+    before do
+      allow(Gitlab.config.cell).to receive(:id).and_return(cell_id)
+    end
+
     it 'encodes the cell_id in the JWT payload' do
-      expect(decoded_token.cell_id).to eq(Gitlab.config.cell.id)
+      expect(decoded_token.cell_id).to eq(cell_id)
+    end
+  end
+
+  describe '#organization_id' do
+    let(:encoded_token) { described_class.encode(job) }
+    let(:decoded_token) { described_class.decode(encoded_token) }
+
+    it 'encodes the organization_id in the JWT payload' do
+      expect(decoded_token.organization_id).to eq(job.project.organization_id)
     end
   end
 
-  describe '#organization' do
+  describe '#project_id' do
     let(:encoded_token) { described_class.encode(job) }
     let(:decoded_token) { described_class.decode(encoded_token) }
 
-    it 'encodes the organization in the JWT payload' do
-      expect(decoded_token.organization).to eq(job.project.organization)
+    it 'encodes the project_id in the JWT payload' do
+      expect(decoded_token.project_id).to eq(job.project_id)
+    end
+  end
+
+  describe '#user_id' do
+    let(:encoded_token) { described_class.encode(job) }
+    let(:decoded_token) { described_class.decode(encoded_token) }
+
+    it 'encodes the user_id in the JWT payload' do
+      expect(decoded_token.user_id).to eq(job.user_id)
+    end
+  end
+
+  describe '#group_id' do
+    let(:encoded_token) { described_class.encode(job) }
+    let(:decoded_token) { described_class.decode(encoded_token) }
+
+    context 'when project belongs to a group' do
+      let_it_be(:job) { create(:ci_build, user: user, project: create(:project, :in_group)) }
+
+      it 'encodes the group_id in the JWT payload' do
+        expect(decoded_token.group_id).to eq(job.project.group.id)
+      end
+    end
+
+    context 'when project belongs to a personal namespace' do
+      it 'does not encode the group_id in the JWT payload' do
+        expect(decoded_token.group_id).to be_nil
+      end
     end
   end