diff --git a/doc/api/environments.md b/doc/api/environments.md
index f86349a55296f487d036b73f33b9d3bdc258934a..81f40cc9ec4cabdb7a454e03158f525b4f7b52f2 100644
--- a/doc/api/environments.md
+++ b/doc/api/environments.md
@@ -192,12 +192,13 @@ It returns `201` if the environment was successfully created, `400` for wrong pa
 POST /projects/:id/environments
 ```
 
-| Attribute      | Type           | Required | Description                                                                                                         |
-|----------------|----------------|----------|---------------------------------------------------------------------------------------------------------------------|
-| `id`           | integer/string | yes      | The ID or [URL-encoded path](rest/index.md#namespaced-path-encoding) of the project.                                |
-| `name`         | string         | yes      | The name of the environment.                                                                                        |
-| `external_url` | string         | no       | Place to link to for this environment.                                                                              |
-| `tier`         | string         | no       | The tier of the new environment. Allowed values are `production`, `staging`, `testing`, `development`, and `other`. |
+| Attribute          | Type           | Required | Description                                                                                                         |
+|--------------------|----------------|----------|---------------------------------------------------------------------------------------------------------------------|
+| `id`               | integer/string | yes      | The ID or [URL-encoded path](rest/index.md#namespaced-path-encoding) of the project.                                |
+| `name`             | string         | yes      | The name of the environment.                                                                                        |
+| `external_url`     | string         | no       | Place to link to for this environment.                                                                              |
+| `tier`             | string         | no       | The tier of the new environment. Allowed values are `production`, `staging`, `testing`, `development`, and `other`. |
+| `cluster_agent_id` | integer        | no       | The cluster agent to associate with this environment.                                                               |
 
 ```shell
 curl --data "name=deploy&external_url=https://deploy.gitlab.example.com" \
@@ -231,12 +232,13 @@ It returns `200` if the environment was successfully updated. In case of an erro
 PUT /projects/:id/environments/:environments_id
 ```
 
-| Attribute        | Type           | Required | Description                                                                                                         |
-|------------------|----------------|----------|---------------------------------------------------------------------------------------------------------------------|
-| `id`             | integer/string | yes      | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding).                                |
-| `environment_id` | integer        | yes      | The ID of the environment.                                                                                          |
-| `external_url`   | string         | no       | The new `external_url`.                                                                                             |
-| `tier`           | string         | no       | The tier of the new environment. Allowed values are `production`, `staging`, `testing`, `development`, and `other`. |
+| Attribute          | Type            | Required | Description                                                                                                         |
+|--------------------|-----------------|----------|---------------------------------------------------------------------------------------------------------------------|
+| `id`               | integer/string  | yes      | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding).                                |
+| `environment_id`   | integer         | yes      | The ID of the environment.                                                                                          |
+| `external_url`     | string          | no       | The new `external_url`.                                                                                             |
+| `tier`             | string          | no       | The tier of the new environment. Allowed values are `production`, `staging`, `testing`, `development`, and `other`. |
+| `cluster_agent_id` | integer or null | no       | The cluster agent to associate with this environment or `null` to remove it.                                        |
 
 ```shell
 curl --request PUT --data "external_url=https://staging.gitlab.example.com" \
diff --git a/lib/api/environments.rb b/lib/api/environments.rb
index 9b9d35e3cc9783de9bd98eb708ffd9c5163341f1..0b69611d651fb6cf4f61eb6ecbe36968b1430126 100644
--- a/lib/api/environments.rb
+++ b/lib/api/environments.rb
@@ -66,12 +66,21 @@ class Environments < ::API::Base
         optional :external_url,   type: String,   desc: 'Place to link to for this environment'
         optional :slug, absence: { message: "is automatically generated and cannot be changed" }, documentation: { hidden: true }
         optional :tier, type: String, values: Environment.tiers.keys, desc: 'The tier of the new environment. Allowed values are `production`, `staging`, `testing`, `development`, and `other`'
+        optional :cluster_agent_id, type: Integer, desc: 'The ID of the Cluster Agent to associate with this environment'
       end
       route_setting :authentication, job_token_allowed: true
       post ':id/environments' do
         authorize! :create_environment, user_project
 
-        environment = user_project.environments.create(declared_params)
+        params = declared_params
+
+        if params[:cluster_agent_id]
+          agent = ::Clusters::AgentsFinder.new(user_project, current_user).execute.find_by_id(params[:cluster_agent_id])
+
+          bad_request!("cluster agent doesn't exist or cannot be associated with this environment") unless agent
+        end
+
+        environment = user_project.environments.create(params)
 
         if environment.persisted?
           present environment, with: Entities::Environment, current_user: current_user
@@ -95,6 +104,7 @@ class Environments < ::API::Base
         optional :external_url,   type: String,   desc: 'The new URL on which this deployment is viewable'
         optional :slug, absence: { message: "is automatically generated and cannot be changed" }, documentation: { hidden: true }
         optional :tier, type: String, values: Environment.tiers.keys, desc: 'The tier of the new environment. Allowed values are `production`, `staging`, `testing`, `development`, and `other`'
+        optional :cluster_agent_id, type: Integer, desc: 'The ID of the Cluster Agent to associate with this environment'
       end
       route_setting :authentication, job_token_allowed: true
       put ':id/environments/:environment_id' do
