diff --git a/guix-jupyter-kernel.scm b/guix-jupyter-kernel.scm
index 3a544445ea9e5856276485d9cff9c9221070b35d..da7a8d5f82a48792ea0600cf99e49d694c024112 100644
--- a/guix-jupyter-kernel.scm
+++ b/guix-jupyter-kernel.scm
@@ -229,8 +229,8 @@ stripped."
                             ,@(alist-delete "code" content))))))
 
 (define (reply-execute-request kernel kind message state)
-  (let* ((content (message-content message))
-         (code    (assoc-ref (json-string->scm content) "code"))
+  (let* ((request (json->execute-request (message-content message)))
+         (code    (execute-request-code request))
          (line    (string-take code
                                (or (string-index code #\newline)
                                    (string-length code))))
diff --git a/jupyter/messages.scm b/jupyter/messages.scm
index 0a8a81797707f8da4b9d7575cf4091cd01eb69aa..50e8b2ae33758646625832d164ee718b0e35f49a 100644
--- a/jupyter/messages.scm
+++ b/jupyter/messages.scm
@@ -78,7 +78,17 @@
             kernel-status
             kernel-status-execution-state
             json->kernel-status
-            kernel-status->json))
+            kernel-status->json
+
+            execute-request?
+            execute-request
+            execute-request-code
+            execute-request-silent?
+            execute-request-store-history?
+            execute-request-allow-stdin?
+            execute-request-stop-on-error?
+            json->execute-request
+            execute-request->json))
 
 ;;; Commentary:
 ;;;
@@ -309,3 +319,18 @@ This is a low-level procedure for internal use."
   (execution-state  kernel-status-execution-state
                     (json "execution_state" string->symbol symbol->string)
                     (default 'idle)))
+
+(define-json-mapping <execute-request> execute-request
+  make-execute-request
+  execute-request?
+  json->execute-request <=> execute-request->json
+  (code             execute-request-code)
+  (silent?          execute-request-silent?
+                    (json "silent") (default #f))
+  (store-history?   execute-request-store-history?
+                    (json "store_history") (default #t))
+  ;; TODO: Add "user_expressions" dictionary.
+  (allow-stdin?     execute-request-allow-stdin?
+                    (json "allow_stdin") (default #t))
+  (stop-on-error?   execute-request-stop-on-error?
+                    (json "stop_on_error") (default #f)))
diff --git a/tests/kernels.scm b/tests/kernels.scm
index 494f9d7108ae883795510e58cacdcf2d6de639cf..fdbde44effa3ca32f0baf7fd0731253fabc42d0f 100644
--- a/tests/kernels.scm
+++ b/tests/kernels.scm
@@ -100,8 +100,8 @@
   42
   (let ((request (message (header "execute_request" "luser" "12345")
                           (scm->json-string
-                           '((code . "40 + 2\n")
-                             (silent . #f))))))
+                           (execute-request->json
+                            (execute-request (code "40 + 2\n")))))))
     (send-message %kernel request)
 
     ;; As noted at