diff --git a/src/dataset.ml b/src/dataset.ml
index 6ce481846cd19043eea6f79a4265e965fe684d8c..456981ec3794394be178be1b57ce9f50d6ca7cbb 100644
--- a/src/dataset.ml
+++ b/src/dataset.ml
@@ -23,9 +23,13 @@
 open Why3
 open Base
 
+let float_of_real_constant rc =
+  Float.of_string (Fmt.str "%a" Number.(print_real_constant full_support) rc)
+
 type normalization =
   | MinMax of float * float (* min-max scaling *)
   | Znorm of float * float (* z-normalization (mean, standard deviation) *)
+[@@deriving yojson, show]
 
 let require_clipping = function MinMax _ -> true | Znorm _ -> false
 
@@ -45,9 +49,6 @@ let apply_normalization csv ds =
     class_ :: features)
 
 let interpret_normalization =
-  let float_of_real_constant rc =
-    Float.of_string (Fmt.str "%a" Number.(print_real_constant full_support) rc)
-  in
   Trans.fold_decl
     (fun decl acc ->
       match decl.d_node with
@@ -83,12 +84,13 @@ let interpret_normalization =
       | _ -> acc)
     []
 
-type eps = Constant.constant
+type eps = float [@@deriving yojson, show]
 
 type property =
   | Correct
   | Robust of eps
   | CondRobust of eps
+[@@deriving yojson, show]
 
 type ('a, 'b) predicate = {
   model : 'a;
@@ -97,7 +99,7 @@ type ('a, 'b) predicate = {
   property : property;
 }
 
-let string_of_eps eps = Fmt.str "%a" Constant.print_def eps
+let string_of_eps eps = Float.to_string eps
 
 let string_of_predicate_kind = function
   | Correct -> "correct"
@@ -131,13 +133,14 @@ let interpret_predicate env ~on_model ~on_dataset task =
         if Term.ls_equal ls correct_predicate
         then Correct
         else failwith_unsupported_ls ls
-      | [ { t_node = Tconst (Constant.ConstReal _ as e); _ } ] ->
+      | [ { t_node = Tconst (Constant.ConstReal e); _ } ] ->
         let robust_predicate = find_predicate_ls env "robust" in
         let cond_robust_predicate = find_predicate_ls env "cond_robust" in
+        let f = float_of_real_constant e in
         if Term.ls_equal ls robust_predicate
-        then Robust e
+        then Robust f
         else if Term.ls_equal ls cond_robust_predicate
-        then CondRobust e
+        then CondRobust f
         else failwith_unsupported_ls ls
       | _ -> failwith_unsupported_term term
     in
diff --git a/src/dataset.mli b/src/dataset.mli
index 5efbdb9b017720a70e2c04d9aa9bacc988bac23c..e1465f7bf029ec4d2adff8386e6263dff225490d 100644
--- a/src/dataset.mli
+++ b/src/dataset.mli
@@ -22,14 +22,15 @@
 
 open Why3
 
-type eps
+type eps [@@deriving yojson, show]
 
 type property = private
   | Correct
   | Robust of eps
   | CondRobust of eps
+[@@deriving yojson, show]
 
-type normalization
+type normalization [@@deriving yojson, show]
 
 type ('model, 'dataset) predicate = private {
   model : 'model;
diff --git a/src/main.ml b/src/main.ml
index 6b22ab77bc2553b58b3faecdc3ff0000641fb1b9..42ae008f8d195cb3dca6c7ca264a7c207226c0a3 100644
--- a/src/main.ml
+++ b/src/main.ml
@@ -130,6 +130,33 @@ let verify format loadpath memlimit timelimit prover dataset_csv files =
          ?dataset_csv)
     files
 
+let verify_json format loadpath memlimit timelimit file =
+  let json_config =
+    match
+      Verification.File.json_config_of_yojson (Yojson.Safe.from_file file)
+    with
+    | Ok json_config -> json_config
+    | Error msg ->
+      invalid_arg
+        (Fmt.str "Unrecognized JSON configuration in file '%s' (%s)" file msg)
+    | exception Yojson.Json_error msg ->
+      failwith
+        (Fmt.str "Unexpected error while parsing JSON file '%s' (%s)" file msg)
+  in
+  let dataset_csv =
+    (* The call to [verify] assumes [dataset_csv] to exist. For the [verify_cmd]
+       case, this property is ensured by the Cmdliner [file] converter. Here,
+       since the dataset string is inside a json, we need to perform such
+       checking by hand. *)
+    if Caml.Sys.file_exists json_config.property.dataset
+    then Some json_config.property.dataset
+    else
+      failwith
+        (Fmt.str "No '%s' file or directory" json_config.property.dataset)
+  in
+  verify format loadpath memlimit timelimit json_config.prover dataset_csv
+    [ Verification.File.of_json_config json_config ]
+
 let exec_cmd cmdname cmd =
   Logs.debug (fun m -> m "Command `%s'" cmdname);
   cmd ()
@@ -232,6 +259,31 @@ let verify_cmd =
   in
   Cmd.v info term
 
+let verify_json_cmd =
+  let cmdname = "verify-json" in
+  let info =
+    let doc =
+      "Property verification using external provers via json configuration \
+       file."
+    in
+    Cmd.info cmdname ~sdocs:Manpage.s_common_options ~exits:Cmd.Exit.defaults
+      ~doc
+      ~man:[ `S Manpage.s_description; `P doc ]
+  in
+  let term =
+    let json =
+      let doc = "JSON file." in
+      Arg.(
+        required & pos ~rev:true 0 (some file) None & info [] ~doc ~docv:"FILE")
+    in
+    Term.(
+      const (fun format loadpath memlimit timelimit json _ ->
+        exec_cmd cmdname (fun () ->
+          verify_json format loadpath memlimit timelimit json))
+      $ format $ loadpath $ memlimit $ timelimit $ json $ setup_logs)
+  in
+  Cmd.v info term
+
 let default_info =
   let doc =
     "A platform for characterizing the safety and robustness of artificial \
@@ -265,7 +317,8 @@ let () =
 
 let () =
   try
-    Cmd.group ~default:default_cmd default_info [ config_cmd; verify_cmd ]
+    Cmd.group ~default:default_cmd default_info
+      [ config_cmd; verify_cmd; verify_json_cmd ]
     |> Cmd.eval ~catch:false |> Caml.exit
   with exn when not (log_level_is_debug ()) ->
     Logs.err (fun m -> m "@[%a@]" Why3.Exn_printer.exn_printer exn)
diff --git a/src/prover.ml b/src/prover.ml
index 517d4f447504bdac64e80191001cff15679417b1..847cc3e20e0896f11deb8cf00cf636834185642a 100644
--- a/src/prover.ml
+++ b/src/prover.ml
@@ -25,6 +25,7 @@ type t =
   | Pyrat
   | Saver
   | CVC5
+[@@deriving yojson, show]
 
 let list_available () = [ Marabou; Pyrat; Saver; CVC5 ]
 
diff --git a/src/prover.mli b/src/prover.mli
index 36ddf1a72a47776dc3ec2322a3359fed09011af2..1ae811a671fa5d950f8c60f1f9baa0bfd4d9192d 100644
--- a/src/prover.mli
+++ b/src/prover.mli
@@ -25,6 +25,7 @@ type t = private
   | Pyrat
   | Saver
   | CVC5
+[@@deriving yojson, show]
 
 val list_available : unit -> t list
 val of_string : string -> t option
diff --git a/src/verification.ml b/src/verification.ml
index 47f418bef37c0eca9ef9fb589accc25d3f4c0bd7..eeef52de9d03823cb31d6477add9fd9d09dd3700 100644
--- a/src/verification.ml
+++ b/src/verification.ml
@@ -25,9 +25,26 @@ open Base
 module Filename = Caml.Filename
 
 module File = struct
+  type json_config = {
+    prover : Prover.t;
+    model : string;
+    property : property;
+  }
+  [@@deriving yojson, show]
+
+  and property = {
+    dataset : string;
+    normalization : Dataset.normalization list;
+    kind : Dataset.property;
+  }
+  [@@deriving yojson, show]
+
   type t =
     | Stdin
     | File of string
+    | Json of json_config
+
+  let of_json_config json = Json json
 
   let of_string s =
     if String.equal s "-"
@@ -39,6 +56,7 @@ module File = struct
   let pretty fmt = function
     | Stdin -> Fmt.string fmt "-"
     | File f -> Fmt.string fmt f
+    | Json j -> Fmt.fmt "%a" fmt pp_json_config j
 end
 
 let () =
@@ -173,6 +191,13 @@ let call_prover ~limit config env prover config_prover driver dataset_csv task =
       Fmt.(option ~none:nop (any " " ++ string))
       additional_info)
 
