diff --git a/crates/configuration/src/lib.rs b/crates/configuration/src/lib.rs
index fa434f988100957e65b6b812277b4f152da3926d..8e71fff612723e47b49e6157decc24ea7d7617fc 100644
--- a/crates/configuration/src/lib.rs
+++ b/crates/configuration/src/lib.rs
@@ -10,7 +10,9 @@ mod utils;
 pub use global_settings::{GlobalSettings, GlobalSettingsBuilder};
 pub use hrmp_channel::{HrmpChannelConfig, HrmpChannelConfigBuilder};
 pub use network::{NetworkConfig, NetworkConfigBuilder};
-pub use parachain::{ParachainConfig, ParachainConfigBuilder, RegistrationStrategy};
+pub use parachain::{
+    states as para_states, ParachainConfig, ParachainConfigBuilder, RegistrationStrategy,
+};
 pub use relaychain::{RelaychainConfig, RelaychainConfigBuilder};
 // re-export shared
 pub use shared::{node::NodeConfig, types};
diff --git a/crates/configuration/src/network.rs b/crates/configuration/src/network.rs
index 31bc433074af4e3f874b50a74ca2617528b5ca4d..21f2d069d9287384db60b159b186b9f8c586ceb4 100644
--- a/crates/configuration/src/network.rs
+++ b/crates/configuration/src/network.rs
@@ -373,8 +373,11 @@ impl NetworkConfigBuilder<WithRelaychain> {
     pub fn with_parachain(
         self,
         f: fn(
-            ParachainConfigBuilder<parachain::Initial>,
-        ) -> ParachainConfigBuilder<parachain::WithAtLeastOneCollator>,
+            ParachainConfigBuilder<parachain::states::Initial, parachain::states::Bootstrap>,
+        ) -> ParachainConfigBuilder<
+            parachain::states::WithAtLeastOneCollator,
+            parachain::states::Bootstrap,
+        >,
     ) -> Self {
         match f(ParachainConfigBuilder::new(self.validation_context.clone())).build() {
             Ok(parachain) => Self::transition(
diff --git a/crates/configuration/src/parachain.rs b/crates/configuration/src/parachain.rs
index 5f3aa083790ff2dc6977e26f4d839dabe5313a9d..e09752da73882979198b25157cc6de99ec67fcb6 100644
--- a/crates/configuration/src/parachain.rs
+++ b/crates/configuration/src/parachain.rs
@@ -11,7 +11,6 @@ use crate::{
     shared::{
         errors::{ConfigError, FieldError},
         helpers::{merge_errors, merge_errors_vecs},
-        macros::states,
         node::{self, NodeConfig, NodeConfigBuilder},
         resources::{Resources, ResourcesBuilder},
         types::{
@@ -224,21 +223,36 @@ impl ParachainConfig {
     }
 }
 
-states! {
-    Initial,
-    WithId,
-    WithAtLeastOneCollator
+pub mod states {
+    use crate::shared::macros::states;
+
+    states! {
+        Initial,
+        WithId,
+        WithAtLeastOneCollator
+    }
+
+    states! {
+        Bootstrap,
+        Running
+    }
+
+    pub trait Context {}
+    impl Context for Bootstrap {}
+    impl Context for Running {}
 }
 
+use states::{Bootstrap, Context, Initial, Running, WithAtLeastOneCollator, WithId};
 /// A parachain configuration builder, used to build a [`ParachainConfig`] declaratively with fields validation.
-pub struct ParachainConfigBuilder<S> {
+pub struct ParachainConfigBuilder<S, C> {
     config: ParachainConfig,
     validation_context: Rc<RefCell<ValidationContext>>,
     errors: Vec<anyhow::Error>,
     _state: PhantomData<S>,
+    _context: PhantomData<C>,
 }
 
-impl Default for ParachainConfigBuilder<Initial> {
+impl<C: Context> Default for ParachainConfigBuilder<Initial, C> {
     fn default() -> Self {
         Self {
             config: ParachainConfig {
@@ -265,21 +279,23 @@ impl Default for ParachainConfigBuilder<Initial> {
             validation_context: Default::default(),
             errors: vec![],
             _state: PhantomData,
+            _context: PhantomData,
         }
     }
 }
 
-impl<A> ParachainConfigBuilder<A> {
+impl<A, C> ParachainConfigBuilder<A, C> {
     fn transition<B>(
         config: ParachainConfig,
         validation_context: Rc<RefCell<ValidationContext>>,
         errors: Vec<anyhow::Error>,
-    ) -> ParachainConfigBuilder<B> {
+    ) -> ParachainConfigBuilder<B, C> {
         ParachainConfigBuilder {
             config,
             validation_context,
             errors,
             _state: PhantomData,
+            _context: PhantomData,
         }
     }
 
@@ -294,19 +310,51 @@ impl<A> ParachainConfigBuilder<A> {
     }
 }
 
-impl ParachainConfigBuilder<Initial> {
+impl ParachainConfigBuilder<Initial, Bootstrap> {
     pub fn new(
         validation_context: Rc<RefCell<ValidationContext>>,
-    ) -> ParachainConfigBuilder<Initial> {
+    ) -> ParachainConfigBuilder<Initial, Bootstrap> {
         Self {
             validation_context,
             ..Self::default()
         }
     }
+}
 
+impl ParachainConfigBuilder<WithId, Bootstrap> {
+    /// Set the registration strategy for the parachain, could be without registration, using extrinsic or in genesis.
+    pub fn with_registration_strategy(self, strategy: RegistrationStrategy) -> Self {
+        Self::transition(
+            ParachainConfig {
+                registration_strategy: Some(strategy),
+                ..self.config
+            },
+            self.validation_context,
+            self.errors,
+        )
+    }
+}
+
+impl ParachainConfigBuilder<Initial, Running> {
+    /// Start a new builder in the context of a running network
+    pub fn new_with_running(
+        validation_context: Rc<RefCell<ValidationContext>>,
+    ) -> ParachainConfigBuilder<Initial, Running> {
+        let mut builder = Self {
+            validation_context,
+            ..Self::default()
+        };
+
+        // override the registration strategy
+        builder.config.registration_strategy = Some(RegistrationStrategy::UsingExtrinsic);
+        builder
+    }
+}
+
+impl<C: Context> ParachainConfigBuilder<Initial, C> {
     /// Set the parachain ID (should be unique).
     // TODO: handle unique validation
-    pub fn with_id(self, id: u32) -> ParachainConfigBuilder<WithId> {
+    pub fn with_id(self, id: u32) -> ParachainConfigBuilder<WithId, C> {
         Self::transition(
             ParachainConfig { id, ..self.config },
             self.validation_context,
@@ -315,7 +363,7 @@ impl ParachainConfigBuilder<Initial> {
     }
 }
 
-impl ParachainConfigBuilder<WithId> {
+impl<C: Context> ParachainConfigBuilder<WithId, C> {
     /// Set the chain name (e.g. rococo-local).
     /// Use [`None`], if you are running adder-collator or undying-collator).
     pub fn with_chain<T>(self, chain: T) -> Self
@@ -340,18 +388,6 @@ impl ParachainConfigBuilder<WithId> {
         }
     }
 
-    /// Set the registration strategy for the parachain, could be without registration, using extrinsic or in genesis.
-    pub fn with_registration_strategy(self, strategy: RegistrationStrategy) -> Self {
-        Self::transition(
-            ParachainConfig {
-                registration_strategy: Some(strategy),
-                ..self.config
-            },
-            self.validation_context,
-            self.errors,
-        )
-    }
-
     /// Set whether the parachain should be onboarded or stay a parathread. Default is ```true```.
     pub fn onboard_as_parachain(self, choice: bool) -> Self {
         Self::transition(
@@ -615,7 +651,7 @@ impl ParachainConfigBuilder<WithId> {
     pub fn with_collator(
         self,
         f: fn(NodeConfigBuilder<node::Initial>) -> NodeConfigBuilder<node::Buildable>,
-    ) -> ParachainConfigBuilder<WithAtLeastOneCollator> {
+    ) -> ParachainConfigBuilder<WithAtLeastOneCollator, C> {
         match f(NodeConfigBuilder::new(
             self.default_chain_context(),
             self.validation_context.clone(),
@@ -645,7 +681,7 @@ impl ParachainConfigBuilder<WithId> {
     }
 }
 
-impl ParachainConfigBuilder<WithAtLeastOneCollator> {
+impl<C: Context> ParachainConfigBuilder<WithAtLeastOneCollator, C> {
     /// Add a new collator using a nested [`NodeConfigBuilder`].
     pub fn with_collator(
         self,
@@ -1122,4 +1158,19 @@ mod tests {
 
         assert!(config.onboard_as_parachain());
     }
+
+    #[test]
+    fn build_config_in_running_context() {
+        let config = ParachainConfigBuilder::new_with_running(Default::default())
+            .with_id(2000)
+            .with_chain("myparachain")
+            .with_collator(|collator| collator.with_name("collator"))
+            .build()
+            .unwrap();
+
+        assert_eq!(
+            config.registration_strategy(),
+            Some(&RegistrationStrategy::UsingExtrinsic)
+        );
+    }
 }
diff --git a/crates/configuration/src/shared/node.rs b/crates/configuration/src/shared/node.rs
index 7720b911f41f0e1f3266753ebcddfd671319d0e0..e8b0ff3507851016160569209aaa28327dde6f6f 100644
--- a/crates/configuration/src/shared/node.rs
+++ b/crates/configuration/src/shared/node.rs
@@ -18,6 +18,11 @@ use crate::{
     utils::{default_as_true, default_initial_balance},
 };
 
+states! {
+    Buildable,
+    Initial
+}
+
 /// An environment variable with a name and a value.
 /// It can be constructed from a `(&str, &str)`.
 ///
@@ -245,11 +250,6 @@ impl NodeConfig {
     }
 }
 
-states! {
-    Initial,
-    Buildable
-}
-
 /// A node configuration builder, used to build a [`NodeConfig`] declaratively with fields validation.
 pub struct NodeConfigBuilder<S> {
     config: NodeConfig,
diff --git a/crates/examples/Cargo.toml b/crates/examples/Cargo.toml
index 9c4d0c1fd8c69d10cb12a7e1311a3956d5705b13..f067060a678a6e14a59055238bec5c7e45cf44ce 100644
--- a/crates/examples/Cargo.toml
+++ b/crates/examples/Cargo.toml
@@ -12,3 +12,4 @@ futures = { workspace = true }
 subxt = { workspace = true }
 tracing-subscriber = "0.3"
 serde_json = { workspace = true }
+anyhow = { workspace = true }
diff --git a/crates/examples/examples/add_para.rs b/crates/examples/examples/add_para.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ee2e2d38ad9d99f264f0671ed93136b90bbbebad
--- /dev/null
+++ b/crates/examples/examples/add_para.rs
@@ -0,0 +1,51 @@
+use anyhow::anyhow;
+use futures::stream::StreamExt;
+use zombienet_sdk::{NetworkConfigBuilder, NetworkConfigExt};
+
+#[tokio::main]
+async fn main() -> Result<(), anyhow::Error> {
+    tracing_subscriber::fmt::init();
+    let mut network = NetworkConfigBuilder::new()
+        .with_relaychain(|r| {
+            r.with_chain("rococo-local")
+                .with_default_command("polkadot")
+                .with_node(|node| node.with_name("alice"))
+                .with_node(|node| node.with_name("bob"))
+        })
+        .build()
+        .unwrap()
+        .spawn_native()
+        .await?;
+
+    println!("🚀🚀🚀🚀 network deployed");
+
+    let alice = network.get_node("alice")?;
+    let client = alice.client::<subxt::PolkadotConfig>().await?;
+
+    // wait 3 blocks
+    let mut blocks = client.blocks().subscribe_finalized().await?.take(3);
+
+    while let Some(block) = blocks.next().await {
+        println!("Block #{}", block?.header().number);
+    }
+
+    println!("⚙️  adding parachain to the running network");
+
+    let para_config = network
+        .para_config_builder()
+        .with_id(100)
+        .with_default_command("polkadot-parachain")
+        .with_collator(|c| c.with_name("col-100-1"))
+        .build()
+        .map_err(|_e| anyhow!("Building config"))?;
+
+    network.add_parachain(&para_config, None).await?;
+
+    // For now let just loop....
+    #[allow(clippy::empty_loop)]
+    loop {}
+
+    #[allow(clippy::unreachable)]
+    #[allow(unreachable_code)]
+    Ok(())
+}
diff --git a/crates/orchestrator/src/generators/chain_spec.rs b/crates/orchestrator/src/generators/chain_spec.rs
index b363793e3589d5b3b2c1871c60b9e4db5d45a6fd..0d3f9df1c8536244819d34bae0af684f104700f5 100644
--- a/crates/orchestrator/src/generators/chain_spec.rs
+++ b/crates/orchestrator/src/generators/chain_spec.rs
@@ -213,22 +213,7 @@ impl ChainSpec {
         T: FileSystem,
     {
         let (content, _) = self.read_spec(scoped_fs).await?;
-        let chain_spec_json: serde_json::Value = serde_json::from_str(&content).map_err(|_| {
-            GeneratorError::ChainSpecGeneration("Can not parse chain-spec as json".into())
-        })?;
-        if let Some(chain_id) = chain_spec_json.get("id") {
-            if let Some(chain_id) = chain_id.as_str() {
-                Ok(chain_id.to_string())
-            } else {
-                Err(GeneratorError::ChainSpecGeneration(
-                    "id should be an string in the chain-spec, this is a bug".into(),
-                ))
-            }
-        } else {
-            Err(GeneratorError::ChainSpecGeneration(
-                "'id' should be a fields in the chain-spec of the relaychain".into(),
-            ))
-        }
+        ChainSpec::chain_id_from_spec(&content)
     }
 
     async fn read_spec<'a, T>(
@@ -511,6 +496,27 @@ impl ChainSpec {
 
         Ok(())
     }
+
+    /// Get the chain_is from the json content of a chain-spec file.
+    pub fn chain_id_from_spec(spec_content: &str) -> Result<String, GeneratorError> {
+        let chain_spec_json: serde_json::Value =
+            serde_json::from_str(spec_content).map_err(|_| {
+                GeneratorError::ChainSpecGeneration("Can not parse chain-spec as json".into())
+            })?;
+        if let Some(chain_id) = chain_spec_json.get("id") {
+            if let Some(chain_id) = chain_id.as_str() {
+                Ok(chain_id.to_string())
+            } else {
+                Err(GeneratorError::ChainSpecGeneration(
+                    "id should be an string in the chain-spec, this is a bug".into(),
+                ))
+            }
+        } else {
+            Err(GeneratorError::ChainSpecGeneration(
+                "'id' should be a fields in the chain-spec of the relaychain".into(),
+            ))
+        }
+    }
 }
 
 type GenesisNodeKey = (String, String, HashMap<String, String>);
diff --git a/crates/orchestrator/src/lib.rs b/crates/orchestrator/src/lib.rs
index 26f748f271d6cd75c2166e8a5111c92ab2e45d50..01e8c34b526250e6901bcd72c67e56b64d4e311e 100644
--- a/crates/orchestrator/src/lib.rs
+++ b/crates/orchestrator/src/lib.rs
@@ -98,33 +98,16 @@ where
         let relay_chain_name = network_spec.relaychain.chain.as_str();
         // TODO: if we don't need to register this para we can skip it
         for para in network_spec.parachains.iter_mut() {
-            let para_cloned = para.clone();
-            let chain_spec_raw_path = if let Some(chain_spec) = para.chain_spec.as_mut() {
-                chain_spec.build(&ns, &scoped_fs).await?;
-                debug!("chain_spec: {:#?}", chain_spec);
-
-                chain_spec
-                    .customize_para(&para_cloned, &relay_chain_id, &scoped_fs)
-                    .await?;
-                chain_spec.build_raw(&ns).await?;
-
-                let chain_spec_raw_path =
-                    chain_spec
-                        .raw_path()
-                        .ok_or(OrchestratorError::InvariantError(
-                            "chain-spec raw path should be set now",
-                        ))?;
-                Some(chain_spec_raw_path)
-            } else {
-                None
-            };
+            let chain_spec_raw_path = para
+                .build_chain_spec(&relay_chain_id, &ns, &scoped_fs)
+                .await?;
 
             // TODO: this need to be abstracted in a single call to generate_files.
             scoped_fs.create_dir(para.id.to_string()).await?;
             // create wasm/state
             para.genesis_state
                 .build(
-                    chain_spec_raw_path,
+                    chain_spec_raw_path.clone(),
                     format!("{}/genesis-state", para.id),
                     &ns,
                     &scoped_fs,
@@ -279,38 +262,11 @@ where
 
         // spawn paras
         for para in network_spec.parachains.iter() {
-            // global files to include for this parachain
-            let mut para_files_to_inject = global_files_to_inject.clone();
-
-            // parachain id is used for the keystore
-            let parachain_id = if let Some(chain_spec) = para.chain_spec.as_ref() {
-                let id = chain_spec.read_chain_id(&scoped_fs).await?;
-
-                // add the spec to global files to inject
-                let spec_name = chain_spec.chain_spec_name();
-                para_files_to_inject.push(TransferedFile {
-                    local_path: ns.base_dir().join(format!("{}.json", spec_name)),
-                    remote_path: PathBuf::from(format!("/cfg/{}.json", para.id)),
-                });
-
-                let raw_path = chain_spec
-                    .raw_path()
-                    .ok_or(OrchestratorError::InvariantError(
-                        "chain-spec path should be set by now.",
-                    ))?;
-                let mut running_para = Parachain::with_chain_spec(para.id, &id, raw_path);
-                if let Some(chain_name) = chain_spec.chain_name() {
-                    running_para.chain = Some(chain_name.to_string());
-                }
-                network.add_para(running_para);
-
-                Some(id)
-            } else {
-                network.add_para(Parachain::new(para.id));
-
-                None
-            };
+            // Create parachain (in the context of the running network)
+            let parachain = Parachain::from_spec(para, &global_files_to_inject, &scoped_fs).await?;
+            let parachain_id = parachain.chain_id.clone();
 
+            // Create `ctx` for spawn the nodes
             let ctx_para = SpawnNodeCtx {
                 parachain: Some(para),
                 parachain_id: parachain_id.as_deref(),
@@ -323,13 +279,16 @@ where
                 ..ctx.clone()
             };
 
-            let spawning_tasks = para
-                .collators
-                .iter()
-                .map(|node| spawner::spawn_node(node, para_files_to_inject.clone(), &ctx_para));
-            // TODO: Add para to Network instance
-            for node in futures::future::try_join_all(spawning_tasks).await? {
-                network.add_running_node(node, Some(para.id));
+            // Spawn the nodes
+            let spawning_tasks = para.collators.iter().map(|node| {
+                spawner::spawn_node(node, parachain.files_to_inject.clone(), &ctx_para)
+            });
+
+            let running_nodes = futures::future::try_join_all(spawning_tasks).await?;
+            let running_para_id = parachain.para_id;
+            network.add_para(parachain);
+            for node in running_nodes {
+                network.add_running_node(node, Some(running_para_id));
             }
         }
 
@@ -360,8 +319,8 @@ where
                     .to_path_buf(),
                 node_ws_url: node_ws_url.clone(),
                 onboard_as_para: para.onboard_as_parachain,
-                seed: None,          // TODO: Seed is passed by?
-                finalization: false, // TODO: Seed is passed by?
+                seed: None, // TODO: Seed is passed by?
+                finalization: false,
             };
 
             Parachain::register(register_para_options, &scoped_fs).await?;
diff --git a/crates/orchestrator/src/network.rs b/crates/orchestrator/src/network.rs
index 0b981e44c20345b377d7f10e6e4e4b196540eaef..73c55c30cc2f967407706da03f68f4a90d60638b 100644
--- a/crates/orchestrator/src/network.rs
+++ b/crates/orchestrator/src/network.rs
@@ -5,16 +5,22 @@ pub mod relaychain;
 use std::{collections::HashMap, path::PathBuf};
 
 use configuration::{
+    para_states::{Initial, Running},
     shared::node::EnvVar,
     types::{Arg, Command, Image, Port},
+    ParachainConfig, ParachainConfigBuilder,
 };
 use provider::{types::TransferedFile, DynNamespace};
 use support::fs::FileSystem;
 
 use self::{node::NetworkNode, parachain::Parachain, relaychain::Relaychain};
 use crate::{
+    generators::chain_spec::ChainSpec,
     network_spec::{self, NetworkSpec},
-    shared::{macros, types::ChainDefaultContext},
+    shared::{
+        macros,
+        types::{ChainDefaultContext, RegisterParachainOptions},
+    },
     spawner::{self, SpawnNodeCtx},
     ScopedFilesystem, ZombieRole,
 };
@@ -290,8 +296,171 @@ impl<T: FileSystem> Network<T> {
         Ok(())
     }
 
-    // This should include at least of collator?
-    // add_parachain()
+    /// Get a parachain config builder from a running network
+    ///
+    /// This allow you to build a new parachain config to be deployed into
+    /// the running network.
+    pub fn para_config_builder(&self) -> ParachainConfigBuilder<Initial, Running> {
+        // TODO: build the validation context from the running network
+        ParachainConfigBuilder::new_with_running(Default::default())
+    }
+
+    /// Add a new parachain to the running network
+    ///
+    /// NOTE: para_id must be unique in the whole network.
+    ///
+    /// # Example:
+    /// ```rust
+    /// # use anyhow::anyhow;
+    /// # use provider::NativeProvider;
+    /// # use support::{fs::local::LocalFileSystem, process::os::OsProcessManager};
+    /// # use zombienet_orchestrator::{errors, AddCollatorOptions, Orchestrator};
+    /// # use configuration::NetworkConfig;
+    /// # async fn example() -> Result<(), anyhow::Error> {
+    /// #   let provider = NativeProvider::new(LocalFileSystem {}, OsProcessManager {});
+    /// #   let orchestrator = Orchestrator::new(LocalFileSystem {}, provider);
+    /// #   let config = NetworkConfig::load_from_toml("config.toml")?;
+    /// let mut network = orchestrator.spawn(config).await?;
+    /// let para_config = network
+    ///     .para_config_builder()
+    ///     .with_id(100)
+    ///     .with_default_command("polkadot-parachain")
+    ///     .with_collator(|c| c.with_name("col-100-1"))
+    ///     .build()
+    ///     .map_err(|_e| anyhow!("Building config"))?;
+    ///
+    /// network.add_parachain(&para_config, None).await?;
+    ///
+    /// #   Ok(())
+    /// # }
+    /// ```
+    pub async fn add_parachain(
+        &mut self,
+        para_config: &ParachainConfig,
+        custom_relaychain_spec: Option<PathBuf>,
+    ) -> Result<(), anyhow::Error> {
+        // build
+        let mut para_spec = network_spec::parachain::ParachainSpec::from_config(para_config)?;
+        let base_dir = self.ns.base_dir().to_string_lossy().to_string();
+        let scoped_fs = ScopedFilesystem::new(&self.filesystem, &base_dir);
+
+        let mut global_files_to_inject = vec![];
+
+        // get relaychain id
+        let relay_chain_id = if let Some(custom_path) = custom_relaychain_spec {
+            // use this file as relaychain spec
+            global_files_to_inject.push(TransferedFile {
+                local_path: custom_path.clone(),
+                remote_path: PathBuf::from(format!("/cfg/{}.json", self.relaychain().chain)),
+            });
+            let content = std::fs::read_to_string(custom_path)?;
+            ChainSpec::chain_id_from_spec(&content)?
+        } else {
+            global_files_to_inject.push(TransferedFile {
+                local_path: PathBuf::from(format!(
+                    "{}/{}",
+                    scoped_fs.base_dir,
+                    self.relaychain().chain_spec_path.to_string_lossy()
+                )),
+                remote_path: PathBuf::from(format!("/cfg/{}.json", self.relaychain().chain)),
+            });
+            self.relay.chain_id.clone()
+        };
+
+        let chain_spec_raw_path = para_spec
+            .build_chain_spec(&relay_chain_id, &self.ns, &scoped_fs)
+            .await?;
+        scoped_fs.create_dir(para_spec.id.to_string()).await?;
+        // create wasm/state
+        para_spec
+            .genesis_state
+            .build(
+                chain_spec_raw_path.as_ref(),
+                format!("{}/genesis-state", para_spec.id),
+                &self.ns,
+                &scoped_fs,
+            )
+            .await?;
+        para_spec
+            .genesis_wasm
+            .build(
+                chain_spec_raw_path.as_ref(),
+                format!("{}/para_spec-wasm", para_spec.id),
+                &self.ns,
+                &scoped_fs,
+            )
+            .await?;
+
+        let parachain =
+            Parachain::from_spec(&para_spec, &global_files_to_inject, &scoped_fs).await?;
+        let parachain_id = parachain.chain_id.clone();
+
+        // Create `ctx` for spawn the nodes
+        let ctx_para = SpawnNodeCtx {
+            parachain: Some(&para_spec),
+            parachain_id: parachain_id.as_deref(),
+            role: if para_spec.is_cumulus_based {
+                ZombieRole::CumulusCollator
+            } else {
+                ZombieRole::Collator
+            },
+            bootnodes_addr: &vec![],
+            chain_id: &self.relaychain().chain_id,
+            chain: &self.relaychain().chain,
+            ns: &self.ns,
+            scoped_fs: &scoped_fs,
+            wait_ready: false,
+        };
+
+        // Register the parachain to the running network
+        let first_node_url = self
+            .relaychain()
+            .nodes
+            .first()
+            .ok_or(anyhow::anyhow!(
+                "At least one node of the relaychain should be running"
+            ))?
+            .ws_uri();
+        let register_para_options = RegisterParachainOptions {
+            id: parachain.para_id,
+            // This needs to resolve correctly
+            wasm_path: para_spec
+                .genesis_wasm
+                .artifact_path()
+                .ok_or(anyhow::anyhow!(
+                    "artifact path for wasm must be set at this point",
+                ))?
+                .to_path_buf(),
+            state_path: para_spec
+                .genesis_state
+                .artifact_path()
+                .ok_or(anyhow::anyhow!(
+                    "artifact path for state must be set at this point",
+                ))?
+                .to_path_buf(),
+            node_ws_url: first_node_url.to_string(),
+            onboard_as_para: para_spec.onboard_as_parachain,
+            seed: None, // TODO: Seed is passed by?
+            finalization: false,
+        };
+
+        Parachain::register(register_para_options, &scoped_fs).await?;
+
+        // Spawn the nodes
+        let spawning_tasks = para_spec
+            .collators
+            .iter()
+            .map(|node| spawner::spawn_node(node, parachain.files_to_inject.clone(), &ctx_para));
+
+        let running_nodes = futures::future::try_join_all(spawning_tasks).await?;
+        let running_para_id = parachain.para_id;
+        self.add_para(parachain);
+        for node in running_nodes {
+            self.add_running_node(node, Some(running_para_id));
+        }
+
+        Ok(())
+    }
 
     // deregister and stop the collator?
     // remove_parachain()
@@ -324,6 +493,7 @@ impl<T: FileSystem> Network<T> {
             if let Some(para) = self.parachains.get_mut(&para_id) {
                 para.collators.push(node.clone());
             } else {
+                // is the first node of the para, let create the entry
                 unreachable!()
             }
         } else {
diff --git a/crates/orchestrator/src/network/parachain.rs b/crates/orchestrator/src/network/parachain.rs
index 0a3c54d786d6c7f33da502e650507f4fde4fe435..9e43039eadfc7047780b37e99a627a7503e7fc34 100644
--- a/crates/orchestrator/src/network/parachain.rs
+++ b/crates/orchestrator/src/network/parachain.rs
@@ -3,6 +3,7 @@ use std::{
     str::FromStr,
 };
 
+use provider::types::TransferedFile;
 use subxt::{dynamic::Value, OnlineClient, SubstrateConfig};
 use subxt_signer::{sr25519::Keypair, SecretUri};
 use support::fs::FileSystem;
@@ -11,7 +12,10 @@ use tracing::info;
 // use crate::generators::key::generate_pair;
 // use sp_core::{sr25519, Pair};
 use super::node::NetworkNode;
-use crate::{shared::types::RegisterParachainOptions, ScopedFilesystem};
+use crate::{
+    network_spec::parachain::ParachainSpec, shared::types::RegisterParachainOptions,
+    ScopedFilesystem,
+};
 
 #[derive(Debug)]
 pub struct Parachain {
@@ -20,6 +24,7 @@ pub struct Parachain {
     pub(crate) chain_id: Option<String>,
     pub(crate) chain_spec_path: Option<PathBuf>,
     pub(crate) collators: Vec<NetworkNode>,
+    pub(crate) files_to_inject: Vec<TransferedFile>,
 }
 
 impl Parachain {
@@ -30,6 +35,7 @@ impl Parachain {
             chain_id: None,
             chain_spec_path: None,
             collators: Default::default(),
+            files_to_inject: Default::default(),
         }
     }
 
@@ -44,9 +50,46 @@ impl Parachain {
             chain_id: Some(chain_id.into()),
             chain_spec_path: Some(chain_spec_path.as_ref().into()),
             collators: Default::default(),
+            files_to_inject: Default::default(),
         }
     }
 
+    pub(crate) async fn from_spec(
+        para: &ParachainSpec,
+        files_to_inject: &[TransferedFile],
+        scoped_fs: &ScopedFilesystem<'_, impl FileSystem>,
+    ) -> Result<Self, anyhow::Error> {
+        let mut para_files_to_inject = files_to_inject.to_owned();
+
+        // parachain id is used for the keystore
+        let mut para = if let Some(chain_spec) = para.chain_spec.as_ref() {
+            let id = chain_spec.read_chain_id(scoped_fs).await?;
+
+            // add the spec to global files to inject
+            let spec_name = chain_spec.chain_spec_name();
+            let base = PathBuf::from_str(scoped_fs.base_dir)?;
+            para_files_to_inject.push(TransferedFile {
+                local_path: base.join(format!("{}.json", spec_name)),
+                remote_path: PathBuf::from(format!("/cfg/{}.json", para.id)),
+            });
+
+            let raw_path = chain_spec
+                .raw_path()
+                .ok_or(anyhow::anyhow!("chain-spec path should be set by now.",))?;
+            let mut running_para = Parachain::with_chain_spec(para.id, id, raw_path);
+            if let Some(chain_name) = chain_spec.chain_name() {
+                running_para.chain = Some(chain_name.to_string());
+            }
+            running_para
+        } else {
+            Parachain::new(para.id)
+        };
+
+        para.files_to_inject = para_files_to_inject;
+
+        Ok(para)
+    }
+
     pub async fn register(
         options: RegisterParachainOptions,
         scoped_fs: &ScopedFilesystem<'_, impl FileSystem>,
@@ -105,3 +148,69 @@ impl Parachain {
         Ok(())
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use std::collections::HashMap;
+
+    use super::*;
+
+    #[test]
+    fn create_with_is_works() {
+        let para = Parachain::new(100);
+        // only para_id should be set
+        assert_eq!(para.para_id, 100);
+        assert_eq!(para.chain_id, None);
+        assert_eq!(para.chain, None);
+        assert_eq!(para.chain_spec_path, None);
+    }
+
+    #[test]
+    fn create_with_chain_spec_works() {
+        let para = Parachain::with_chain_spec(100, "rococo-local", "/tmp/rococo-local.json");
+        // only para_id should be set
+        assert_eq!(para.para_id, 100);
+        assert_eq!(para.chain_id, Some("rococo-local".to_string()));
+        assert_eq!(para.chain, None);
+        assert_eq!(
+            para.chain_spec_path,
+            Some(PathBuf::from("/tmp/rococo-local.json"))
+        );
+    }
+
+    #[tokio::test]
+    async fn create_with_para_spec_works() {
+        use configuration::ParachainConfigBuilder;
+
+        use crate::network_spec::parachain::ParachainSpec;
+
+        let para_config = ParachainConfigBuilder::new(Default::default())
+            .with_id(100)
+            .cumulus_based(false)
+            .with_default_command("adder-collator")
+            .with_collator(|c| c.with_name("col"))
+            .build()
+            .unwrap();
+
+        let para_spec = ParachainSpec::from_config(&para_config).unwrap();
+        let fs = support::fs::in_memory::InMemoryFileSystem::new(HashMap::default());
+        let scoped_fs = ScopedFilesystem {
+            fs: &fs,
+            base_dir: "/tmp/some",
+        };
+
+        let files = vec![TransferedFile {
+            local_path: PathBuf::from("/tmp/some"),
+            remote_path: PathBuf::from("/tmp/some"),
+        }];
+        let para = Parachain::from_spec(&para_spec, &files, &scoped_fs)
+            .await
+            .unwrap();
+        println!("{:#?}", para);
+        assert_eq!(para.para_id, 100);
+        assert_eq!(para.chain_id, None);
+        assert_eq!(para.chain, None);
+        // one file should be added.
+        assert_eq!(para.files_to_inject.len(), 1);
+    }
+}
diff --git a/crates/orchestrator/src/network_spec/parachain.rs b/crates/orchestrator/src/network_spec/parachain.rs
index 33e1217f0690f178b5a754d91f24f34768d39705..8597cd468f1efdabec7dc7c7535855b23ef23d81 100644
--- a/crates/orchestrator/src/network_spec/parachain.rs
+++ b/crates/orchestrator/src/network_spec/parachain.rs
@@ -1,8 +1,12 @@
+use std::path::PathBuf;
+
 use configuration::{
     shared::resources::Resources,
     types::{Arg, AssetLocation, Command, Image},
     ParachainConfig, RegistrationStrategy,
 };
+use provider::DynNamespace;
+use support::fs::FileSystem;
 
 use super::node::NodeSpec;
 use crate::{
@@ -12,6 +16,7 @@ use crate::{
         para_artifact::*,
     },
     shared::types::ChainDefaultContext,
+    ScopedFilesystem,
 };
 
 #[derive(Debug, Clone)]
@@ -183,4 +188,39 @@ impl ParachainSpec {
 
         Ok(para_spec)
     }
+
+    /// Build parachain chain-spec
+    ///
+    /// This fn customize the chain-spec (if is possible) and build the raw version
+    /// of the chain-spec.
+    pub(crate) async fn build_chain_spec<'a, T>(
+        &mut self,
+        relay_chain_id: &str,
+        ns: &DynNamespace,
+        scoped_fs: &ScopedFilesystem<'a, T>,
+    ) -> Result<Option<PathBuf>, anyhow::Error>
+    where
+        T: FileSystem,
+    {
+        let cloned = self.clone();
+        let chain_spec_raw_path = if let Some(chain_spec) = self.chain_spec.as_mut() {
+            chain_spec.build(ns, scoped_fs).await?;
+
+            chain_spec
+                .customize_para(&cloned, relay_chain_id, scoped_fs)
+                .await?;
+            chain_spec.build_raw(ns).await?;
+
+            let chain_spec_raw_path =
+                chain_spec
+                    .raw_path()
+                    .ok_or(OrchestratorError::InvariantError(
+                        "chain-spec raw path should be set now",
+                    ))?;
+            Some(chain_spec_raw_path.to_path_buf())
+        } else {
+            None
+        };
+        Ok(chain_spec_raw_path)
+    }
 }