diff --git a/CMakeLists.txt b/CMakeLists.txt
index 07ea2346b198fca89882619717aa1f45a7fbf01e..48cfdfb822979c2e01e0031af33171fc280dd4b3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -237,7 +237,7 @@ message(STATUS "[${PROJECT_NAME}] Downloading and building genopts")
 FetchContent_Declare(
   genopts
   GIT_REPOSITORY https://storage.bsc.es/gitlab/utils/genopts
-  GIT_TAG 1dcef400f8fbc6e1969c856ca844707b730c3002 # v0.1.0-pre
+  GIT_TAG c456c2d8ec92f26d9074b123446261103e5c847c # v0.1.0-pre
   GIT_SHALLOW ON
   GIT_PROGRESS ON
 )
diff --git a/examples/cxx/ADM_register_job.cpp b/examples/cxx/ADM_register_job.cpp
index b9621752d956829568cdcab0b4f68f1a80e451e6..634ed2ba7a0239246c264148cf8579ab4be98ed3 100644
--- a/examples/cxx/ADM_register_job.cpp
+++ b/examples/cxx/ADM_register_job.cpp
@@ -59,7 +59,7 @@ main(int argc, char* argv[]) {
                 server, name, scord::adhoc_storage::type::gekkofs,
                 adhoc_storage_ctx);
 
-        scord::job_requirements reqs(inputs, outputs, adhoc_storage);
+        scord::job::requirements reqs(inputs, outputs, adhoc_storage);
 
         [[maybe_unused]] const auto job = scord::register_job(
                 server, scord::job::resources{job_nodes}, reqs, 0);
diff --git a/examples/cxx/ADM_remove_job.cpp b/examples/cxx/ADM_remove_job.cpp
index f4f4100f273011b5fc641a67938d5ccc4302d30c..ca3975f6945d80cc914a21bc406f16ce086a0dac 100644
--- a/examples/cxx/ADM_remove_job.cpp
+++ b/examples/cxx/ADM_remove_job.cpp
@@ -59,7 +59,7 @@ main(int argc, char* argv[]) {
                 server, name, scord::adhoc_storage::type::gekkofs,
                 adhoc_storage_ctx);
 
-        scord::job_requirements reqs(inputs, outputs, adhoc_storage);
+        scord::job::requirements reqs(inputs, outputs, adhoc_storage);
 
         [[maybe_unused]] const auto job = scord::register_job(
                 server, scord::job::resources{job_nodes}, reqs, 0);
diff --git a/examples/cxx/ADM_transfer_datasets.cpp b/examples/cxx/ADM_transfer_datasets.cpp
index 393c3e390e0344121e5b0e2a997cca6ea03b424e..56b5a24cd84da48c52dd00f2bc9d0d8dce97fed2 100644
--- a/examples/cxx/ADM_transfer_datasets.cpp
+++ b/examples/cxx/ADM_transfer_datasets.cpp
@@ -66,7 +66,7 @@ main(int argc, char* argv[]) {
                 server, name, scord::adhoc_storage::type::gekkofs,
                 adhoc_storage_ctx);
 
-        scord::job_requirements reqs(inputs, outputs, adhoc_storage);
+        scord::job::requirements reqs(inputs, outputs, adhoc_storage);
 
         const auto job = scord::register_job(
                 server, scord::job::resources{job_nodes}, reqs, 0);
diff --git a/examples/cxx/ADM_update_job.cpp b/examples/cxx/ADM_update_job.cpp
index cf837e2bc32c2e97c4db98e7e9bf754a61120106..581029cc978a590d4a7e0d41d52a3e86c882e255 100644
--- a/examples/cxx/ADM_update_job.cpp
+++ b/examples/cxx/ADM_update_job.cpp
@@ -55,7 +55,7 @@ main(int argc, char* argv[]) {
                     scord::adhoc_storage::access_type::read_write,
                     scord::adhoc_storage::resources{adhoc_nodes}, 100, false});
 
