diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/AddInvitedRegistrationCall.java b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/AddInvitedRegistrationCall.java
index 0aefd7168c141f47a806b24aece170d997eaadee..d0a1ad4ce706082acb6fc4ead041c149421370b3 100644
--- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/AddInvitedRegistrationCall.java
+++ b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/AddInvitedRegistrationCall.java
@@ -22,7 +22,8 @@ public class AddInvitedRegistrationCall extends BaseGmsCall {
 
         String endpoint = "invited-registration";
 
-        String bodyParams = "token_hash=" + tokenHash
+        // plus symbol in token hash is encoded to %2B, otherwise it will be interpreted as space
+        String bodyParams = "token_hash=" + tokenHash.replace("+", "%2B")
                 + "&email=" + email + "&groups="
                 + String.join("\n", groupsPermissions.entrySet()
                         .stream().map(e -> e.getKey() + " " + e.getValue())
diff --git a/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/GmsClientTest.java b/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/GmsClientTest.java
index 54ce5b82212e534fc7ff1a6ba5a2125dd2a4da86..b086de3f764f6649034f9779f2076622efe86429 100644
--- a/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/GmsClientTest.java
+++ b/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/GmsClientTest.java
@@ -7,14 +7,23 @@ import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.net.http.HttpClient;
 import java.net.http.HttpRequest;
+import java.net.http.HttpRequest.BodyPublisher;
 import java.net.http.HttpResponse;
+import java.net.http.HttpResponse.BodySubscriber;
+import java.net.http.HttpResponse.BodySubscribers;
+import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Flow;
+import java.util.concurrent.Flow.Subscriber;
 import static org.junit.Assert.assertEquals;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.AdditionalMatchers;
 import org.mockito.ArgumentMatcher;
 import org.mockito.ArgumentMatchers;
 import static org.mockito.ArgumentMatchers.any;
@@ -145,6 +154,72 @@ public class GmsClientTest {
         verify(httpClient, times(1)).sendAsync(endpointEq("DELETE", "permission/LBT.INAF?user_id=user"), any());
     }
 
+    @Test
+    public void testInvitedRegistration() {
+
+        CompletableFuture response = CompletableFuture.completedFuture(getMockedResponse(201));
+
+        when(httpClient.sendAsync(any(), any())).thenReturn(response);
+        Map<String, Permission> permissionsMap = new HashMap<>();
+        permissionsMap.put("group1", Permission.MANAGE_MEMBERS);
+        permissionsMap.put("group2", Permission.MANAGE_MEMBERS);
+        client.addInvitedRegistration("bvjsgqu423", "email", permissionsMap);
+        // hash = AOyojiwaRR7BHPde6Tomg3+BMoQQggNM3wUHEarXuNQ=
+
+        verify(httpClient, times(1)).sendAsync(
+                AdditionalMatchers.and(
+                        endpointEq("POST", "invited-registration"),
+                        ArgumentMatchers.argThat(req -> {
+                            String reqbody = req.bodyPublisher().map(p -> {
+                                var bodySubscriber = BodySubscribers.ofString(StandardCharsets.UTF_8);
+                                var flowSubscriber = new StringSubscriber(bodySubscriber);
+                                p.subscribe(flowSubscriber);
+                                return bodySubscriber.getBody().toCompletableFuture().join();
+                            }).get();
+
+                            // If the resulting hash contains a + symbol it has to be encoded to %2B,
+                            // otherwise it will be interpreted as a space and wrong value will be
+                            // stored into the database
+                            String expectedBody = "token_hash=AOyojiwaRR7BHPde6Tomg3%2BBMoQQggNM3wUHEarXuNQ="
+                                    + "&email=email&groups=group2 MANAGE_MEMBERS\n"
+                                    + "group1 MANAGE_MEMBERS";
+
+                            return reqbody.equals(expectedBody);
+                        })), any());
+    }
+
+    /**
+     * Credit: https://stackoverflow.com/a/55816685/771431
+     */
+    static final class StringSubscriber implements Flow.Subscriber<ByteBuffer> {
+
+        final BodySubscriber<String> wrapped;
+
+        StringSubscriber(BodySubscriber<String> wrapped) {
+            this.wrapped = wrapped;
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            wrapped.onSubscribe(subscription);
+        }
+
+        @Override
+        public void onNext(ByteBuffer item) {
+            wrapped.onNext(List.of(item));
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            wrapped.onError(throwable);
+        }
+
+        @Override
+        public void onComplete() {
+            wrapped.onComplete();
+        }
+    }
+
     private HttpResponse getMockedResponse(int statusCode, String body) {
         HttpResponse response = getMockedResponse(statusCode);
         InputStream in = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8));