diff --git a/README.md b/README.md
index 8410979d9a4493d27e0df7cd15de1b81e022b71e..8157c32586d6b28aeeda3744d7af0c3ea56ad564 100644
--- a/README.md
+++ b/README.md
@@ -27,4 +27,4 @@ and then trust the generated certificate ```dotnet dev-certs https --trust```
 
 ## Testing the new server
 
-The server ist now available under: https://localhost:6000/ (port may be different!) 
\ No newline at end of file
+The server ist now available under: https://localhost:6000/ (port may be different!)     
diff --git a/src/Project.sln b/src/Project.sln
index 9ca995957837dec82b0f967591d4a59dc55ab48f..fd1c0cab4653b543e9482c36f00e8ecd06eb68bb 100644
--- a/src/Project.sln
+++ b/src/Project.sln
@@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio Version 16
 VisualStudioVersion = 16.0.28803.156
 MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Project", "Project\Project.csproj", "{16C4EBA5-BA87-45EC-AE1A-E8569A897959}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Project", "Project\Project.csproj", "{16C4EBA5-BA87-45EC-AE1A-E8569A897959}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Project.Tests", "Project.Tests\Project.Tests.csproj", "{EEE96892-A211-44EE-B2B8-11FAB31F2E26}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Project.Tests", "Project.Tests\Project.Tests.csproj", "{EEE96892-A211-44EE-B2B8-11FAB31F2E26}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/src/Project/Controllers/ProjectController.cs b/src/Project/Controllers/ProjectController.cs
index aa92bccaca91faf284bcd3e843f5ca974c643759..d557f6cf43494e46c35b18fd8aad1625122f1052 100644
--- a/src/Project/Controllers/ProjectController.cs
+++ b/src/Project/Controllers/ProjectController.cs
@@ -1,5 +1,6 @@
 using Coscine.Action;
 using Coscine.Action.EventArgs;
+using Coscine.Action.Utils;
 using Coscine.Api.Project.ParameterObjects;
 using Coscine.Api.Project.ReturnObjects;
 using Coscine.ApiCommons;
@@ -15,6 +16,7 @@ using Coscine.ResourceLoader;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Logging;
+using Newtonsoft.Json.Linq;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -39,7 +41,13 @@ namespace Coscine.Api.Project.Controllers
         private readonly ResourceModel _resourceModel;
         private readonly CoscineLogger _coscineLogger;
         private readonly VisibilityModel _visibilityModel;
+        private readonly InvitationModel _invitationModel;
+        private readonly RoleModel _roleModel;
+        private readonly UserModel _userModel;
         private readonly int _maxAvailable = 100;
+        private readonly string _userUrlPrefix = "https://purl.org/coscine/users";
+        private readonly Uri _orgPrefixUrl = new Uri("http://www.w3.org/ns/org#");
+        private readonly RdfStoreConnector _rdfStoreConnector;
 
         /// <summary>
         /// ProjectController constructor
@@ -58,6 +66,10 @@ namespace Coscine.Api.Project.Controllers
             _projectQuotaModel = new ProjectQuotaModel();
             _coscineLogger = new CoscineLogger(logger);
             _visibilityModel = new VisibilityModel();
+            _rdfStoreConnector = new RdfStoreConnector(Program.Configuration.GetString("coscine/local/virtuoso/additional/url"));
+            _invitationModel = new InvitationModel();
+            _roleModel = new RoleModel();
+            _userModel = new UserModel();
         }
 
         /// <summary>
@@ -506,6 +518,11 @@ namespace Coscine.Api.Project.Controllers
                     _projectQuotaModel.Delete(projectQuota);
                 }
 
+                foreach (var invitation in _invitationModel.GetAllWhere((x) => x.Project == project.Id))
+                {
+                    _invitationModel.Delete(invitation);
+                }
+
                 _activatedFeaturesModel.DeactivateAllFeatures(project);
 
                 if (propegateAction)