-    scord::job_requirements reqs{inputs, outputs, gkfs_storage};
+    scord::job::requirements reqs{inputs, outputs, gkfs_storage};
 
     const auto new_inputs = prepare_datasets("input-new-dataset-{}", NINPUTS);
     const auto new_outputs =
diff --git a/src/lib/c_wrapper.cpp b/src/lib/c_wrapper.cpp
index 8e9cd12ad85538cf4a3b2c228ca346502008031a..c00d09f38308ffd61bec57620d71d92461d42905 100644
--- a/src/lib/c_wrapper.cpp
+++ b/src/lib/c_wrapper.cpp
@@ -78,7 +78,7 @@ ADM_register_job(ADM_server_t server, ADM_job_resources_t res,
     const scord::server srv{server};
 
     const auto rv = scord::detail::register_job(srv, scord::job::resources{res},
-                                                scord::job_requirements{reqs},
+                                                scord::job::requirements{reqs},
                                                 slurm_id);
 
     if(!rv) {
diff --git a/src/lib/detail/impl.cpp b/src/lib/detail/impl.cpp
index 2eaf848fa373d8c42be45cd48a93c3b58c4e6601..d197fb41566c379ac4d50c8d6bf75e9ffa7ba009 100644
--- a/src/lib/detail/impl.cpp
+++ b/src/lib/detail/impl.cpp
@@ -84,7 +84,7 @@ ping(const server& srv) {
 
 tl::expected<scord::job, scord::error_code>
 register_job(const server& srv, const job::resources& job_resources,
-             const job_requirements& job_requirements,
+             const job::requirements& job_requirements,
              scord::slurm_job_id slurm_id) {
 
     scord::network::client rpc_client{srv.protocol()};
diff --git a/src/lib/detail/impl.hpp b/src/lib/detail/impl.hpp
index 369b083653d7300743d7d309831154a265014222..d437b944aaa6cc46fbfa312d357da758607cff3a 100644
--- a/src/lib/detail/impl.hpp
+++ b/src/lib/detail/impl.hpp
@@ -36,7 +36,7 @@ ping(const server& srv);
 
 tl::expected<scord::job, scord::error_code>
 register_job(const server& srv, const job::resources& job_resources,
-             const job_requirements& job_requirements,
+             const job::requirements& job_requirements,
              scord::slurm_job_id slurm_id);
 
 scord::error_code
diff --git a/src/lib/libscord.cpp b/src/lib/libscord.cpp
index 3d80dd3057e8335014e4bf7614d8900adfc4ea11..39e00510f32428100beaab16d9f8a1692d98ebad 100644
--- a/src/lib/libscord.cpp
+++ b/src/lib/libscord.cpp
@@ -210,9 +210,11 @@ ping(const server& srv) {
 
 scord::job
 register_job(const server& srv, const job::resources& resources,
-             const job_requirements& reqs, scord::slurm_job_id slurm_job_id) {
+             const job::requirements& job_requirements,
+             scord::slurm_job_id slurm_job_id) {
 
-    const auto rv = detail::register_job(srv, resources, reqs, slurm_job_id);
+    const auto rv = detail::register_job(srv, resources, job_requirements,
+                                         slurm_job_id);
 
     if(!rv) {
         throw std::runtime_error(fmt::format("ADM_register_job() error: {}",
diff --git a/src/lib/scord/scord.hpp b/src/lib/scord/scord.hpp
index 5a48f62f03a25afc113964027f63c0e2a758b303..d3642e2847408b22084c47582e4988b2f6d6a22f 100644
--- a/src/lib/scord/scord.hpp
+++ b/src/lib/scord/scord.hpp
@@ -48,7 +48,8 @@ ping(const server& srv);
 
 scord::job
 register_job(const server& srv, const job::resources& job_resources,
-             const job_requirements& reqs, scord::slurm_job_id slurm_id);
+             const job::requirements& job_requirements,
+             scord::slurm_job_id slurm_id);
 
 void
 update_job(const server& srv, const job&, const job::resources& job_resources);
diff --git a/src/lib/scord/types.hpp b/src/lib/scord/types.hpp
index 86aa6f19194b9a9fee9697cebac0c343436a99a4..7978a1cd226a89015c3310d4b62ee662c10fb7ea 100644
--- a/src/lib/scord/types.hpp
+++ b/src/lib/scord/types.hpp
@@ -167,205 +167,7 @@ private:
     std::unique_ptr<impl> m_pimpl;
 };
 
-struct job_requirements;
-
-struct job {
-
-    struct resources {
-        resources();
-        explicit resources(std::vector<scord::node> nodes);
-        explicit resources(ADM_job_resources_t res);
-
-        std::vector<scord::node>
-        nodes() const;
-
-        template <typename Archive>
-        void
-        serialize(Archive&& ar) {
-            ar& m_nodes;
-        }
-
-    private:
-        std::vector<scord::node> m_nodes;
-    };
-
-    job();
-    job(job_id id, slurm_job_id slurm_id);
-    explicit job(ADM_job_t job);
-    explicit operator ADM_job_t() const;
-    job(const job&) noexcept;
-    job(job&&) noexcept;
-    job&
-    operator=(job&&) noexcept;
-    job&
-    operator=(const job&) noexcept;
-    ~job();
-
-    job_id
-    id() const;
-
-    slurm_job_id
-    slurm_id() const;
-
-private:
-    class impl;
-    std::unique_ptr<impl> m_pimpl;
-
-    friend class cereal::access;
-    template <class Archive>
-    void
-    serialize(Archive& ar);
-};
-
-struct transfer {
-
-    enum class mapping : std::underlying_type<ADM_transfer_mapping_t>::type {
-        one_to_one = ADM_MAPPING_ONE_TO_ONE,
-        one_to_n = ADM_MAPPING_ONE_TO_N,
-        n_to_n = ADM_MAPPING_N_TO_N
-    };
-
-    transfer();
-    explicit transfer(transfer_id id);
-    explicit transfer(ADM_transfer_t transfer);
-    explicit operator ADM_transfer_t() const;
-
-    transfer(const transfer&) noexcept;
-    transfer(transfer&&) noexcept;
-    transfer&
-    operator=(const transfer&) noexcept;
-    transfer&
-    operator=(transfer&&) noexcept;
-
-    ~transfer();
-
-    transfer_id
-    id() const;
-
-    // The implementation for this must be deferred until
-    // after the declaration of the PIMPL class
-    template <class Archive>
-    void
-    serialize(Archive& ar);
-
-private:
-    class impl;
-    std::unique_ptr<impl> m_pimpl;
-};
-
-namespace qos {
-
-enum class subclass : std::underlying_type<ADM_qos_class_t>::type {
-    bandwidth = ADM_QOS_CLASS_BANDWIDTH,
-    iops = ADM_QOS_CLASS_IOPS,
-};
-
-enum class scope : std::underlying_type<ADM_qos_scope_t>::type {
-    dataset = ADM_QOS_SCOPE_DATASET,
-    node = ADM_QOS_SCOPE_NODE,
-    job = ADM_QOS_SCOPE_JOB,
-    transfer = ADM_QOS_SCOPE_TRANSFER
-};
-
-struct entity {
-
-    entity();
-    template <typename T>
-    entity(scord::qos::scope s, T&& data);
-    explicit entity(ADM_qos_entity_t entity);
-
-    entity(const entity&) noexcept;
-    entity(entity&&) noexcept;
-    entity&
-    operator=(const entity&) noexcept;
-    entity&
-    operator=(entity&&) noexcept;
-
-    ~entity();
-
-    scord::qos::scope
-    scope() const;
-
-    template <typename T>
-    T
-    data() const;
-
-    // The implementation for this must be deferred until
-    // after the declaration of the PIMPL class
-    template <class Archive>
-    void
-    serialize(Archive& ar);
-
-private:
-    class impl;
-    std::unique_ptr<impl> m_pimpl;
-};
-
-struct limit {
-
-    limit();
-    limit(scord::qos::subclass cls, uint64_t value);
-    limit(scord::qos::subclass cls, uint64_t value,
-          const scord::qos::entity& e);
-    explicit limit(ADM_qos_limit_t l);
-
-    limit(const limit&) noexcept;
-    limit(limit&&) noexcept;
-    limit&
-    operator=(const limit&) noexcept;
-    limit&
-    operator=(limit&&) noexcept;
-
-    ~limit();
-
-    std::optional<scord::qos::entity>
-    entity() const;
-
-    scord::qos::subclass
-    subclass() const;
-
-    uint64_t
-    value() const;
-
-    // The implementation for this must be deferred until
-    // after the declaration of the PIMPL class
-    template <class Archive>
-    void
-    serialize(Archive& ar);
-
-private:
-    class impl;
-    std::unique_ptr<impl> m_pimpl;
-};
-
-} // namespace qos
-
-
-struct dataset {
-    dataset();
-    explicit dataset(std::string id);
-    explicit dataset(ADM_dataset_t dataset);
-    dataset(const dataset&) noexcept;
-    dataset(dataset&&) noexcept;
-    dataset&
-    operator=(const dataset&) noexcept;
-    dataset&
-    operator=(dataset&&) noexcept;
-    ~dataset();
-
-    std::string
-    id() const;
-
-    // The implementation for this must be deferred until
-    // after the declaration of the PIMPL class
-    template <class Archive>
-    void
-    serialize(Archive& ar);
-
-private:
-    class impl;
-    std::unique_ptr<impl> m_pimpl;
-};
+struct dataset;
 
 struct adhoc_storage {
 
@@ -560,34 +362,225 @@ private:
     std::unique_ptr<impl> m_pimpl;
 };
 
-struct job_requirements {
+struct job {
 
-    job_requirements();
+    struct resources {
+        resources();
+        explicit resources(std::vector<scord::node> nodes);
+        explicit resources(ADM_job_resources_t res);
 
-    job_requirements(std::vector<scord::dataset> inputs,
-                     std::vector<scord::dataset> outputs);
+        std::vector<scord::node>
+        nodes() const;
 
-    job_requirements(std::vector<scord::dataset> inputs,
+        template <typename Archive>
+        void
+        serialize(Archive&& ar) {
+            ar& m_nodes;
+        }
+
+    private:
+        std::vector<scord::node> m_nodes;
+    };
+
+    struct requirements {
+
+        requirements();
+        requirements(std::vector<scord::dataset> inputs,
+                     std::vector<scord::dataset> outputs);
+        requirements(std::vector<scord::dataset> inputs,
                      std::vector<scord::dataset> outputs,
                      scord::adhoc_storage adhoc_storage);
+        explicit requirements(ADM_job_requirements_t reqs);
+
+        std::vector<scord::dataset>
+        inputs() const;
+        std::vector<scord::dataset>
+        outputs() const;
+        std::optional<scord::adhoc_storage>
+        adhoc_storage() const;
+
+        // The implementation for this must be deferred until
+        // after the declaration of the PIMPL class
+        template <class Archive>
+        void
+        serialize(Archive& ar) {
+            ar& m_inputs;
+            ar& m_outputs;
+            ar& m_adhoc_storage;
+        }
+
+    private:
+        std::vector<scord::dataset> m_inputs;
+        std::vector<scord::dataset> m_outputs;
+        std::optional<scord::adhoc_storage> m_adhoc_storage;
+    };
+
+    job();
+    job(job_id id, slurm_job_id slurm_id);
+    explicit job(ADM_job_t job);
+    explicit operator ADM_job_t() const;
+    job(const job&) noexcept;
+    job(job&&) noexcept;
+    job&
+    operator=(job&&) noexcept;
+    job&
+    operator=(const job&) noexcept;
+    ~job();
+
+    job_id
+    id() const;
+
+    slurm_job_id
+    slurm_id() const;
+
+private:
+    class impl;
+    std::unique_ptr<impl> m_pimpl;
+
+    friend class cereal::access;
+    template <class Archive>
+    void
+    serialize(Archive& ar);
+};
+
+struct transfer {
+
+    enum class mapping : std::underlying_type<ADM_transfer_mapping_t>::type {
+        one_to_one = ADM_MAPPING_ONE_TO_ONE,
+        one_to_n = ADM_MAPPING_ONE_TO_N,
+        n_to_n = ADM_MAPPING_N_TO_N
+    };
+
+    transfer();
+    explicit transfer(transfer_id id);
+    explicit transfer(ADM_transfer_t transfer);
+    explicit operator ADM_transfer_t() const;
+
+    transfer(const transfer&) noexcept;
+    transfer(transfer&&) noexcept;
+    transfer&
+    operator=(const transfer&) noexcept;
+    transfer&
+    operator=(transfer&&) noexcept;
+
+    ~transfer();
+
+    transfer_id
+    id() const;
+
+    // The implementation for this must be deferred until
+    // after the declaration of the PIMPL class
+    template <class Archive>
+    void
+    serialize(Archive& ar);
+
+private:
+    class impl;
+    std::unique_ptr<impl> m_pimpl;
+};
+
+namespace qos {
 
-    explicit job_requirements(ADM_job_requirements_t reqs);
+enum class subclass : std::underlying_type<ADM_qos_class_t>::type {
+    bandwidth = ADM_QOS_CLASS_BANDWIDTH,
+    iops = ADM_QOS_CLASS_IOPS,
+};
 
-    job_requirements(const job_requirements&) noexcept;
-    job_requirements(job_requirements&&) noexcept;
-    job_requirements&
-    operator=(const job_requirements&) noexcept;
-    job_requirements&
-    operator=(job_requirements&&) noexcept;
+enum class scope : std::underlying_type<ADM_qos_scope_t>::type {
+    dataset = ADM_QOS_SCOPE_DATASET,
+    node = ADM_QOS_SCOPE_NODE,
+    job = ADM_QOS_SCOPE_JOB,
+    transfer = ADM_QOS_SCOPE_TRANSFER
+};
 
-    ~job_requirements();
+struct entity {
+
+    entity();
+    template <typename T>
+    entity(scord::qos::scope s, T&& data);
+    explicit entity(ADM_qos_entity_t entity);
+
+    entity(const entity&) noexcept;
+    entity(entity&&) noexcept;
+    entity&
+    operator=(const entity&) noexcept;
+    entity&
+    operator=(entity&&) noexcept;
+
+    ~entity();
 
-    std::vector<scord::dataset>
-    inputs() const;
-    std::vector<scord::dataset>
-    outputs() const;
-    std::optional<scord::adhoc_storage>
-    adhoc_storage() const;
+    scord::qos::scope
+    scope() const;
+
+    template <typename T>
+    T
+    data() const;
+
+    // The implementation for this must be deferred until
+    // after the declaration of the PIMPL class
+    template <class Archive>
+    void
+    serialize(Archive& ar);
+
+private:
+    class impl;
+    std::unique_ptr<impl> m_pimpl;
+};
+
+struct limit {
+
+    limit();
+    limit(scord::qos::subclass cls, uint64_t value);
+    limit(scord::qos::subclass cls, uint64_t value,
+          const scord::qos::entity& e);
+    explicit limit(ADM_qos_limit_t l);
+
+    limit(const limit&) noexcept;
+    limit(limit&&) noexcept;
+    limit&
+    operator=(const limit&) noexcept;
+    limit&
+    operator=(limit&&) noexcept;
+
+    ~limit();
+
+    std::optional<scord::qos::entity>
+    entity() const;
+
+    scord::qos::subclass
+    subclass() const;
+
+    uint64_t
+    value() const;
+
+    // The implementation for this must be deferred until
+    // after the declaration of the PIMPL class
+    template <class Archive>
+    void
+    serialize(Archive& ar);
+
+private:
+    class impl;
+    std::unique_ptr<impl> m_pimpl;
+};
+
+} // namespace qos
+
+
+struct dataset {
+    dataset();
+    explicit dataset(std::string id);
+    explicit dataset(ADM_dataset_t dataset);
+    dataset(const dataset&) noexcept;
+    dataset(dataset&&) noexcept;
+    dataset&
+    operator=(const dataset&) noexcept;
+    dataset&
+    operator=(dataset&&) noexcept;
+    ~dataset();
+
+    std::string
+    id() const;
 
     // The implementation for this must be deferred until
     // after the declaration of the PIMPL class
@@ -858,11 +851,11 @@ struct fmt::formatter<scord::job::resources> : formatter<std::string_view> {
 };
 
 template <>
-struct fmt::formatter<scord::job_requirements> : formatter<std::string_view> {
+struct fmt::formatter<scord::job::requirements> : formatter<std::string_view> {
     // parse is inherited from formatter<string_view>.
     template <typename FormatContext>
     auto
-    format(const scord::job_requirements& r, FormatContext& ctx) const {
+    format(const scord::job::requirements& r, FormatContext& ctx) const {
         return formatter<std::string_view>::format(
                 fmt::format("{{inputs: {}, outputs: {}, adhoc_storage: {}}}",
                             r.inputs(), r.outputs(), r.adhoc_storage()),
diff --git a/src/lib/types.cpp b/src/lib/types.cpp
index abbd67bf220fdce904cb54433b18bec23aed1331..ca3430137257fc7e7bf1f0b5e27e4f6338f3a017 100644
--- a/src/lib/types.cpp
+++ b/src/lib/types.cpp
@@ -215,6 +215,53 @@ private:
     slurm_job_id m_slurm_job_id;
 };
 
+job::requirements::requirements() = default;
+
+job::requirements::requirements(std::vector<scord::dataset> inputs,
+                                std::vector<scord::dataset> outputs)
+    : m_inputs(std::move(inputs)), m_outputs(std::move(outputs)) {}
+
+job::requirements::requirements(std::vector<scord::dataset> inputs,
+                                std::vector<scord::dataset> outputs,
+                                scord::adhoc_storage adhoc_storage)
+    : m_inputs(std::move(inputs)), m_outputs(std::move(outputs)),
+      m_adhoc_storage(std::move(adhoc_storage)) {}
+
+job::requirements::requirements(ADM_job_requirements_t reqs) {
+
+    m_inputs.reserve(reqs->r_inputs->l_length);
+
+    for(size_t i = 0; i < reqs->r_inputs->l_length; ++i) {
+        m_inputs.emplace_back(reqs->r_inputs->l_datasets[i].d_id);
+    }
+
+    m_outputs.reserve(reqs->r_outputs->l_length);
+
+    for(size_t i = 0; i < reqs->r_outputs->l_length; ++i) {
+        m_outputs.emplace_back(reqs->r_outputs->l_datasets[i].d_id);
+    }
+
+    if(reqs->r_adhoc_storage) {
+        m_adhoc_storage = scord::adhoc_storage(reqs->r_adhoc_storage);
+    }
+}
+
+std::vector<scord::dataset>
+job::requirements::inputs() const {
+    return m_inputs;
+}
+
+std::vector<scord::dataset>
+job::requirements::outputs() const {
+    return m_outputs;
+}
+
+std::optional<scord::adhoc_storage>
+job::requirements::adhoc_storage() const {
+    return m_adhoc_storage;
+}
+
+
 job::resources::resources() = default;
 
 job::resources::resources(std::vector<scord::node> nodes)
@@ -876,156 +923,6 @@ template void
 pfs_storage::serialize<scord::network::serialization::input_archive>(
         scord::network::serialization::input_archive&);
 
-class job_requirements::impl {
-
-public:
-    impl() = default;
-    impl(std::vector<scord::dataset> inputs,
-         std::vector<scord::dataset> outputs)
-        : m_inputs(std::move(inputs)), m_outputs(std::move(outputs)) {}
-
-    impl(std::vector<scord::dataset> inputs,
-         std::vector<scord::dataset> outputs,
-         scord::adhoc_storage adhoc_storage)
-        : m_inputs(std::move(inputs)), m_outputs(std::move(outputs)),
-          m_adhoc_storage(std::move(adhoc_storage)) {}
-
-
-    explicit impl(ADM_job_requirements_t reqs) {
-        m_inputs.reserve(reqs->r_inputs->l_length);
-
-        for(size_t i = 0; i < reqs->r_inputs->l_length; ++i) {
-            m_inputs.emplace_back(reqs->r_inputs->l_datasets[i].d_id);
-        }
-
-        m_outputs.reserve(reqs->r_outputs->l_length);
-
-        for(size_t i = 0; i < reqs->r_outputs->l_length; ++i) {
-            m_outputs.emplace_back(reqs->r_outputs->l_datasets[i].d_id);
-        }
-
-        if(reqs->r_adhoc_storage) {
-            m_adhoc_storage = scord::adhoc_storage(reqs->r_adhoc_storage);
-        }
-    }
-
-    impl(const impl& rhs) = default;
-    impl(impl&& rhs) = default;
-    impl&
-    operator=(const impl& other) noexcept = default;
-    impl&
-    operator=(impl&&) noexcept = default;
-
-    std::vector<scord::dataset>
-    inputs() const {
-        return m_inputs;
-    }
-
-    std::vector<scord::dataset>
-    outputs() const {
-        return m_outputs;
-    }
-
-    std::optional<scord::adhoc_storage>
-    adhoc_storage() const {
-        return m_adhoc_storage;
-    }
-
-    template <class Archive>
-    void
-    load(Archive& ar) {
-        ar(SCORD_SERIALIZATION_NVP(m_inputs));
-        ar(SCORD_SERIALIZATION_NVP(m_outputs));
-        ar(SCORD_SERIALIZATION_NVP(m_adhoc_storage));
-    }
-
-    template <class Archive>
-    void
-    save(Archive& ar) const {
-        ar(SCORD_SERIALIZATION_NVP(m_inputs));
-        ar(SCORD_SERIALIZATION_NVP(m_outputs));
-        ar(SCORD_SERIALIZATION_NVP(m_adhoc_storage));
-    }
-
-private:
-    std::vector<scord::dataset> m_inputs;
-    std::vector<scord::dataset> m_outputs;
-    std::optional<scord::adhoc_storage> m_adhoc_storage;
-};
-
-
-job_requirements::job_requirements() = default;
-
-job_requirements::job_requirements(std::vector<scord::dataset> inputs,
-                                   std::vector<scord::dataset> outputs)
-    : m_pimpl(std::make_unique<impl>(std::move(inputs), std::move(outputs))) {}
-
-job_requirements::job_requirements(std::vector<scord::dataset> inputs,
-                                   std::vector<scord::dataset> outputs,
-                                   scord::adhoc_storage adhoc_storage)
-    : m_pimpl(std::make_unique<impl>(std::move(inputs), std::move(outputs),
-                                     std::move(adhoc_storage))) {}
-
-job_requirements::job_requirements(ADM_job_requirements_t reqs)
-    : m_pimpl(std::make_unique<impl>(reqs)) {}
-
-job_requirements::job_requirements(const job_requirements& other) noexcept
-    : m_pimpl(std::make_unique<job_requirements::impl>(*other.m_pimpl)) {}
-
-job_requirements::job_requirements(job_requirements&&) noexcept = default;
-
-job_requirements&
-job_requirements::operator=(const job_requirements& other) noexcept {
-    this->m_pimpl = std::make_unique<impl>(*other.m_pimpl);
-    return *this;
-}
-
-job_requirements&
-job_requirements::operator=(job_requirements&&) noexcept = default;
-
-job_requirements::~job_requirements() = default;
-
-std::vector<scord::dataset>
-job_requirements::inputs() const {
-    return m_pimpl->inputs();
-}
-
-std::vector<scord::dataset>
-job_requirements::outputs() const {
-    return m_pimpl->outputs();
-}
-
-std::optional<scord::adhoc_storage>
-job_requirements::adhoc_storage() const {
-    return m_pimpl->adhoc_storage();
-}
-
-// since the PIMPL class is fully defined at this point, we can now
-// define the serialization function
-template <class Archive>
-inline void
-job_requirements::serialize(Archive& ar) {
-    ar(SCORD_SERIALIZATION_NVP(m_pimpl));
-}
-
-//  we must also explicitly instantiate our template functions for
-//  serialization in the desired archives
-template void
-job_requirements::impl::save<scord::network::serialization::output_archive>(
-        scord::network::serialization::output_archive&) const;
-
-template void
-job_requirements::impl::load<scord::network::serialization::input_archive>(
-        scord::network::serialization::input_archive&);
-
-template void
-job_requirements::serialize<scord::network::serialization::output_archive>(
-        scord::network::serialization::output_archive&);
-
-template void
-job_requirements::serialize<scord::network::serialization::input_archive>(
-        scord::network::serialization::input_archive&);
-
 namespace qos {
 
 class entity::impl {
diff --git a/src/scord/internal_types.hpp b/src/scord/internal_types.hpp
index 8d41f34b72eeda88d54763c45771b64c6b0bee77..f8064945913f016c3dbda0899cb84e25d49b6b0d 100644
--- a/src/scord/internal_types.hpp
+++ b/src/scord/internal_types.hpp
@@ -34,7 +34,7 @@ struct job_info {
     explicit job_info(scord::job job) : m_job(std::move(job)) {}
 
     job_info(scord::job job, scord::job::resources resources,
-             scord::job_requirements requirements)
+             scord::job::requirements requirements)
         : m_job(std::move(job)), m_resources(std::move(resources)),
           m_requirements(std::move(requirements)) {}
 
@@ -48,7 +48,7 @@ struct job_info {
         return m_resources;
     }
 
-    std::optional<scord::job_requirements>
+    std::optional<scord::job::requirements>
     requirements() const {
         return m_requirements;
     }
@@ -60,7 +60,7 @@ struct job_info {
 
     scord::job m_job;
     std::optional<scord::job::resources> m_resources;
-    std::optional<scord::job_requirements> m_requirements;
+    std::optional<scord::job::requirements> m_requirements;
 };
 
 struct adhoc_storage_info {
diff --git a/src/scord/job_manager.hpp b/src/scord/job_manager.hpp
index fa30b02b5835f8eecb1b11131e6db26c4ef6d55f..cf9da9c21fa826f5ac8d68da526946c571d26bfb 100644
--- a/src/scord/job_manager.hpp
+++ b/src/scord/job_manager.hpp
@@ -42,7 +42,7 @@ struct job_manager : scord::utils::singleton<job_manager> {
 
     tl::expected<std::shared_ptr<scord::internal::job_info>, scord::error_code>
     create(scord::slurm_job_id slurm_id, scord::job::resources job_resources,
-           scord::job_requirements job_requirements) {
+           scord::job::requirements job_requirements) {
 
         static std::atomic_uint64_t current_id;
         scord::job_id id = current_id++;
diff --git a/src/scord/rpc_handlers.cpp b/src/scord/rpc_handlers.cpp
index 9ffe06467639f5c24119237ab71a18af183193e5..0f4909fa3c7c531f3a02a58302eaf5d6631cd6db 100644
--- a/src/scord/rpc_handlers.cpp
+++ b/src/scord/rpc_handlers.cpp
@@ -71,7 +71,7 @@ ping(const scord::network::request& req) {
 void
 register_job(const scord::network::request& req,
              const scord::job::resources& job_resources,
-             const scord::job_requirements& job_requirements,
+             const scord::job::requirements& job_requirements,
              scord::slurm_job_id slurm_id) {
 
     using scord::network::get_address;
diff --git a/src/scord/rpc_handlers.hpp b/src/scord/rpc_handlers.hpp
index 285bb962ec9245d592ca4eed503e319470bd4658..94d84aebab169a3cc8396d3af98e4065ef07deda 100644
--- a/src/scord/rpc_handlers.hpp
+++ b/src/scord/rpc_handlers.hpp
@@ -37,7 +37,7 @@ ping(const scord::network::request& req);
 void
 register_job(const scord::network::request& req,
              const scord::job::resources& job_resources,
-             const scord::job_requirements& job_requirements,
+             const scord::job::requirements& job_requirements,
              scord::slurm_job_id slurm_id);
 
 void