+let mstr_theory_of_json_config env _json_config =
+  let name = "T" in
+  let th_uc = Theory.create_theory (Ident.id_fresh name) in
+  let th_real = Env.read_theory env [ "real" ] "Real" in
+  let th_uc = Theory.use_export th_uc th_real in
+  Wstdlib.Mstr.singleton name (Theory.close_theory th_uc)
+
 let verify ?(debug = false) ?format ~loadpath ?memlimit ?timelimit prover
   ?dataset_csv file =
   if debug then Debug.set_flag (Debug.lookup_flag "call_prover");
@@ -188,13 +213,14 @@ let verify ?(debug = false) ?format ~loadpath ?memlimit ?timelimit prover
       Call_provers.limit_mem = memlimit;
     }
   in
-  let _, mstr_theory =
+  let mstr_theory =
     match file with
     | File.Stdin ->
-      ("stdin", Env.(read_channel ?format base_language env "stdin" Caml.stdin))
+      Env.(read_channel ?format base_language env "stdin" Caml.stdin)
     | File file ->
       let mlw_files, _ = Env.(read_file ?format base_language env file) in
-      (file, mlw_files)
+      mlw_files
+    | Json json_config -> mstr_theory_of_json_config env json_config
   in
   Wstdlib.Mstr.iter
     (fun _ theory ->
diff --git a/src/verification.mli b/src/verification.mli
index f71abad2969244b51e4d7a3e9ed0b902273cff41..73d527396860e6653c5812552954c5951833b8d5 100644
--- a/src/verification.mli
+++ b/src/verification.mli
@@ -21,8 +21,23 @@
 (**************************************************************************)
 
 module File : sig
+  type json_config = private {
+    prover : Prover.t;
+    model : string;
+    property : property;
+  }
+  [@@deriving yojson, show]
+
+  and property = private {
+    dataset : string;
+    normalization : Dataset.normalization list;
+    kind : Dataset.property;
+  }
+  [@@deriving yojson, show]
+
   type t
 
+  val of_json_config : json_config -> t
   val of_string : string -> [ `Ok of t | `Error of string ]
   val pretty : Format.formatter -> t -> unit
 end