diff --git a/CHANGELOG b/CHANGELOG
index 593e8f77ab47861b830f22d4e647d2e955452a75..3c03b26cf513cc86bcd02fe318ac625afa51fd6d 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -37,6 +37,7 @@ v 8.7.0 (unreleased)
   - ClosingIssueExtractor regex now also works with colons. e.g. "Fixes: #1234" !3591
   - Update number of Todos in the sidebar when it's marked as "Done". !3600
   - API: Expose 'updated_at' for issue, snippet, and merge request notes (Robert Schilling)
+  - API: User can leave a project through the API when not master or owner. !3613
 
 v 8.6.5
   - Fix importing from GitHub Enterprise. !3529
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 3a909a2bc87c051e5bac6aaca32bc208e53d8f23..ab716c229dcde534b1cdf63a5f6f047901bd9196 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -780,8 +780,10 @@ Parameters:
 - `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
 - `user_id` (required) - The ID of a team member
 
-This method is idempotent and can be called multiple times with the same parameters.
-Revoking team membership for a user who is not currently a team member is considered success.
+This method removes the project member if the user has the proper access rights to do so.
+It returns a status code 403 if the member does not have the proper rights to perform this action.
+In all other cases this method is idempotent and revoking team membership for a user who is not
+currently a team member is considered success.
 Please note that the returned JSON currently differs slightly. Thus you should not
 rely on the returned JSON structure.
 
diff --git a/lib/api/project_members.rb b/lib/api/project_members.rb
index c756bb479fc2bc01507b99a50b30fb6bc4f0e974..4aefdf319c68283ce3832dc08e0e57c93033645f 100644
--- a/lib/api/project_members.rb
+++ b/lib/api/project_members.rb
@@ -93,12 +93,17 @@ module API
       # Example Request:
       #   DELETE /projects/:id/members/:user_id
       delete ":id/members/:user_id" do
-        authorize! :admin_project, user_project
         project_member = user_project.project_members.find_by(user_id: params[:user_id])
-        unless project_member.nil?
-          project_member.destroy
-        else
+
+        unless current_user.can?(:admin_project, user_project) ||
+                current_user.can?(:destroy_project_member, project_member)
+          forbidden!
+        end
+
+        if project_member.nil?
           { message: "Access revoked", id: params[:user_id].to_i }
+        else
+          project_member.destroy
         end
       end
     end
diff --git a/spec/requests/api/project_members_spec.rb b/spec/requests/api/project_members_spec.rb
index 4301588b16a122d9313fba4b4d229942d98a8a78..c112ca5e3ca6d0a80b3518686d0b2df1b8066f74 100644
--- a/spec/requests/api/project_members_spec.rb
+++ b/spec/requests/api/project_members_spec.rb
@@ -118,8 +118,10 @@ describe API::API, api: true  do
   end
 
   describe "DELETE /projects/:id/members/:user_id" do
-    before { project_member }
-    before { project_member2 }
+    before do
+      project_member
+      project_member2
+    end
 
     it "should remove user from project team" do
       expect do
@@ -132,6 +134,7 @@ describe API::API, api: true  do
       expect do
         delete api("/projects/#{project.id}/members/#{user3.id}", user)
       end.to_not change { ProjectMember.count }
+      expect(response.status).to eq(200)
     end
 
     it "should return 200 if team member already removed" do
@@ -145,8 +148,19 @@ describe API::API, api: true  do
         delete api("/projects/#{project.id}/members/1000000", user)
       end.to change { ProjectMember.count }.by(0)
       expect(response.status).to eq(200)
-      expect(json_response['message']).to eq("Access revoked")
       expect(json_response['id']).to eq(1000000)
+      expect(json_response['message']).to eq('Access revoked')
+    end
+
+    context 'when the user is not an admin or owner' do
+      it 'can leave the project' do
+        expect do
+          delete api("/projects/#{project.id}/members/#{user3.id}", user3)
+        end.to change { ProjectMember.count }.by(-1)
+
+        expect(response.status).to eq(200)
+        expect(json_response['id']).to eq(project_member2.id)
+      end
     end
   end
 end