@@ -102,7 +112,13 @@ class Environments < ::API::Base
 
         environment = user_project.environments.find(params[:environment_id])
 
-        update_params = declared_params(include_missing: false).extract!(:external_url, :tier)
+        update_params = declared_params(include_missing: false).extract!(:external_url, :tier, :cluster_agent_id)
+
+        if update_params[:cluster_agent_id]
+          agent = ::Clusters::AgentsFinder.new(user_project, current_user).execute.find_by_id(params[:cluster_agent_id])
+
+          bad_request!("cluster agent doesn't exist or cannot be associated with this environment") unless agent
+        end
 
         environment.assign_attributes(update_params)
 
diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb
index 3c2c92510e0891d314ee791475a92ea4810f0353..d04a3be4cd9ad53b6868a3fc8a3b8e5f2858a362 100644
--- a/spec/requests/api/environments_spec.rb
+++ b/spec/requests/api/environments_spec.rb
@@ -136,6 +136,33 @@
         expect(json_response['external']).to be nil
       end
 
+      context 'when associating a cluster agent' do
+        let_it_be(:cluster_agent) { create(:cluster_agent, project: project) }
+        let_it_be(:foreign_cluster_agent) { create(:cluster_agent) }
+
+        it 'creates a environment with associated cluster agent' do
+          post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", cluster_agent_id: cluster_agent.id }
+
+          expect(response).to have_gitlab_http_status(:created)
+          expect(response).to match_response_schema('public_api/v4/environment')
+          expect(json_response['cluster_agent']).to be_present
+        end
+
+        it 'fails to create environment with a non existing cluster agent' do
+          post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", cluster_agent_id: non_existing_record_id }
+
+          expect(response).to have_gitlab_http_status(:bad_request)
+          expect(json_response['message']).to eq("400 Bad request - cluster agent doesn't exist or cannot be associated with this environment")
+        end
+
+        it 'fails to create environment with a foreign cluster agent' do
+          post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", cluster_agent_id: foreign_cluster_agent.id }
+
+          expect(response).to have_gitlab_http_status(:bad_request)
+          expect(json_response['message']).to eq("400 Bad request - cluster agent doesn't exist or cannot be associated with this environment")
+        end
+      end
+
       it 'returns 200 HTTP status when using JOB-TOKEN auth' do
         job = create(:ci_build, :running, project: project, user: user)
 
@@ -231,8 +258,9 @@
   end
 
   describe 'PUT /projects/:id/environments/:environment_id' do
+    let_it_be(:url) { 'https://mepmep.whatever.ninja' }
+
     it 'returns a 200 if external_url is changed' do
-      url = 'https://mepmep.whatever.ninja'
       put api("/projects/#{project.id}/environments/#{environment.id}", user),
         params: { external_url: url }
 
@@ -259,6 +287,53 @@
       expect(response).to have_gitlab_http_status(:ok)
     end
 
+    context 'when associating a cluster agent' do
+      let_it_be(:cluster_agent) { create(:cluster_agent, project: project) }
+      let_it_be(:foreign_cluster_agent) { create(:cluster_agent) }
+
+      it 'updates an environment with associated cluster agent' do
+        put api("/projects/#{project.id}/environments/#{environment.id}", user), params: { cluster_agent_id: cluster_agent.id }
+
+        expect(response).to have_gitlab_http_status(:ok)
+        expect(response).to match_response_schema('public_api/v4/environment')
+        expect(json_response['cluster_agent']).to be_present
+      end
+
+      it 'updates an environment to remove cluster agent' do
+        environment.update!(cluster_agent_id: cluster_agent.id)
+
+        put api("/projects/#{project.id}/environments/#{environment.id}", user), params: { cluster_agent_id: nil }
+
+        expect(response).to have_gitlab_http_status(:ok)
+        expect(response).to match_response_schema('public_api/v4/environment')
+        expect(json_response['cluster_agent']).not_to be_present
+      end
+
+      it 'leaves cluster agent unchanged when not specified in update' do
+        environment.update!(cluster_agent_id: cluster_agent.id)
+
+        put api("/projects/#{project.id}/environments/#{environment.id}", user), params: { external_url: url }
+
+        expect(response).to have_gitlab_http_status(:ok)
+        expect(response).to match_response_schema('public_api/v4/environment')
+        expect(json_response['cluster_agent']).to be_present
+      end
+
+      it 'fails to create environment with a non existing cluster agent' do
+        put api("/projects/#{project.id}/environments/#{environment.id}", user), params: { cluster_agent_id: non_existing_record_id }
+
+        expect(response).to have_gitlab_http_status(:bad_request)
+        expect(json_response['message']).to eq("400 Bad request - cluster agent doesn't exist or cannot be associated with this environment")
+      end
+
+      it 'fails to create environment with a foreign cluster agent' do
+        put api("/projects/#{project.id}/environments/#{environment.id}", user), params: { cluster_agent_id: foreign_cluster_agent.id }
+
+        expect(response).to have_gitlab_http_status(:bad_request)
+        expect(json_response['message']).to eq("400 Bad request - cluster agent doesn't exist or cannot be associated with this environment")
+      end
+    end
+
     it "won't allow slug to be changed" do
       slug = environment.slug
       api_url = api("/projects/#{project.id}/environments/#{environment.id}", user)