diff --git a/app.go b/app.go
index 2f2e8b76055aa48c8dedceccab3b1f27a846deba..08cd160f47566b06b44eeb44044e416c8263ad8d 100644
--- a/app.go
+++ b/app.go
@@ -54,11 +54,8 @@ func serve(sig chan os.Signal, db *leveldb.DB) error {
 			confEnv[hCaptchaSiteKey][0], confEnv[hCaptchaSecretKey][0])
 	}
 
-	// /register
-	routeRegister(app, db, captcha)
-
-	// /hcaptcha-site-key
-	routeHCaptchaSiteKey(app, !hCaptchaEnable, conf[hCaptchaSiteKey])
+	routeHCaptchaSiteKey(app, "/api", !hCaptchaEnable, conf[hCaptchaSiteKey])
+	routeRegister(app, "/api/register", db, captcha)
 
 	// graceful shutdown
 	go func() {
diff --git a/captcha.go b/captcha.go
index f2d517d514d49c5f41f1f7d69333fe2023b58f15..d8e0612edd61ce1db4e437aa563c7f6c8aa3287e 100644
--- a/captcha.go
+++ b/captcha.go
@@ -12,7 +12,7 @@ type respHSiteKey struct {
 // Route to expose hCaptcha site key.
 // Returns a constant pre-generated response
 // to avoid unnecessary allocations or serialisations
-func routeHCaptchaSiteKey(app *fiber.App, stub bool, siteKey string) {
+func routeHCaptchaSiteKey(app *fiber.App, p string, stub bool, siteKey string) {
 	var resp string
 	if stub {
 		resp = mustConstResp(newMessage(false, "hCaptcha is not enabled on this instance."))
@@ -20,7 +20,7 @@ func routeHCaptchaSiteKey(app *fiber.App, stub bool, siteKey string) {
 		resp = mustConstResp(respHSiteKey{true, siteKey})
 	}
 
-	app.Get("/captcha", func(c fiber.Ctx) error {
+	app.Get(p, func(c fiber.Ctx) error {
 		c.Set("Content-Type", "application/json; charset=utf-8")
 		return c.SendString(resp)
 	})
diff --git a/register.go b/register.go
index 02b4f60031e328308469e0fa080950fd8192bb3f..a27fb2497b8bc310a7adc18e48cc2a7fa4caac14 100644
--- a/register.go
+++ b/register.go
@@ -15,18 +15,16 @@ type registration struct {
 }
 
 // Waitlist registration route
-func routeRegister(app *fiber.App, db *leveldb.DB, captcha fiber.Handler) {
-	app.Post("/register", func(c fiber.Ctx) error {
+func routeRegister(app *fiber.App, p string, db *leveldb.DB, captcha fiber.Handler) {
+	app.Post(p, func(c fiber.Ctx) error {
 		req := new(registration)
 
 		// Parse and validate the request
 		if err := c.Bind().Body(req); err != nil {
 			if verbose {
-				log.Printf("invalid request from %q: %v", c.IP(), err)
+				log.Printf("invalid json from %q: %v", c.IP(), err)
 			}
-			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
-				"message": "Invalid request",
-			})
+			return c.Status(fiber.StatusBadRequest).JSON(newMessage(false, "Invalid request"))
 		}
 
 		// Validate email format
@@ -34,37 +32,27 @@ func routeRegister(app *fiber.App, db *leveldb.DB, captcha fiber.Handler) {
 			if verbose {
 				log.Printf("invalid email from %q: %s", c.IP(), req.Email)
 			}
-			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
-				"message": "Invalid email format",
-			})
+			return c.Status(fiber.StatusBadRequest).JSON(newMessage(false, "Invalid email address"))
 		}
 
 		// Check if email is already registered
 		if ok, err := db.Has([]byte(req.Email), nil); err != nil {
 			log.Printf("cannot check for existence of email %q: %v", req.Email, err)
-			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
-				"message": "Error checking registration status",
-			})
+			return c.Status(fiber.StatusInternalServerError).JSON(newMessage(false, "Cannot check registration status"))
 		} else if ok {
 			if verbose {
 				log.Printf("duplicate email from %q: %s", c.IP(), req.Email)
 			}
-			return c.Status(fiber.StatusConflict).JSON(fiber.Map{
-				"message": "Email already registered",
-			})
+			return c.Status(fiber.StatusConflict).JSON(newMessage(false, "Email already registered"))
 		}
 
 		// Save the email to the waitlist
 		if err := db.Put([]byte(req.Email), []byte{'x'}, nil); err != nil {
 			log.Printf("cannot register email %q: %v", req.Email, err)
-			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
-				"message": "Error registering email",
-			})
+			return c.Status(fiber.StatusInternalServerError).JSON(newMessage(false, "Cannot register email"))
 		}
 
 		log.Printf("registered email %q", req.Email)
-		return c.JSON(fiber.Map{
-			"message": "Email registered successfully",
-		})
+		return c.JSON(newMessage(true, "Email registered successfully"))
 	}, captcha)
 }