@@ -532,7 +549,6 @@ namespace Coscine.Api.Project.Controllers
         public IActionResult Store()
         {
             var user = _authenticator.GetUser();
-            var isRWTHMember = IsRWTHMember(user);
             var projectObject = ObjectFactory<ProjectObject>.DeserializeFromStream(Request.Body);
 
             if (projectObject?.ParentId != new Guid()
@@ -541,7 +557,7 @@ namespace Coscine.Api.Project.Controllers
                 return Unauthorized("User is not allowed to create SubProjects.");
             }
 
-            var project = _projectModel.StoreFromObject(projectObject, user, isRWTHMember);
+            var project = _projectModel.StoreFromObject(projectObject, user, _rdfStoreConnector.GetQuotaDefault(user.Id.ToString()));
 
             if (projectObject.ParentId != new Guid()
                 // for now, only an owner can add subprojects to projects
@@ -563,26 +579,199 @@ namespace Coscine.Api.Project.Controllers
         }
 
         /// <summary>
-        /// Checks if the given user is a member of the RWTH 
+        /// List all invitations of a project.
         /// </summary>
-        /// <param name="user">User object</param>
-        /// <returns>True, if member of RWTH or false, if not a member of RWTH</returns>
-        private bool IsRWTHMember(User user)
+        /// <param name="projectId">Project id of the project</param>
+        /// <returns>List of invitations</returns>
+        [HttpGet("[controller]/invitation/list/{projectId}")]
+        public ActionResult<IEnumerable<InvitationReturnObject>> ListInvitations(Guid projectId)
         {
-            var externalIds = new ExternalIdModel().GetAllWhere((externalId) => externalId.UserId == user.Id);
-            if (!externalIds.Any())
+            var project = _projectModel.GetById(projectId);
+
+            if (project == null)
             {
-                return false;
+                return NotFound($@"The project ""{projectId}"" was not found.");
+            }
+
+            var user = _authenticator.GetUser();
+
+            if (!_projectModel.HasAccess(user, project, UserRoles.Owner))
+            {
+                return Unauthorized($"You are not an owner of the project.");
+            }
+
+            var invitations = _invitationModel.GetAllWhere(x => x.Project == projectId && x.Expiration > DateTime.UtcNow)
+                .Select(x => new InvitationReturnObject
+                {
+                    Id = x.Id,
+                    Expiration = x.Expiration,
+                    Issuer = x.Issuer,
+                    ProjectId = x.Project,
+                    RoleId = x.Role,
+                    UserMail = x.InviteeEmail 
+                });
+
+            return new ActionResult<IEnumerable<InvitationReturnObject>>(invitations);
+        }
+
+        /// <summary>
+        /// Create and send an invitation to specified mail.
+        /// </summary>
+        /// <param name="sendInvitationObject">Informations for sending an invitation</param>
+        /// <returns>NoContent</returns>
+        [HttpPost("[controller]/invitation")]
+        public IActionResult SendInvitation(SendInvitationObject sendInvitationObject)
+        {
+            var user = _authenticator.GetUser();
+
+            if (!IsValidEmail(sendInvitationObject.Mail))
+            {
+                return BadRequest($@"The email ""{sendInvitationObject.Mail}"" is invalid.");
+            }
+
+            var project = _projectModel.GetById(sendInvitationObject.Project);
+
+            if (project == null)
+            {
+                return NotFound($@"The project ""{sendInvitationObject.Project}"" was not found.");
+            }
+
+            if (_roleModel.GetById(sendInvitationObject.Role) == null)
+            {
+                return NotFound($@"The role ""{sendInvitationObject.Role}"" was not found.");
+            }
+
+            if (!_projectModel.HasAccess(user, project, UserRoles.Owner))
+            {
+                return Unauthorized($"You are not an owner of the project.");
+            }
+
+            var invitations = _invitationModel.GetAllWhere(
+                x => x.Project == sendInvitationObject.Project &&
+                     x.InviteeEmail == sendInvitationObject.Mail &&
+                     x.Expiration > DateTime.UtcNow
+            );
+
+            if (invitations != null && invitations.Any())
+            {
+                return BadRequest("This invitee already has a valid invitation to this project.");
+            }
+
+            var token = _invitationModel.CreateInvitation(sendInvitationObject.Project, user.Id, sendInvitationObject.Role, sendInvitationObject.Mail);
+
+            var body = new JObject
+            {
+                ["Args"] = new JObject()
+                {
+                    ["placeholder"] = new JObject()
+                    {
+                        ["confirmation_link"] = $@"{_configuration.GetString("coscine/local/api/additional/url")}/invitation?token={token}"
+                    }
+                }
+            };
+
+            NotificationBusUtil.Send(Program.Configuration, "user_invitation", NotificationBusUtil.GetUserList(new User { EmailAddress = sendInvitationObject.Mail }), sendInvitationObject.Project.ToString(), body);
+
+            return NoContent();
+        }
+
+        /// <summary>
+        /// Deletes an invitation.
+        /// </summary>
+        /// <param name="invitationId">Id of a invitation</param>
+        /// <returns>NoContent</returns>
+        [HttpDelete("[controller]/invitation/{invitationId}")]
+        public IActionResult DeleteInvitation(Guid invitationId)
+        {
+            var invitation = _invitationModel.GetById(invitationId);
+
+            if(invitation == null)
+            {
+                return NotFound("Invitation was not found.");
             }
-            var externalIdList = new List<string>();
 
-            foreach (var externalId in externalIds)
+            var user = _authenticator.GetUser();
+
+            if (!_projectModel.HasAccess(user, _projectModel.GetById(invitation.Project), UserRoles.Owner))
             {
-                externalIdList.Add(externalId.ExternalId1);
+                return Unauthorized($"You are not an owner of this project.");
             }
-            return new RdfStoreConnector(Program.Configuration.GetStringAndWait("coscine/local/virtuoso/additional/url")).GetTriples(new Uri("https://ror.org/04xfq0f34"), null, null, 1, externalIdList).Any();
+
+            _invitationModel.Delete(invitation);
+
+            return NoContent();
         }
 
+        /// <summary>
+        /// Resolve an invitation for the current user.
+        /// </summary>
+        /// <param name="token">Token of a invitation</param>
+        /// <returns>NoContent</returns>
+        [HttpGet("[controller]/invitation/resolve/{token}")]
+        public IActionResult ResolveInvitation(Guid token)
+        {
+            var user = _authenticator.GetUser();
+
+            var invitation = _invitationModel.GetByToken(token);
+
+            if(invitation == null)
+            {
+                return NotFound("Invitation was not found.");
+            }
+
+            if (invitation.Expiration < DateTime.UtcNow)
+            {
+                return BadRequest("The invitation has expired");
+            }
+
+            var project = _projectModel.GetById(invitation.Project);
+
+            if (!_projectModel.HasAccess(_userModel.GetById(invitation.Issuer), project, UserRoles.Owner))
+            {
+                return Unauthorized($"The issuer is not an owner of the project.");
+            }
+
+            if (_projectRoleModel.GetAllWhere(x => x.ProjectId == invitation.Project && x.UserId == user.Id).Any())
+            {
+                return BadRequest($"The invitee is already part of the project.");
+            }
+
+            var role = _roleModel.GetById(invitation.Role);
+
+            _emitter.EmitUserAdd(new UserEventArgs(_configuration)
+            {
+                Project = project,
+                Role = role,
+                User = user,
+            });
+
+            var projectRole = new ProjectRole()
+            {
+                RelationId = Guid.NewGuid(),
+                ProjectId = invitation.Project,
+                UserId = user.Id,
+                RoleId = invitation.Role
+            };
+
+            _projectRoleModel.Insert(projectRole);
+
+            _invitationModel.Delete(invitation);
+
+            return Ok($"User {user.Id} is now {role.DisplayName} of project {project.Id}.");
+        }
+
+        private static bool IsValidEmail(string email)
+        {
+            try
+            {
+                return new System.Net.Mail.MailAddress(email).Address == email;
+            }
+            catch
+            {
+                return false;
+            }
+        }
+        
         private void LogAnalyticsViewHome(List<string> projectIds)
         {
             _coscineLogger.AnalyticsLog(
diff --git a/src/Project/ParameterObjects/SendInvitationObject.cs b/src/Project/ParameterObjects/SendInvitationObject.cs
new file mode 100644
index 0000000000000000000000000000000000000000..04fc270a95df192d272697b2b4dd743ae06cbf23
--- /dev/null
+++ b/src/Project/ParameterObjects/SendInvitationObject.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace Coscine.Api.Project.ParameterObjects
+{
+    /// <summary>
+    ///  Parameter object containing the invitation informations.
+    /// </summary>
+    public class SendInvitationObject
+    {
+        /// <summary>
+        ///  Id of the project
+        /// </summary>
+        public Guid Project { get; set; }
+        /// <summary>
+        ///  Id of the target role
+        /// </summary>
+        public Guid Role { get; set; }
+        /// <summary>
+        ///  Email of the target user
+        /// </summary>
+        public string Mail { get; set; }
+    }
+}
diff --git a/src/Project/ReturnObjects/InvitationReturnObject.cs b/src/Project/ReturnObjects/InvitationReturnObject.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4241c6e3957ebecb99cd8df74d01870644a5e637
--- /dev/null
+++ b/src/Project/ReturnObjects/InvitationReturnObject.cs
@@ -0,0 +1,35 @@
+using System;
+
+namespace Coscine.Api.Project.ReturnObjects
+{
+    /// <summary>
+    /// Return object for an invitation.
+    /// </summary>
+    public class InvitationReturnObject
+    {
+        /// <summary>
+        /// The invitation id.
+        /// </summary>
+        public Guid Id { get; set; }
+        /// <summary>
+        /// When the invite will expire.
+        /// </summary>
+        public DateTime Expiration { get; set; }
+        /// <summary>
+        /// Email of the invitee.
+        /// </summary>
+        public string UserMail { get; set; }
+        /// <summary>
+        /// Id of the issuer.
+        /// </summary>
+        public Guid Issuer { get; set; }
+        /// <summary>
+        /// Id of the project.
+        /// </summary>
+        public Guid ProjectId { get; set; }
+        /// <summary>
+        /// Id of the target Role.
+        /// </summary>
+        public Guid RoleId { get; set; }
+    }
+}