You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by oa...@apache.org on 2021/01/27 10:17:23 UTC

[camel] branch master updated: CAMEL-15933: Add Stitch component to Camel (#4936)

This is an automated email from the ASF dual-hosted git repository.

oalsafi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/master by this push:
     new db6d9c9  CAMEL-15933: Add Stitch component to Camel (#4936)
db6d9c9 is described below

commit db6d9c9d2c7832b3edd0b9a82a31866883cc1498
Author: Omar Al-Safi <om...@gmail.com>
AuthorDate: Wed Jan 27 11:16:52 2021 +0100

    CAMEL-15933: Add Stitch component to Camel (#4936)
    
    * CAMEL-15933: Stitch component scaffold
    
    * CAMEL-15933: Add initial objects data
    
    * CAMEL-15933: Add initial objects data
    
    * CAMEL-15933: Add more models
    
    * CAMEL-15933: Working simple request
    
    * CAMEL-15933: Start of refactor
    
    * CAMEL-15933: Progress in the refactor
    
    * CAMEL-15933: Regen
    
    * CAMEL-15933: Refactor
    
    * CAMEL-15933: Complete the client with all unit tests and IT
    
    * CAMEL-15933: Start working on the component
    
    * CAMEL-15933: Start working on the component
    
    * CAMEL-15933: Start working on the component
    
    * CAMEL-15933: Start working on the component
    
    * CAMEL-15933: Add tests
    
    * CAMEL-15933: Add IT tests
    
    * CAMEL-15933: Add IT tests
    
    * CAMEL-15933: Add the last tests and fix source check
    
    * CAMEL-15933: Add the last tests and fix source check
    
    * CAMEL-15933: Change to doStart
    
    * CAMEL-15933: Add docs
    
    * CAMEL-15933: Address comments
    
    * CAMEL-15933: Address comments
---
 .../camel/catalog/docs/stitch-component.adoc       | 312 +++++++++++++++++
 components/camel-stitch/pom.xml                    | 113 +++++++
 .../stitch/StitchComponentConfigurer.java          | 106 ++++++
 .../component/stitch/StitchEndpointConfigurer.java |  90 +++++
 .../component/stitch/StitchEndpointUriFactory.java |  71 ++++
 .../services/org/apache/camel/component.properties |   7 +
 .../services/org/apache/camel/component/stitch     |   2 +
 .../org/apache/camel/configurer/stitch-component   |   2 +
 .../org/apache/camel/configurer/stitch-endpoint    |   2 +
 .../org/apache/camel/urifactory/stitch-endpoint    |   2 +
 .../org/apache/camel/component/stitch/stitch.json  |  47 +++
 .../src/main/docs/stitch-component.adoc            | 312 +++++++++++++++++
 .../camel/component/stitch/StitchComponent.java    |  71 ++++
 .../component/stitch/StitchConfiguration.java      | 160 +++++++++
 .../camel/component/stitch/StitchConstants.java    |  32 ++
 .../camel/component/stitch/StitchEndpoint.java     | 111 +++++++
 .../camel/component/stitch/StitchProducer.java     |  79 +++++
 .../camel/component/stitch/client/JsonUtils.java   |  48 +++
 .../component/stitch/client/StitchClient.java      |  42 +++
 .../stitch/client/StitchClientBuilder.java         |  87 +++++
 .../component/stitch/client/StitchClientImpl.java  | 131 ++++++++
 .../component/stitch/client/StitchRegion.java      |  41 +++
 .../stitch/client/models/StitchError.java          |  56 ++++
 .../stitch/client/models/StitchException.java      |  54 +++
 .../stitch/client/models/StitchMessage.java        | 165 +++++++++
 .../stitch/client/models/StitchModel.java          |  29 ++
 .../stitch/client/models/StitchRequestBody.java    | 205 ++++++++++++
 .../stitch/client/models/StitchResponse.java       |  88 +++++
 .../stitch/client/models/StitchSchema.java         |  76 +++++
 .../operations/StitchProducerOperations.java       | 197 +++++++++++
 .../component/stitch/StitchComponentTest.java      |  46 +++
 .../camel/component/stitch/StitchProducerIT.java   | 171 ++++++++++
 .../camel/component/stitch/StitchProducerTest.java | 230 +++++++++++++
 .../camel/component/stitch/StitchTestUtils.java    |  37 +++
 .../stitch/client/StitchClientBuilderTest.java     |  51 +++
 .../stitch/client/StitchClientImplIT.java          | 109 ++++++
 .../component/stitch/client/StitchRegionTest.java  |  32 ++
 .../stitch/client/models/StitchMessageTest.java    |  79 +++++
 .../client/models/StitchRequestBodyTest.java       | 128 +++++++
 .../stitch/client/models/StitchResponseTest.java   |  43 +++
 .../stitch/client/models/StitchSchemaTest.java     |  55 +++
 .../operations/StitchProducerOperationsIT.java     | 159 +++++++++
 .../operations/StitchProducerOperationsTest.java   | 322 ++++++++++++++++++
 .../src/test/resources/log4j2.properties           |  27 ++
 components/pom.xml                                 |   1 +
 core/camel-allcomponents/pom.xml                   |   4 +
 .../component/ComponentsBuilderFactory.java        |  14 +
 .../dsl/StitchComponentBuilderFactory.java         | 271 +++++++++++++++
 .../src/generated/resources/metadata.json          |  22 ++
 .../builder/endpoint/EndpointBuilderFactory.java   |   1 +
 .../camel/builder/endpoint/EndpointBuilders.java   |   1 +
 .../builder/endpoint/StaticEndpointBuilders.java   |  51 +++
 .../endpoint/dsl/StitchEndpointBuilderFactory.java | 369 +++++++++++++++++++++
 .../modules/ROOT/pages/stitch-component.adoc       | 314 ++++++++++++++++++
 parent/pom.xml                                     |   6 +
 55 files changed, 5281 insertions(+)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/stitch-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/stitch-component.adoc
new file mode 100644
index 0000000..7bfac91
--- /dev/null
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/stitch-component.adoc
@@ -0,0 +1,312 @@
+[[stitch-component]]
+= Stitch Component
+:docTitle: Stitch
+:artifactId: camel-stitch
+:description: Stitch is a cloud ETL service that integrates various data sources into a central data warehouse through various integrations.
+:since: 3.8
+:supportLevel: Preview
+:component-header: Only producer is supported
+include::{cq-version}@camel-quarkus:ROOT:partial$reference/components/stitch.adoc[opts=optional]
+
+*Since Camel {since}*
+
+*{component-header}*
+
+Stitch is a cloud ETL service, developer-focused platform for rapidly moving and replicates data from more than 90
+applications and databases. It integrates various data sources into a central data warehouse. Stitch has integrations
+for many enterprise software data sources, and can receive data via WebHooks and an API (Stitch Import API) which
+Camel Stitch Component uses to produce the data to Stitch ETL.
+
+For more info, feel free to visit their website: https://www.stitchdata.com/
+
+Prerequisites
+
+You must have a valid Stitch account, you will need to enable Stitch Import API and generate a token for the
+integration, for more info, please find more info https://www.stitchdata.com/docs/developers/import-api/guides/quick-start[here].
+
+Maven users will need to add the following dependency to their `pom.xml`
+for this component:
+
+[source,xml]
+------------------------------------------------------------
+<dependency>
+    <groupId>org.apache.camel</groupId>
+    <artifactId>camel-stitch</artifactId>
+    <version>x.x.x</version>
+  <!-- use the same version as your Camel core version -->
+</dependency>
+------------------------------------------------------------
+
+
+== URI format
+
+[source,text]
+------------------------------
+stitch:[tableName]//[?options]
+------------------------------
+
+For example in order to produce data to Stitch from a custom processor:
+[source,java]
+--------------------------------------------------------------------------------
+from("direct:sendStitch")
+     .process(exchange -> {
+         final StitchMessage stitchMessage = StitchMessage.builder()
+               .withData("field_1", "stitchMessage2-1")
+               .build();
+
+         final StitchRequestBody stitchRequestBody = StitchRequestBody.builder()
+                .addMessage(stitchMessage)
+                .withSchema(StitchSchema.builder().addKeyword("field_1", "string").build())
+                .withTableName("table_1")
+                .withKeyNames(Collections.singleton("field_1"))
+                .build();
+
+                exchange.getMessage().setBody(stitchRequestBody);
+     })
+.to("stitch:table_1?token=RAW({{token}})");
+--------------------------------------------------------------------------------
+
+== URI Options
+// endpoint options: START
+The Stitch endpoint is configured using URI syntax:
+
+----
+stitch:tableName
+----
+
+with the following path and query parameters:
+
+=== Path Parameters (1 parameters):
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *tableName* | The name of the destination table the data is being pushed to. Table names must be unique in each destination schema, or loading issues will occur. Note: The number of characters in the table name should be within the destinations allowed limits or data will rejected. |  | String
+|===
+
+
+=== Query Parameters (8 parameters):
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *keyNames* (producer) | A collection of comma separated strings representing the Primary Key fields in the source table. Stitch use these Primary Keys to de-dupe data during loading If not provided, the table will be loaded in an append-only manner. |  | String
+| *lazyStartProducer* (producer) | Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and [...]
+| *region* (producer) | Stitch account region, e.g: europe. There are 2 enums and the value can be one of: NORTH_AMERICA, EUROPE | europe | StitchRegion
+| *stitchSchema* (producer) | *Autowired* A schema that describes the record(s) |  | StitchSchema
+| *connectionProvider* (producer) | *Autowired* ConnectionProvider contain configuration for the HttpClient like Maximum connection limit .. etc, you can inject this ConnectionProvider and the StitchClient will initialize HttpClient with this ConnectionProvider |  | ConnectionProvider
+| *httpClient* (producer) | *Autowired* Reactor Netty HttpClient, you can injected it if you want to have custom HttpClient |  | HttpClient
+| *stitchClient* (advanced) | *Autowired* Set a custom StitchClient that implements org.apache.camel.component.stitch.client.StitchClient interface |  | StitchClient
+| *token* (security) | *Required* Stitch access token for the Stitch Import API |  | String
+|===
+// endpoint options: END
+
+== Component Options
+// component options: START
+The Stitch component supports 10 options, which are listed below.
+
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *configuration* (producer) | The component configurations |  | StitchConfiguration
+| *keyNames* (producer) | A collection of comma separated strings representing the Primary Key fields in the source table. Stitch use these Primary Keys to de-dupe data during loading If not provided, the table will be loaded in an append-only manner. |  | String
+| *lazyStartProducer* (producer) | Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and [...]
+| *region* (producer) | Stitch account region, e.g: europe. There are 2 enums and the value can be one of: NORTH_AMERICA, EUROPE | europe | StitchRegion
+| *stitchSchema* (producer) | *Autowired* A schema that describes the record(s) |  | StitchSchema
+| *connectionProvider* (producer) | *Autowired* ConnectionProvider contain configuration for the HttpClient like Maximum connection limit .. etc, you can inject this ConnectionProvider and the StitchClient will initialize HttpClient with this ConnectionProvider |  | ConnectionProvider
+| *httpClient* (producer) | *Autowired* Reactor Netty HttpClient, you can injected it if you want to have custom HttpClient |  | HttpClient
+| *autowiredEnabled* (advanced) | Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc. | true | boolean
+| *stitchClient* (advanced) | *Autowired* Set a custom StitchClient that implements org.apache.camel.component.stitch.client.StitchClient interface |  | StitchClient
+| *token* (security) | *Required* Stitch access token for the Stitch Import API |  | String
+|===
+// component options: END
+
+
+== Async Producer
+
+This component implements the async Consumer and producer.
+
+This allows camel route to consume and produce events asynchronously without blocking any threads.
+
+== Usage
+
+=== Message headers evaluated by the component producer
+Before sending a message to Stitch component you can configure the following headers.
+
+[width="100%",cols="10%,10%,10%,70%",options="header",]
+|=======================================================================
+|Header |Variable Name |Type |Description
+
+|`CamelStitchTableName`| `StitchConstants.TABLE_NAME`|`String`| The name of the destination table the data is being pushed to. Table names must be unique in each destination schema, or loading issues will occur. Note: The number of characters in the table name should be within the destinations allowed limits or data will rejected.
+|`CamelStitchSchema`| `StitchConstants.SCHEMA`|`StitchSchema`| The schema that describes the Stitch message of type `org.apache.camel.component.stitch.client.models.StitchSchema`
+|`CamelStitchKeyNames`| `StitchConstants.KEY_NAMES`|`Collection<String>`| A collection of strings representing the Primary Key fields in the source table. Stitch use these Primary Keys to de-dupe data during loading If not provided, the table will be loaded in an append-only manner.
+|=======================================================================
+
+=== Message headers set by the component producer
+After the message is sent to Stitch, the following headers are available
+
+[width="100%",cols="10%,10%,10%,70%",options="header",]
+|=======================================================================
+|Header |Variable Name |Type |Description
+
+|`CamelStitchCode`| `StitchConstants.CODE`|`Integer`| HTTP Status code that is returned from Stitch Import HTTP API.
+|`CamelStitchHeaders`| `StitchConstants.HEADERS`|`Map`| HTTP headers that are returned from Stitch Import HTTP API.
+|`CamelStitchStatus`| `StitchConstants.STATUS`|`String`| The status message that Stitch returns after sending the data through Stitch Import API.
+|=======================================================================
+
+=== Message body type
+Currently, the component supports the following types for the body message on the producer side when producing a message to Stitch component:
+
+* `org.apache.camel.component.stitch.client.models.StitchRequestBody`: This represents this Stitch https://www.stitchdata.com/docs/developers/import-api/api#batch-data--arguments[JSON Message]. However,
+`StitchRequestBody` includes a type safe builder that helps on building the request body. Please note that, `tableName`, `keyNames` and `schema` options are no longer required if you send the
+data with `StitchRequestBody`, if you still set these options, they override whatever being set in message body `StitchRequestBody`.
+
+* `org.apache.camel.component.stitch.client.models.StitchMessage`: This represents https://www.stitchdata.com/docs/developers/import-api/api#message-object[this Stitch message structure].
+If you choose to send your message as `StitchMessage`, *you will need* to add `tableName`, `keyNames` and `schema` options to either the Exchange headers or through the endpoint options.
+
+* `Map`: You can also send the data as `Map`, the data structure must follow this https://www.stitchdata.com/docs/developers/import-api/api#batch-data--arguments[JSON Message] structure which is similar to
+`StitchRequestBody` but with drawback losing on all the type safety builder that is included with `StitchRequestBody`.
+
+* `Iterable`: You can send multiple Stitch messages that are aggregated by Camel or aggregated through custom processor. These aggregated messages
+can be type of `StitchMessage`, `StitchRequestBody` or `Map` but this Map here is similar to `StitchMessage`.
+
+
+
+=== Examples
+
+Here are list of examples of data that can be proceeded to Stitch:
+
+===== Input body type `org.apache.camel.component.stitch.client.models.StitchRequestBody`:
+[source,java]
+--------------------------------------------------------------------------------
+from("direct:sendStitch")
+     .process(exchange -> {
+         final StitchMessage stitchMessage = StitchMessage.builder()
+               .withData("field_1", "stitchMessage2-1")
+               .build();
+
+         final StitchRequestBody stitchRequestBody = StitchRequestBody.builder()
+                .addMessage(stitchMessage)
+                .withSchema(StitchSchema.builder().addKeyword("field_1", "string").build())
+                .withTableName("table_1")
+                .withKeyNames(Collections.singleton("field_1"))
+                .build();
+
+                exchange.getMessage().setBody(stitchRequestBody);
+     })
+.to("stitch:table_1?token=RAW({{token}})");
+--------------------------------------------------------------------------------
+
+
+===== Input body type `org.apache.camel.component.stitch.client.models.StitchMessage`:
+[source,java]
+--------------------------------------------------------------------------------
+from("direct:sendStitch")
+     .process(exchange -> {
+         exchange.getMessage().setHeader(StitchConstants.SCHEMA, StitchSchema.builder().addKeyword("field_1", "string").build());
+         exchange.getMessage().setHeader(StitchConstants.KEY_NAMES, Collections.singleton("field_1"));
+         exchange.getMessage().setHeader(StitchConstants.TABLE_NAME, "table_1");
+
+         final StitchMessage stitchMessage = StitchMessage.builder()
+               .withData("field_1", "stitchMessage2-1")
+               .build();
+
+                exchange.getMessage().setBody(stitchMessage);
+     })
+.to("stitch:table_1?token=RAW({{token}})");
+--------------------------------------------------------------------------------
+
+
+===== Input body type `Map`:
+[source,java]
+--------------------------------------------------------------------------------
+from("direct:sendStitch")
+     .process(exchange -> {
+        final Map<String, Object> properties = new LinkedHashMap<>();
+        properties.put("id", Collections.singletonMap("type", "integer"));
+        properties.put("name", Collections.singletonMap("type", "string"));
+        properties.put("age", Collections.singletonMap("type", "integer"));
+        properties.put("has_magic", Collections.singletonMap("type", "boolean"));
+
+        final Map<String, Object> data = new LinkedHashMap<>();
+        data.put(StitchRequestBody.TABLE_NAME, "my_table");
+        data.put(StitchRequestBody.SCHEMA, Collections.singletonMap("properties", properties));
+        data.put(StitchRequestBody.MESSAGES,
+                Collections.singletonList(Collections.singletonMap("data", Collections.singletonMap("id", 2))));
+        data.put(StitchRequestBody.KEY_NAMES, Collections.singletonList("test_key"));
+
+        exchange.getMessage().setBody(data);
+     })
+.to("stitch:table_1?token=RAW({{token}})");
+--------------------------------------------------------------------------------
+
+
+
+===== Input body type `Iterable`:
+[source,java]
+--------------------------------------------------------------------------------
+from("direct:sendStitch")
+     .process(exchange -> {
+         exchange.getMessage().setHeader(StitchConstants.SCHEMA, StitchSchema.builder().addKeyword("field_1", "string").build());
+         exchange.getMessage().setHeader(StitchConstants.KEY_NAMES, Collections.singleton("field_1"));
+         exchange.getMessage().setHeader(StitchConstants.TABLE_NAME, "table_1");
+
+        final StitchMessage stitchMessage1 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage1")
+                .build();
+
+        final StitchMessage stitchMessage2 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage2-1")
+                .build();
+
+        final StitchRequestBody stitchMessage2RequestBody = StitchRequestBody.builder()
+                .addMessage(stitchMessage2)
+                .withSchema(StitchSchema.builder().addKeyword("field_1", "integer").build())
+                .withTableName("table_1")
+                .withKeyNames(Collections.singleton("field_1"))
+                .build();
+
+        final Map<String, Object> stitchMessage3 = new LinkedHashMap<>();
+        stitchMessage3.put(StitchMessage.DATA, Collections.singletonMap("field_1", "stitchMessage3"));
+
+        final StitchMessage stitchMessage4 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage4")
+                .build();
+
+        final Exchange stitchMessage4Exchange = new DefaultExchange(context);
+        stitchMessage4Exchange.getMessage().setBody(stitchMessage4);
+
+        final StitchMessage stitchMessage5 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage5")
+                .build();
+
+        final Message stitchMessage5Message = new DefaultExchange(context).getMessage();
+        stitchMessage5Message.setBody(stitchMessage5);
+
+        final List<Object> inputMessages = new LinkedList<>();
+        inputMessages.add(stitchMessage1);
+        inputMessages.add(stitchMessage2RequestBody);
+        inputMessages.add(stitchMessage3);
+        inputMessages.add(stitchMessage4Exchange);
+        inputMessages.add(stitchMessage5Message);
+
+        exchange.getMessage().setBody(inputMessages);
+     })
+.to("stitch:table_1?token=RAW({{token}})");
+--------------------------------------------------------------------------------
+
+=== Development Notes (Important)
+When developing on this component, you will need to obtain your Stitch token in order to run the integration tests. In addition to the mocked unit tests
+you *will need to run the integration tests with every change you make*
+To run the integration tests, on this component directory, run the following maven command:
+----
+mvn verify -PfullTests -Dtoken=stitchToken
+----
+Whereby `token` is your Stitch token that is generated for Stitch Import API integration.
+
+
+include::camel-spring-boot::page$stitch-starter.adoc[]
diff --git a/components/camel-stitch/pom.xml b/components/camel-stitch/pom.xml
new file mode 100644
index 0000000..061f599
--- /dev/null
+++ b/components/camel-stitch/pom.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>components</artifactId>
+        <version>3.8.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>camel-stitch</artifactId>
+    <packaging>jar</packaging>
+
+    <name>Camel :: Stitch</name>
+    <description>Camel Stitch Component</description>
+
+    <properties>
+        <firstVersion>3.8.0</firstVersion>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+
+        <!-- reactor -->
+        <dependency>
+            <groupId>io.projectreactor.netty</groupId>
+            <artifactId>reactor-netty</artifactId>
+            <version>${reactor-netty-version}</version>
+        </dependency>
+
+        <!-- for testing -->
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-slf4j-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-junit-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>${commons-lang3-version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <profiles>
+        <profile>
+            <id>fullTests</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <version>${maven-surefire-plugin-version}</version>
+                        <executions>
+                            <execution>
+                                <phase>integration-test</phase>
+                                <goals>
+                                    <goal>test</goal>
+                                </goals>
+                                <configuration>
+                                    <excludes>
+                                        <exclude>none</exclude>
+                                    </excludes>
+                                    <includes>
+                                        <include>**/*IT</include>
+                                    </includes>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/components/camel-stitch/src/generated/java/org/apache/camel/component/stitch/StitchComponentConfigurer.java b/components/camel-stitch/src/generated/java/org/apache/camel/component/stitch/StitchComponentConfigurer.java
new file mode 100644
index 0000000..d000f03
--- /dev/null
+++ b/components/camel-stitch/src/generated/java/org/apache/camel/component/stitch/StitchComponentConfigurer.java
@@ -0,0 +1,106 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.stitch;
+
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.support.component.PropertyConfigurerSupport;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@SuppressWarnings("unchecked")
+public class StitchComponentConfigurer extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+    private org.apache.camel.component.stitch.StitchConfiguration getOrCreateConfiguration(StitchComponent target) {
+        if (target.getConfiguration() == null) {
+            target.setConfiguration(new org.apache.camel.component.stitch.StitchConfiguration());
+        }
+        return target.getConfiguration();
+    }
+
+    @Override
+    public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
+        StitchComponent target = (StitchComponent) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "autowiredenabled":
+        case "autowiredEnabled": target.setAutowiredEnabled(property(camelContext, boolean.class, value)); return true;
+        case "configuration": target.setConfiguration(property(camelContext, org.apache.camel.component.stitch.StitchConfiguration.class, value)); return true;
+        case "connectionprovider":
+        case "connectionProvider": getOrCreateConfiguration(target).setConnectionProvider(property(camelContext, reactor.netty.resources.ConnectionProvider.class, value)); return true;
+        case "httpclient":
+        case "httpClient": getOrCreateConfiguration(target).setHttpClient(property(camelContext, reactor.netty.http.client.HttpClient.class, value)); return true;
+        case "keynames":
+        case "keyNames": getOrCreateConfiguration(target).setKeyNames(property(camelContext, java.lang.String.class, value)); return true;
+        case "lazystartproducer":
+        case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true;
+        case "region": getOrCreateConfiguration(target).setRegion(property(camelContext, org.apache.camel.component.stitch.client.StitchRegion.class, value)); return true;
+        case "stitchclient":
+        case "stitchClient": getOrCreateConfiguration(target).setStitchClient(property(camelContext, org.apache.camel.component.stitch.client.StitchClient.class, value)); return true;
+        case "stitchschema":
+        case "stitchSchema": getOrCreateConfiguration(target).setStitchSchema(property(camelContext, org.apache.camel.component.stitch.client.models.StitchSchema.class, value)); return true;
+        case "token": getOrCreateConfiguration(target).setToken(property(camelContext, java.lang.String.class, value)); return true;
+        default: return false;
+        }
+    }
+
+    @Override
+    public String[] getAutowiredNames() {
+        return new String[]{"connectionProvider","httpClient","stitchClient","stitchSchema"};
+    }
+
+    @Override
+    public Class<?> getOptionType(String name, boolean ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "autowiredenabled":
+        case "autowiredEnabled": return boolean.class;
+        case "configuration": return org.apache.camel.component.stitch.StitchConfiguration.class;
+        case "connectionprovider":
+        case "connectionProvider": return reactor.netty.resources.ConnectionProvider.class;
+        case "httpclient":
+        case "httpClient": return reactor.netty.http.client.HttpClient.class;
+        case "keynames":
+        case "keyNames": return java.lang.String.class;
+        case "lazystartproducer":
+        case "lazyStartProducer": return boolean.class;
+        case "region": return org.apache.camel.component.stitch.client.StitchRegion.class;
+        case "stitchclient":
+        case "stitchClient": return org.apache.camel.component.stitch.client.StitchClient.class;
+        case "stitchschema":
+        case "stitchSchema": return org.apache.camel.component.stitch.client.models.StitchSchema.class;
+        case "token": return java.lang.String.class;
+        default: return null;
+        }
+    }
+
+    @Override
+    public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+        StitchComponent target = (StitchComponent) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "autowiredenabled":
+        case "autowiredEnabled": return target.isAutowiredEnabled();
+        case "configuration": return target.getConfiguration();
+        case "connectionprovider":
+        case "connectionProvider": return getOrCreateConfiguration(target).getConnectionProvider();
+        case "httpclient":
+        case "httpClient": return getOrCreateConfiguration(target).getHttpClient();
+        case "keynames":
+        case "keyNames": return getOrCreateConfiguration(target).getKeyNames();
+        case "lazystartproducer":
+        case "lazyStartProducer": return target.isLazyStartProducer();
+        case "region": return getOrCreateConfiguration(target).getRegion();
+        case "stitchclient":
+        case "stitchClient": return getOrCreateConfiguration(target).getStitchClient();
+        case "stitchschema":
+        case "stitchSchema": return getOrCreateConfiguration(target).getStitchSchema();
+        case "token": return getOrCreateConfiguration(target).getToken();
+        default: return null;
+        }
+    }
+}
+
diff --git a/components/camel-stitch/src/generated/java/org/apache/camel/component/stitch/StitchEndpointConfigurer.java b/components/camel-stitch/src/generated/java/org/apache/camel/component/stitch/StitchEndpointConfigurer.java
new file mode 100644
index 0000000..a7bc06f
--- /dev/null
+++ b/components/camel-stitch/src/generated/java/org/apache/camel/component/stitch/StitchEndpointConfigurer.java
@@ -0,0 +1,90 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.stitch;
+
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.support.component.PropertyConfigurerSupport;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@SuppressWarnings("unchecked")
+public class StitchEndpointConfigurer extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+    @Override
+    public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
+        StitchEndpoint target = (StitchEndpoint) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "connectionprovider":
+        case "connectionProvider": target.getConfiguration().setConnectionProvider(property(camelContext, reactor.netty.resources.ConnectionProvider.class, value)); return true;
+        case "httpclient":
+        case "httpClient": target.getConfiguration().setHttpClient(property(camelContext, reactor.netty.http.client.HttpClient.class, value)); return true;
+        case "keynames":
+        case "keyNames": target.getConfiguration().setKeyNames(property(camelContext, java.lang.String.class, value)); return true;
+        case "lazystartproducer":
+        case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true;
+        case "region": target.getConfiguration().setRegion(property(camelContext, org.apache.camel.component.stitch.client.StitchRegion.class, value)); return true;
+        case "stitchclient":
+        case "stitchClient": target.getConfiguration().setStitchClient(property(camelContext, org.apache.camel.component.stitch.client.StitchClient.class, value)); return true;
+        case "stitchschema":
+        case "stitchSchema": target.getConfiguration().setStitchSchema(property(camelContext, org.apache.camel.component.stitch.client.models.StitchSchema.class, value)); return true;
+        case "token": target.getConfiguration().setToken(property(camelContext, java.lang.String.class, value)); return true;
+        default: return false;
+        }
+    }
+
+    @Override
+    public String[] getAutowiredNames() {
+        return new String[]{"connectionProvider","httpClient","stitchClient","stitchSchema"};
+    }
+
+    @Override
+    public Class<?> getOptionType(String name, boolean ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "connectionprovider":
+        case "connectionProvider": return reactor.netty.resources.ConnectionProvider.class;
+        case "httpclient":
+        case "httpClient": return reactor.netty.http.client.HttpClient.class;
+        case "keynames":
+        case "keyNames": return java.lang.String.class;
+        case "lazystartproducer":
+        case "lazyStartProducer": return boolean.class;
+        case "region": return org.apache.camel.component.stitch.client.StitchRegion.class;
+        case "stitchclient":
+        case "stitchClient": return org.apache.camel.component.stitch.client.StitchClient.class;
+        case "stitchschema":
+        case "stitchSchema": return org.apache.camel.component.stitch.client.models.StitchSchema.class;
+        case "token": return java.lang.String.class;
+        default: return null;
+        }
+    }
+
+    @Override
+    public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+        StitchEndpoint target = (StitchEndpoint) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "connectionprovider":
+        case "connectionProvider": return target.getConfiguration().getConnectionProvider();
+        case "httpclient":
+        case "httpClient": return target.getConfiguration().getHttpClient();
+        case "keynames":
+        case "keyNames": return target.getConfiguration().getKeyNames();
+        case "lazystartproducer":
+        case "lazyStartProducer": return target.isLazyStartProducer();
+        case "region": return target.getConfiguration().getRegion();
+        case "stitchclient":
+        case "stitchClient": return target.getConfiguration().getStitchClient();
+        case "stitchschema":
+        case "stitchSchema": return target.getConfiguration().getStitchSchema();
+        case "token": return target.getConfiguration().getToken();
+        default: return null;
+        }
+    }
+}
+
diff --git a/components/camel-stitch/src/generated/java/org/apache/camel/component/stitch/StitchEndpointUriFactory.java b/components/camel-stitch/src/generated/java/org/apache/camel/component/stitch/StitchEndpointUriFactory.java
new file mode 100644
index 0000000..561b9df
--- /dev/null
+++ b/components/camel-stitch/src/generated/java/org/apache/camel/component/stitch/StitchEndpointUriFactory.java
@@ -0,0 +1,71 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.stitch;
+
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.spi.EndpointUriFactory;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+public class StitchEndpointUriFactory extends org.apache.camel.support.component.EndpointUriFactorySupport implements EndpointUriFactory {
+
+    private static final String BASE = ":tableName";
+
+    private static final Set<String> PROPERTY_NAMES;
+    private static final Set<String> SECRET_PROPERTY_NAMES;
+    static {
+        Set<String> props = new HashSet<>(9);
+        props.add("stitchSchema");
+        props.add("httpClient");
+        props.add("lazyStartProducer");
+        props.add("connectionProvider");
+        props.add("stitchClient");
+        props.add("region");
+        props.add("tableName");
+        props.add("keyNames");
+        props.add("token");
+        PROPERTY_NAMES = Collections.unmodifiableSet(props);
+        Set<String> secretProps = new HashSet<>(1);
+        secretProps.add("token");
+        SECRET_PROPERTY_NAMES = Collections.unmodifiableSet(secretProps);
+    }
+
+    @Override
+    public boolean isEnabled(String scheme) {
+        return "stitch".equals(scheme);
+    }
+
+    @Override
+    public String buildUri(String scheme, Map<String, Object> properties, boolean encode) throws URISyntaxException {
+        String syntax = scheme + BASE;
+        String uri = syntax;
+
+        Map<String, Object> copy = new HashMap<>(properties);
+
+        uri = buildPathParameter(syntax, uri, "tableName", null, false, copy);
+        uri = buildQueryParameters(uri, copy, encode);
+        return uri;
+    }
+
+    @Override
+    public Set<String> propertyNames() {
+        return PROPERTY_NAMES;
+    }
+
+    @Override
+    public Set<String> secretPropertyNames() {
+        return SECRET_PROPERTY_NAMES;
+    }
+
+    @Override
+    public boolean isLenientProperties() {
+        return false;
+    }
+}
+
diff --git a/components/camel-stitch/src/generated/resources/META-INF/services/org/apache/camel/component.properties b/components/camel-stitch/src/generated/resources/META-INF/services/org/apache/camel/component.properties
new file mode 100644
index 0000000..9f43280
--- /dev/null
+++ b/components/camel-stitch/src/generated/resources/META-INF/services/org/apache/camel/component.properties
@@ -0,0 +1,7 @@
+# Generated by camel build tools - do NOT edit this file!
+components=stitch
+groupId=org.apache.camel
+artifactId=camel-stitch
+version=3.8.0-SNAPSHOT
+projectName=Camel :: Stitch
+projectDescription=Camel Stitch Component
diff --git a/components/camel-stitch/src/generated/resources/META-INF/services/org/apache/camel/component/stitch b/components/camel-stitch/src/generated/resources/META-INF/services/org/apache/camel/component/stitch
new file mode 100644
index 0000000..6984fcc
--- /dev/null
+++ b/components/camel-stitch/src/generated/resources/META-INF/services/org/apache/camel/component/stitch
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.stitch.StitchComponent
diff --git a/components/camel-stitch/src/generated/resources/META-INF/services/org/apache/camel/configurer/stitch-component b/components/camel-stitch/src/generated/resources/META-INF/services/org/apache/camel/configurer/stitch-component
new file mode 100644
index 0000000..8081ad2
--- /dev/null
+++ b/components/camel-stitch/src/generated/resources/META-INF/services/org/apache/camel/configurer/stitch-component
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.stitch.StitchComponentConfigurer
diff --git a/components/camel-stitch/src/generated/resources/META-INF/services/org/apache/camel/configurer/stitch-endpoint b/components/camel-stitch/src/generated/resources/META-INF/services/org/apache/camel/configurer/stitch-endpoint
new file mode 100644
index 0000000..25f2036
--- /dev/null
+++ b/components/camel-stitch/src/generated/resources/META-INF/services/org/apache/camel/configurer/stitch-endpoint
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.stitch.StitchEndpointConfigurer
diff --git a/components/camel-stitch/src/generated/resources/META-INF/services/org/apache/camel/urifactory/stitch-endpoint b/components/camel-stitch/src/generated/resources/META-INF/services/org/apache/camel/urifactory/stitch-endpoint
new file mode 100644
index 0000000..d66a9f9
--- /dev/null
+++ b/components/camel-stitch/src/generated/resources/META-INF/services/org/apache/camel/urifactory/stitch-endpoint
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.stitch.StitchEndpointUriFactory
diff --git a/components/camel-stitch/src/generated/resources/org/apache/camel/component/stitch/stitch.json b/components/camel-stitch/src/generated/resources/org/apache/camel/component/stitch/stitch.json
new file mode 100644
index 0000000..04a327f
--- /dev/null
+++ b/components/camel-stitch/src/generated/resources/org/apache/camel/component/stitch/stitch.json
@@ -0,0 +1,47 @@
+{
+  "component": {
+    "kind": "component",
+    "name": "stitch",
+    "title": "Stitch",
+    "description": "Stitch is a cloud ETL service that integrates various data sources into a central data warehouse through various integrations.",
+    "deprecated": false,
+    "firstVersion": "3.8.0",
+    "label": "cloud,api,compute,bigdata",
+    "javaType": "org.apache.camel.component.stitch.StitchComponent",
+    "supportLevel": "Preview",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-stitch",
+    "version": "3.8.0-SNAPSHOT",
+    "scheme": "stitch",
+    "extendsScheme": "",
+    "syntax": "stitch:tableName",
+    "async": false,
+    "api": false,
+    "consumerOnly": false,
+    "producerOnly": true,
+    "lenientProperties": false
+  },
+  "componentProperties": {
+    "configuration": { "kind": "property", "displayName": "Configuration", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.stitch.StitchConfiguration", "deprecated": false, "autowired": false, "secret": false, "description": "The component configurations" },
+    "keyNames": { "kind": "property", "displayName": "Key Names", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.stitch.StitchConfiguration", "configurationField": "configuration", "description": "A collection of comma separated strings representing the Primary Key fields in the source table. Stitch use these Primary K [...]
+    "lazyStartProducer": { "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during star [...]
+    "region": { "kind": "property", "displayName": "Region", "group": "producer", "label": "producer", "required": false, "type": "object", "javaType": "org.apache.camel.component.stitch.client.StitchRegion", "enum": [ "NORTH_AMERICA", "EUROPE" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "europe", "configurationClass": "org.apache.camel.component.stitch.StitchConfiguration", "configurationField": "configuration", "description": "Stitch account region, e.g [...]
+    "stitchSchema": { "kind": "property", "displayName": "Stitch Schema", "group": "producer", "label": "producer", "required": false, "type": "object", "javaType": "org.apache.camel.component.stitch.client.models.StitchSchema", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.stitch.StitchConfiguration", "configurationField": "configuration", "description": "A schema that describes the record(s)" },
+    "connectionProvider": { "kind": "property", "displayName": "Connection Provider", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "object", "javaType": "reactor.netty.resources.ConnectionProvider", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.stitch.StitchConfiguration", "configurationField": "configuration", "description": "ConnectionProvider contain configur [...]
+    "httpClient": { "kind": "property", "displayName": "Http Client", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "object", "javaType": "reactor.netty.http.client.HttpClient", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.stitch.StitchConfiguration", "configurationField": "configuration", "description": "Reactor Netty HttpClient, you can injected it if you want [...]
+    "autowiredEnabled": { "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which t [...]
+    "stitchClient": { "kind": "property", "displayName": "Stitch Client", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.stitch.client.StitchClient", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.stitch.StitchConfiguration", "configurationField": "configuration", "description": "Set a custom StitchClient that implements org.apache.cam [...]
+    "token": { "kind": "property", "displayName": "Token", "group": "security", "label": "security", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.stitch.StitchConfiguration", "configurationField": "configuration", "description": "Stitch access token for the Stitch Import API" }
+  },
+  "properties": {
+    "tableName": { "kind": "path", "displayName": "Table Name", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.stitch.StitchConfiguration", "configurationField": "configuration", "description": "The name of the destination table the data is being pushed to. Table names must be unique in each destination schema, or loading issu [...]
+    "keyNames": { "kind": "parameter", "displayName": "Key Names", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.stitch.StitchConfiguration", "configurationField": "configuration", "description": "A collection of comma separated strings representing the Primary Key fields in the source table. Stitch use these Primary  [...]
+    "lazyStartProducer": { "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during sta [...]
+    "region": { "kind": "parameter", "displayName": "Region", "group": "producer", "label": "producer", "required": false, "type": "object", "javaType": "org.apache.camel.component.stitch.client.StitchRegion", "enum": [ "NORTH_AMERICA", "EUROPE" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "europe", "configurationClass": "org.apache.camel.component.stitch.StitchConfiguration", "configurationField": "configuration", "description": "Stitch account region, e. [...]
+    "stitchSchema": { "kind": "parameter", "displayName": "Stitch Schema", "group": "producer", "label": "producer", "required": false, "type": "object", "javaType": "org.apache.camel.component.stitch.client.models.StitchSchema", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.stitch.StitchConfiguration", "configurationField": "configuration", "description": "A schema that describes the record(s)" },
+    "connectionProvider": { "kind": "parameter", "displayName": "Connection Provider", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "object", "javaType": "reactor.netty.resources.ConnectionProvider", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.stitch.StitchConfiguration", "configurationField": "configuration", "description": "ConnectionProvider contain configu [...]
+    "httpClient": { "kind": "parameter", "displayName": "Http Client", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "object", "javaType": "reactor.netty.http.client.HttpClient", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.stitch.StitchConfiguration", "configurationField": "configuration", "description": "Reactor Netty HttpClient, you can injected it if you wan [...]
+    "stitchClient": { "kind": "parameter", "displayName": "Stitch Client", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.stitch.client.StitchClient", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.stitch.StitchConfiguration", "configurationField": "configuration", "description": "Set a custom StitchClient that implements org.apache.ca [...]
+    "token": { "kind": "parameter", "displayName": "Token", "group": "security", "label": "security", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.stitch.StitchConfiguration", "configurationField": "configuration", "description": "Stitch access token for the Stitch Import API" }
+  }
+}
diff --git a/components/camel-stitch/src/main/docs/stitch-component.adoc b/components/camel-stitch/src/main/docs/stitch-component.adoc
new file mode 100644
index 0000000..7bfac91
--- /dev/null
+++ b/components/camel-stitch/src/main/docs/stitch-component.adoc
@@ -0,0 +1,312 @@
+[[stitch-component]]
+= Stitch Component
+:docTitle: Stitch
+:artifactId: camel-stitch
+:description: Stitch is a cloud ETL service that integrates various data sources into a central data warehouse through various integrations.
+:since: 3.8
+:supportLevel: Preview
+:component-header: Only producer is supported
+include::{cq-version}@camel-quarkus:ROOT:partial$reference/components/stitch.adoc[opts=optional]
+
+*Since Camel {since}*
+
+*{component-header}*
+
+Stitch is a cloud ETL service, developer-focused platform for rapidly moving and replicates data from more than 90
+applications and databases. It integrates various data sources into a central data warehouse. Stitch has integrations
+for many enterprise software data sources, and can receive data via WebHooks and an API (Stitch Import API) which
+Camel Stitch Component uses to produce the data to Stitch ETL.
+
+For more info, feel free to visit their website: https://www.stitchdata.com/
+
+Prerequisites
+
+You must have a valid Stitch account, you will need to enable Stitch Import API and generate a token for the
+integration, for more info, please find more info https://www.stitchdata.com/docs/developers/import-api/guides/quick-start[here].
+
+Maven users will need to add the following dependency to their `pom.xml`
+for this component:
+
+[source,xml]
+------------------------------------------------------------
+<dependency>
+    <groupId>org.apache.camel</groupId>
+    <artifactId>camel-stitch</artifactId>
+    <version>x.x.x</version>
+  <!-- use the same version as your Camel core version -->
+</dependency>
+------------------------------------------------------------
+
+
+== URI format
+
+[source,text]
+------------------------------
+stitch:[tableName]//[?options]
+------------------------------
+
+For example in order to produce data to Stitch from a custom processor:
+[source,java]
+--------------------------------------------------------------------------------
+from("direct:sendStitch")
+     .process(exchange -> {
+         final StitchMessage stitchMessage = StitchMessage.builder()
+               .withData("field_1", "stitchMessage2-1")
+               .build();
+
+         final StitchRequestBody stitchRequestBody = StitchRequestBody.builder()
+                .addMessage(stitchMessage)
+                .withSchema(StitchSchema.builder().addKeyword("field_1", "string").build())
+                .withTableName("table_1")
+                .withKeyNames(Collections.singleton("field_1"))
+                .build();
+
+                exchange.getMessage().setBody(stitchRequestBody);
+     })
+.to("stitch:table_1?token=RAW({{token}})");
+--------------------------------------------------------------------------------
+
+== URI Options
+// endpoint options: START
+The Stitch endpoint is configured using URI syntax:
+
+----
+stitch:tableName
+----
+
+with the following path and query parameters:
+
+=== Path Parameters (1 parameters):
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *tableName* | The name of the destination table the data is being pushed to. Table names must be unique in each destination schema, or loading issues will occur. Note: The number of characters in the table name should be within the destinations allowed limits or data will rejected. |  | String
+|===
+
+
+=== Query Parameters (8 parameters):
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *keyNames* (producer) | A collection of comma separated strings representing the Primary Key fields in the source table. Stitch use these Primary Keys to de-dupe data during loading If not provided, the table will be loaded in an append-only manner. |  | String
+| *lazyStartProducer* (producer) | Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and [...]
+| *region* (producer) | Stitch account region, e.g: europe. There are 2 enums and the value can be one of: NORTH_AMERICA, EUROPE | europe | StitchRegion
+| *stitchSchema* (producer) | *Autowired* A schema that describes the record(s) |  | StitchSchema
+| *connectionProvider* (producer) | *Autowired* ConnectionProvider contain configuration for the HttpClient like Maximum connection limit .. etc, you can inject this ConnectionProvider and the StitchClient will initialize HttpClient with this ConnectionProvider |  | ConnectionProvider
+| *httpClient* (producer) | *Autowired* Reactor Netty HttpClient, you can injected it if you want to have custom HttpClient |  | HttpClient
+| *stitchClient* (advanced) | *Autowired* Set a custom StitchClient that implements org.apache.camel.component.stitch.client.StitchClient interface |  | StitchClient
+| *token* (security) | *Required* Stitch access token for the Stitch Import API |  | String
+|===
+// endpoint options: END
+
+== Component Options
+// component options: START
+The Stitch component supports 10 options, which are listed below.
+
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *configuration* (producer) | The component configurations |  | StitchConfiguration
+| *keyNames* (producer) | A collection of comma separated strings representing the Primary Key fields in the source table. Stitch use these Primary Keys to de-dupe data during loading If not provided, the table will be loaded in an append-only manner. |  | String
+| *lazyStartProducer* (producer) | Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and [...]
+| *region* (producer) | Stitch account region, e.g: europe. There are 2 enums and the value can be one of: NORTH_AMERICA, EUROPE | europe | StitchRegion
+| *stitchSchema* (producer) | *Autowired* A schema that describes the record(s) |  | StitchSchema
+| *connectionProvider* (producer) | *Autowired* ConnectionProvider contain configuration for the HttpClient like Maximum connection limit .. etc, you can inject this ConnectionProvider and the StitchClient will initialize HttpClient with this ConnectionProvider |  | ConnectionProvider
+| *httpClient* (producer) | *Autowired* Reactor Netty HttpClient, you can injected it if you want to have custom HttpClient |  | HttpClient
+| *autowiredEnabled* (advanced) | Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc. | true | boolean
+| *stitchClient* (advanced) | *Autowired* Set a custom StitchClient that implements org.apache.camel.component.stitch.client.StitchClient interface |  | StitchClient
+| *token* (security) | *Required* Stitch access token for the Stitch Import API |  | String
+|===
+// component options: END
+
+
+== Async Producer
+
+This component implements the async Consumer and producer.
+
+This allows camel route to consume and produce events asynchronously without blocking any threads.
+
+== Usage
+
+=== Message headers evaluated by the component producer
+Before sending a message to Stitch component you can configure the following headers.
+
+[width="100%",cols="10%,10%,10%,70%",options="header",]
+|=======================================================================
+|Header |Variable Name |Type |Description
+
+|`CamelStitchTableName`| `StitchConstants.TABLE_NAME`|`String`| The name of the destination table the data is being pushed to. Table names must be unique in each destination schema, or loading issues will occur. Note: The number of characters in the table name should be within the destinations allowed limits or data will rejected.
+|`CamelStitchSchema`| `StitchConstants.SCHEMA`|`StitchSchema`| The schema that describes the Stitch message of type `org.apache.camel.component.stitch.client.models.StitchSchema`
+|`CamelStitchKeyNames`| `StitchConstants.KEY_NAMES`|`Collection<String>`| A collection of strings representing the Primary Key fields in the source table. Stitch use these Primary Keys to de-dupe data during loading If not provided, the table will be loaded in an append-only manner.
+|=======================================================================
+
+=== Message headers set by the component producer
+After the message is sent to Stitch, the following headers are available
+
+[width="100%",cols="10%,10%,10%,70%",options="header",]
+|=======================================================================
+|Header |Variable Name |Type |Description
+
+|`CamelStitchCode`| `StitchConstants.CODE`|`Integer`| HTTP Status code that is returned from Stitch Import HTTP API.
+|`CamelStitchHeaders`| `StitchConstants.HEADERS`|`Map`| HTTP headers that are returned from Stitch Import HTTP API.
+|`CamelStitchStatus`| `StitchConstants.STATUS`|`String`| The status message that Stitch returns after sending the data through Stitch Import API.
+|=======================================================================
+
+=== Message body type
+Currently, the component supports the following types for the body message on the producer side when producing a message to Stitch component:
+
+* `org.apache.camel.component.stitch.client.models.StitchRequestBody`: This represents this Stitch https://www.stitchdata.com/docs/developers/import-api/api#batch-data--arguments[JSON Message]. However,
+`StitchRequestBody` includes a type safe builder that helps on building the request body. Please note that, `tableName`, `keyNames` and `schema` options are no longer required if you send the
+data with `StitchRequestBody`, if you still set these options, they override whatever being set in message body `StitchRequestBody`.
+
+* `org.apache.camel.component.stitch.client.models.StitchMessage`: This represents https://www.stitchdata.com/docs/developers/import-api/api#message-object[this Stitch message structure].
+If you choose to send your message as `StitchMessage`, *you will need* to add `tableName`, `keyNames` and `schema` options to either the Exchange headers or through the endpoint options.
+
+* `Map`: You can also send the data as `Map`, the data structure must follow this https://www.stitchdata.com/docs/developers/import-api/api#batch-data--arguments[JSON Message] structure which is similar to
+`StitchRequestBody` but with drawback losing on all the type safety builder that is included with `StitchRequestBody`.
+
+* `Iterable`: You can send multiple Stitch messages that are aggregated by Camel or aggregated through custom processor. These aggregated messages
+can be type of `StitchMessage`, `StitchRequestBody` or `Map` but this Map here is similar to `StitchMessage`.
+
+
+
+=== Examples
+
+Here are list of examples of data that can be proceeded to Stitch:
+
+===== Input body type `org.apache.camel.component.stitch.client.models.StitchRequestBody`:
+[source,java]
+--------------------------------------------------------------------------------
+from("direct:sendStitch")
+     .process(exchange -> {
+         final StitchMessage stitchMessage = StitchMessage.builder()
+               .withData("field_1", "stitchMessage2-1")
+               .build();
+
+         final StitchRequestBody stitchRequestBody = StitchRequestBody.builder()
+                .addMessage(stitchMessage)
+                .withSchema(StitchSchema.builder().addKeyword("field_1", "string").build())
+                .withTableName("table_1")
+                .withKeyNames(Collections.singleton("field_1"))
+                .build();
+
+                exchange.getMessage().setBody(stitchRequestBody);
+     })
+.to("stitch:table_1?token=RAW({{token}})");
+--------------------------------------------------------------------------------
+
+
+===== Input body type `org.apache.camel.component.stitch.client.models.StitchMessage`:
+[source,java]
+--------------------------------------------------------------------------------
+from("direct:sendStitch")
+     .process(exchange -> {
+         exchange.getMessage().setHeader(StitchConstants.SCHEMA, StitchSchema.builder().addKeyword("field_1", "string").build());
+         exchange.getMessage().setHeader(StitchConstants.KEY_NAMES, Collections.singleton("field_1"));
+         exchange.getMessage().setHeader(StitchConstants.TABLE_NAME, "table_1");
+
+         final StitchMessage stitchMessage = StitchMessage.builder()
+               .withData("field_1", "stitchMessage2-1")
+               .build();
+
+                exchange.getMessage().setBody(stitchMessage);
+     })
+.to("stitch:table_1?token=RAW({{token}})");
+--------------------------------------------------------------------------------
+
+
+===== Input body type `Map`:
+[source,java]
+--------------------------------------------------------------------------------
+from("direct:sendStitch")
+     .process(exchange -> {
+        final Map<String, Object> properties = new LinkedHashMap<>();
+        properties.put("id", Collections.singletonMap("type", "integer"));
+        properties.put("name", Collections.singletonMap("type", "string"));
+        properties.put("age", Collections.singletonMap("type", "integer"));
+        properties.put("has_magic", Collections.singletonMap("type", "boolean"));
+
+        final Map<String, Object> data = new LinkedHashMap<>();
+        data.put(StitchRequestBody.TABLE_NAME, "my_table");
+        data.put(StitchRequestBody.SCHEMA, Collections.singletonMap("properties", properties));
+        data.put(StitchRequestBody.MESSAGES,
+                Collections.singletonList(Collections.singletonMap("data", Collections.singletonMap("id", 2))));
+        data.put(StitchRequestBody.KEY_NAMES, Collections.singletonList("test_key"));
+
+        exchange.getMessage().setBody(data);
+     })
+.to("stitch:table_1?token=RAW({{token}})");
+--------------------------------------------------------------------------------
+
+
+
+===== Input body type `Iterable`:
+[source,java]
+--------------------------------------------------------------------------------
+from("direct:sendStitch")
+     .process(exchange -> {
+         exchange.getMessage().setHeader(StitchConstants.SCHEMA, StitchSchema.builder().addKeyword("field_1", "string").build());
+         exchange.getMessage().setHeader(StitchConstants.KEY_NAMES, Collections.singleton("field_1"));
+         exchange.getMessage().setHeader(StitchConstants.TABLE_NAME, "table_1");
+
+        final StitchMessage stitchMessage1 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage1")
+                .build();
+
+        final StitchMessage stitchMessage2 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage2-1")
+                .build();
+
+        final StitchRequestBody stitchMessage2RequestBody = StitchRequestBody.builder()
+                .addMessage(stitchMessage2)
+                .withSchema(StitchSchema.builder().addKeyword("field_1", "integer").build())
+                .withTableName("table_1")
+                .withKeyNames(Collections.singleton("field_1"))
+                .build();
+
+        final Map<String, Object> stitchMessage3 = new LinkedHashMap<>();
+        stitchMessage3.put(StitchMessage.DATA, Collections.singletonMap("field_1", "stitchMessage3"));
+
+        final StitchMessage stitchMessage4 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage4")
+                .build();
+
+        final Exchange stitchMessage4Exchange = new DefaultExchange(context);
+        stitchMessage4Exchange.getMessage().setBody(stitchMessage4);
+
+        final StitchMessage stitchMessage5 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage5")
+                .build();
+
+        final Message stitchMessage5Message = new DefaultExchange(context).getMessage();
+        stitchMessage5Message.setBody(stitchMessage5);
+
+        final List<Object> inputMessages = new LinkedList<>();
+        inputMessages.add(stitchMessage1);
+        inputMessages.add(stitchMessage2RequestBody);
+        inputMessages.add(stitchMessage3);
+        inputMessages.add(stitchMessage4Exchange);
+        inputMessages.add(stitchMessage5Message);
+
+        exchange.getMessage().setBody(inputMessages);
+     })
+.to("stitch:table_1?token=RAW({{token}})");
+--------------------------------------------------------------------------------
+
+=== Development Notes (Important)
+When developing on this component, you will need to obtain your Stitch token in order to run the integration tests. In addition to the mocked unit tests
+you *will need to run the integration tests with every change you make*
+To run the integration tests, on this component directory, run the following maven command:
+----
+mvn verify -PfullTests -Dtoken=stitchToken
+----
+Whereby `token` is your Stitch token that is generated for Stitch Import API integration.
+
+
+include::camel-spring-boot::page$stitch-starter.adoc[]
diff --git a/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/StitchComponent.java b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/StitchComponent.java
new file mode 100644
index 0000000..4bbbc4f
--- /dev/null
+++ b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/StitchComponent.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch;
+
+import java.util.Map;
+
+import org.apache.camel.Endpoint;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.annotations.Component;
+import org.apache.camel.support.DefaultComponent;
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * Stitch component
+ */
+@Component("stitch")
+public class StitchComponent extends DefaultComponent {
+
+    @Metadata
+    private StitchConfiguration configuration = new StitchConfiguration();
+
+    public StitchComponent() {
+    }
+
+    @Override
+    protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception {
+
+        final StitchConfiguration configuration
+                = this.configuration != null ? this.configuration.copy() : new StitchConfiguration();
+
+        configuration.setTableName(remaining);
+
+        final StitchEndpoint endpoint = new StitchEndpoint(uri, this, configuration);
+        setProperties(endpoint, parameters);
+
+        validateConfigurations(configuration);
+
+        return endpoint;
+    }
+
+    /**
+     * The component configurations
+     */
+    public StitchConfiguration getConfiguration() {
+        return configuration;
+    }
+
+    public void setConfiguration(StitchConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+    private void validateConfigurations(final StitchConfiguration configuration) {
+        if (ObjectHelper.isEmpty(configuration.getToken())) {
+            throw new IllegalArgumentException("Token must be configured in 'token' option.");
+        }
+    }
+}
diff --git a/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/StitchConfiguration.java b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/StitchConfiguration.java
new file mode 100644
index 0000000..802a07c
--- /dev/null
+++ b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/StitchConfiguration.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch;
+
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.component.stitch.client.StitchClient;
+import org.apache.camel.component.stitch.client.StitchRegion;
+import org.apache.camel.component.stitch.client.models.StitchSchema;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriParams;
+import org.apache.camel.spi.UriPath;
+import reactor.netty.http.client.HttpClient;
+import reactor.netty.resources.ConnectionProvider;
+
+@UriParams
+public class StitchConfiguration implements Cloneable {
+
+    @UriPath
+    private String tableName;
+    @UriParam(label = "security", secret = true)
+    @Metadata(required = true)
+    private String token;
+    @UriParam(label = "producer", defaultValue = "europe")
+    private StitchRegion region = StitchRegion.EUROPE;
+    @UriParam(label = "producer")
+    @Metadata(autowired = true)
+    private StitchSchema stitchSchema;
+    @UriParam(label = "producer")
+    private String keyNames;
+    @UriParam(label = "producer,advanced")
+    @Metadata(autowired = true)
+    private HttpClient httpClient;
+    @UriParam(label = "producer,advanced")
+    @Metadata(autowired = true)
+    private ConnectionProvider connectionProvider;
+    @UriParam(label = "advanced")
+    @Metadata(autowired = true)
+    private StitchClient stitchClient;
+
+    /**
+     * The name of the destination table the data is being pushed to. Table names must be unique in each destination
+     * schema, or loading issues will occur.
+     *
+     * Note: The number of characters in the table name should be within the destination’s allowed limits or data will
+     * rejected.
+     */
+    public String getTableName() {
+        return tableName;
+    }
+
+    public void setTableName(String tableName) {
+        this.tableName = tableName;
+    }
+
+    /**
+     * Stitch access token for the Stitch Import API
+     */
+    public String getToken() {
+        return token;
+    }
+
+    public void setToken(String token) {
+        this.token = token;
+    }
+
+    /**
+     * Stitch account region, e.g: europe
+     */
+    public StitchRegion getRegion() {
+        return region;
+    }
+
+    public void setRegion(StitchRegion region) {
+        this.region = region;
+    }
+
+    /**
+     * A schema that describes the record(s)
+     */
+    public StitchSchema getStitchSchema() {
+        return stitchSchema;
+    }
+
+    public void setStitchSchema(StitchSchema stitchSchema) {
+        this.stitchSchema = stitchSchema;
+    }
+
+    /**
+     * A collection of comma separated strings representing the Primary Key fields in the source table. Stitch use these
+     * Primary Keys to de-dupe data during loading If not provided, the table will be loaded in an append-only manner.
+     */
+    public String getKeyNames() {
+        return keyNames;
+    }
+
+    public void setKeyNames(String keyNames) {
+        this.keyNames = keyNames;
+    }
+
+    /**
+     * Reactor Netty HttpClient, you can injected it if you want to have custom HttpClient
+     */
+    public HttpClient getHttpClient() {
+        return httpClient;
+    }
+
+    public void setHttpClient(HttpClient httpClient) {
+        this.httpClient = httpClient;
+    }
+
+    /**
+     * ConnectionProvider contain configuration for the HttpClient like Maximum connection limit .. etc, you can inject
+     * this ConnectionProvider and the StitchClient will initialize HttpClient with this ConnectionProvider
+     */
+    public ConnectionProvider getConnectionProvider() {
+        return connectionProvider;
+    }
+
+    public void setConnectionProvider(ConnectionProvider connectionProvider) {
+        this.connectionProvider = connectionProvider;
+    }
+
+    /**
+     * Set a custom StitchClient that implements org.apache.camel.component.stitch.client.StitchClient interface
+     */
+    public StitchClient getStitchClient() {
+        return stitchClient;
+    }
+
+    public void setStitchClient(StitchClient stitchClient) {
+        this.stitchClient = stitchClient;
+    }
+
+    // *************************************************
+    //
+    // *************************************************
+
+    public StitchConfiguration copy() {
+        try {
+            return (StitchConfiguration) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new RuntimeCamelException(e);
+        }
+    }
+}
diff --git a/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/StitchConstants.java b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/StitchConstants.java
new file mode 100644
index 0000000..f2150ef
--- /dev/null
+++ b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/StitchConstants.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch;
+
+public final class StitchConstants {
+    private static final String HEADER_PREFIX = "CamelStitch";
+    // headers evaluated by producer
+    public static final String TABLE_NAME = HEADER_PREFIX + "TableName";
+    public static final String SCHEMA = HEADER_PREFIX + "Schema";
+    public static final String KEY_NAMES = HEADER_PREFIX + "KeyNames";
+    // headers set by producer
+    public static final String CODE = HEADER_PREFIX + "Code";
+    public static final String HEADERS = HEADER_PREFIX + "Headers";
+    public static final String STATUS = HEADER_PREFIX + "Status";
+
+    private StitchConstants() {
+    }
+}
diff --git a/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/StitchEndpoint.java b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/StitchEndpoint.java
new file mode 100644
index 0000000..525d89a
--- /dev/null
+++ b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/StitchEndpoint.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch;
+
+import org.apache.camel.Category;
+import org.apache.camel.Component;
+import org.apache.camel.Consumer;
+import org.apache.camel.Processor;
+import org.apache.camel.Producer;
+import org.apache.camel.component.stitch.client.StitchClient;
+import org.apache.camel.component.stitch.client.StitchClientBuilder;
+import org.apache.camel.spi.UriEndpoint;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.support.DefaultEndpoint;
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * Stitch is a cloud ETL service that integrates various data sources into a central data warehouse through various
+ * integrations.
+ */
+@UriEndpoint(firstVersion = "3.8.0", scheme = "stitch", title = "Stitch",
+             syntax = "stitch:tableName", producerOnly = true, category = {
+                     Category.CLOUD, Category.API, Category.COMPUTE, Category.BIGDATA })
+public class StitchEndpoint extends DefaultEndpoint {
+
+    @UriParam
+    private StitchConfiguration configuration = new StitchConfiguration();
+
+    private StitchClient stitchClient;
+
+    public StitchEndpoint() {
+    }
+
+    public StitchEndpoint(final String uri, final Component component, final StitchConfiguration configuration) {
+        super(uri, component);
+        this.configuration = configuration;
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        super.doStart();
+
+        // since HttpClient.create() will create a pooled a connection when is called, hence placed in doStart
+        if (stitchClient == null) {
+            stitchClient
+                    = configuration.getStitchClient() != null ? configuration.getStitchClient() : createClient(configuration);
+        }
+    }
+
+    @Override
+    public Producer createProducer() throws Exception {
+        return new StitchProducer(this);
+    }
+
+    @Override
+    public Consumer createConsumer(Processor processor) throws Exception {
+        throw new UnsupportedOperationException("Stitch component does not support consumer operations.");
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        // only close if it is created by the endpoint
+        if (ObjectHelper.isEmpty(configuration.getStitchClient()) && stitchClient != null) {
+            stitchClient.close();
+        }
+
+        super.doStop();
+    }
+
+    /**
+     * The component configurations
+     */
+    public StitchConfiguration getConfiguration() {
+        return configuration;
+    }
+
+    public void setConfiguration(StitchConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+    public StitchClient getStitchClient() {
+        return stitchClient;
+    }
+
+    public void setStitchClient(StitchClient stitchClient) {
+        this.stitchClient = stitchClient;
+    }
+
+    private StitchClient createClient(final StitchConfiguration configuration) {
+        return StitchClientBuilder.builder()
+                .withRegion(configuration.getRegion())
+                .withToken(configuration.getToken())
+                .withHttpClient(configuration.getHttpClient())
+                .withConnectionProvider(configuration.getConnectionProvider())
+                .build();
+    }
+}
diff --git a/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/StitchProducer.java b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/StitchProducer.java
new file mode 100644
index 0000000..4e0266d
--- /dev/null
+++ b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/StitchProducer.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch;
+
+import org.apache.camel.AsyncCallback;
+import org.apache.camel.Endpoint;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.component.stitch.client.models.StitchResponse;
+import org.apache.camel.component.stitch.operations.StitchProducerOperations;
+import org.apache.camel.support.DefaultAsyncProducer;
+
+public class StitchProducer extends DefaultAsyncProducer {
+
+    private StitchProducerOperations operations;
+
+    public StitchProducer(final Endpoint endpoint) {
+        super(endpoint);
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        super.doStart();
+
+        operations = new StitchProducerOperations(getEndpoint().getStitchClient(), getConfiguration());
+    }
+
+    @Override
+    public boolean process(Exchange exchange, AsyncCallback callback) {
+        try {
+            return operations.sendEvents(exchange.getMessage(),
+                    response -> setDataOnExchange(response, exchange), callback);
+        } catch (Exception e) {
+            exchange.setException(e);
+            callback.done(true);
+            return true;
+        }
+
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        super.doStop();
+    }
+
+    @Override
+    public StitchEndpoint getEndpoint() {
+        return (StitchEndpoint) super.getEndpoint();
+    }
+
+    public StitchConfiguration getConfiguration() {
+        return getEndpoint().getConfiguration();
+    }
+
+    private void setDataOnExchange(final StitchResponse response, final Exchange exchange) {
+        final Message message = exchange.getIn();
+
+        // set response message
+        message.setBody(response.getMessage());
+        // set headers
+        message.setHeader(StitchConstants.CODE, response.getHttpStatusCode());
+        message.setHeader(StitchConstants.STATUS, response.getStatus());
+        message.setHeader(StitchConstants.HEADERS, response.getHeaders());
+    }
+}
diff --git a/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/JsonUtils.java b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/JsonUtils.java
new file mode 100644
index 0000000..b24749c
--- /dev/null
+++ b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/JsonUtils.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client;
+
+import java.util.Map;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public final class JsonUtils {
+
+    static final ObjectMapper MAPPER = new ObjectMapper();
+
+    private JsonUtils() {
+    }
+
+    public static String convertMapToJson(final Map<String, Object> inputMap) {
+        try {
+            return MAPPER.writeValueAsString(inputMap);
+        } catch (JsonProcessingException exception) {
+            throw new RuntimeException("Error occurred writing data map to JSON.", exception);
+        }
+    }
+
+    public static Map<String, Object> convertJsonToMap(final String jsonString) {
+        try {
+            return MAPPER.readValue(jsonString, new TypeReference<Map<String, Object>>() {
+            });
+        } catch (JsonProcessingException exception) {
+            throw new RuntimeException("Error occurred writing JSON to Map.", exception);
+        }
+    }
+}
diff --git a/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/StitchClient.java b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/StitchClient.java
new file mode 100644
index 0000000..1e46552
--- /dev/null
+++ b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/StitchClient.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client;
+
+import java.io.Closeable;
+
+import org.apache.camel.component.stitch.client.models.StitchRequestBody;
+import org.apache.camel.component.stitch.client.models.StitchResponse;
+import reactor.core.publisher.Mono;
+
+public interface StitchClient extends Closeable {
+    /**
+     * Create a batch
+     *
+     * Resource URL: /v2/import/batch
+     *
+     * Pushes a record or multiple records for a specified table to Stitch. Each request to this endpoint may only
+     * contain data for a single table. When data for a table is pushed for the first time, Stitch will create the table
+     * in the destination in the specified integration schema.
+     *
+     * How data is loaded during subsequent pushes depends on: 1. The loading behavior types used by the destination.
+     * Stitch supports Upsert and Append-Only loading. 2. Whether the key_names property specifies Primary Key fields.
+     * If Primary Keys aren’t specified, data will be loaded using Append-Only loading.
+     *
+     * @param requestBody the required arguments as StitchRequestBody
+     */
+    Mono<StitchResponse> batch(StitchRequestBody requestBody);
+}
diff --git a/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/StitchClientBuilder.java b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/StitchClientBuilder.java
new file mode 100644
index 0000000..3c5dad0
--- /dev/null
+++ b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/StitchClientBuilder.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client;
+
+import org.apache.camel.util.ObjectHelper;
+import reactor.netty.http.client.HttpClient;
+import reactor.netty.resources.ConnectionProvider;
+
+public final class StitchClientBuilder {
+    private HttpClient httpClient;
+    private String token;
+    private ConnectionProvider connectionProvider;
+    private StitchRegion region;
+
+    private StitchClientBuilder() {
+    }
+
+    public static StitchClientBuilder builder() {
+        return new StitchClientBuilder();
+    }
+
+    public StitchClientBuilder withHttpClient(HttpClient httpClient) {
+        if (ObjectHelper.isNotEmpty(httpClient)) {
+            this.httpClient = httpClient;
+        }
+        return this;
+    }
+
+    public StitchClientBuilder withToken(String token) {
+        if (ObjectHelper.isNotEmpty(token)) {
+            this.token = token;
+        }
+        return this;
+    }
+
+    public StitchClientBuilder withConnectionProvider(ConnectionProvider connectionProvider) {
+        if (ObjectHelper.isNotEmpty(connectionProvider)) {
+            this.connectionProvider = connectionProvider;
+        }
+        return this;
+    }
+
+    public StitchClientBuilder withRegion(StitchRegion region) {
+        if (ObjectHelper.isNotEmpty(region)) {
+            this.region = region;
+        }
+        return this;
+    }
+
+    public StitchClientImpl build() {
+        // let's check if we have all the required properties
+        if (ObjectHelper.isEmpty(token) || ObjectHelper.isEmpty(region)) {
+            throw new IllegalArgumentException("Token or Region cannot be empty!");
+        }
+
+        // if we supplied the HttpClient
+        if (ObjectHelper.isNotEmpty(httpClient)) {
+            return new StitchClientImpl(httpClient, getBaseUrl(region), token);
+        }
+
+        // if we supplied the ConnectionProvider
+        if (ObjectHelper.isNotEmpty(connectionProvider)) {
+            return new StitchClientImpl(HttpClient.create(connectionProvider), getBaseUrl(region), token);
+        }
+
+        // otherwise create using the default options
+        return new StitchClientImpl(HttpClient.create(), getBaseUrl(region), token);
+    }
+
+    private String getBaseUrl(final StitchRegion stitchRegion) {
+        return "https://" + stitchRegion.getUrl();
+    }
+}
diff --git a/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/StitchClientImpl.java b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/StitchClientImpl.java
new file mode 100644
index 0000000..194b24c
--- /dev/null
+++ b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/StitchClientImpl.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpHeaderValues;
+import io.netty.handler.codec.http.HttpHeaders;
+import org.apache.camel.component.stitch.client.models.StitchError;
+import org.apache.camel.component.stitch.client.models.StitchException;
+import org.apache.camel.component.stitch.client.models.StitchRequestBody;
+import org.apache.camel.component.stitch.client.models.StitchResponse;
+import reactor.core.publisher.Mono;
+import reactor.netty.ByteBufMono;
+import reactor.netty.http.client.HttpClient;
+import reactor.netty.http.client.HttpClientResponse;
+
+public class StitchClientImpl implements StitchClient {
+
+    private static final String BATCH_API_RESOURCE_URL = "/v2/import/batch";
+
+    private final HttpClient httpClient;
+    private final String baseUrl;
+    private final String token;
+
+    StitchClientImpl(HttpClient httpClient, String baseUrl, String token) {
+        this.httpClient = httpClient;
+        this.baseUrl = baseUrl;
+        this.token = token;
+    }
+
+    @Override
+    public Mono<StitchResponse> batch(final StitchRequestBody requestBody) {
+        return sendBatch(convertMapToByteBuf(requestBody.toMap()));
+    }
+
+    @Override
+    public void close() {
+        // dispose the connection provider
+        httpClient.configuration().connectionProvider().disposeLater().block();
+    }
+
+    private Mono<StitchResponse> sendBatch(final ByteBufMono bodyAsByte) {
+        return httpClient
+                .headers(applyHeaders())
+                .baseUrl(baseUrl)
+                .post()
+                .uri(BATCH_API_RESOURCE_URL)
+                .send(bodyAsByte)
+                .responseSingle(this::generateStitchResponse);
+    }
+
+    private Consumer<? super HttpHeaders> applyHeaders() {
+        return h -> {
+            h.set(HttpHeaderNames.AUTHORIZATION, "Bearer " + token);
+            h.set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
+        };
+    }
+
+    private ByteBufMono convertMapToByteBuf(final Map<String, Object> bodyAsMap) {
+        return ByteBufMono.fromString(Mono.just(JsonUtils.convertMapToJson(bodyAsMap)));
+    }
+
+    private Mono<StitchResponse> generateStitchResponse(final HttpClientResponse clientResponse, final ByteBufMono bufMono) {
+        final int code = getStatusCode(clientResponse);
+        final Map<String, Object> headers = getHeaders(clientResponse);
+
+        return bufMono.asString()
+                .map(JsonUtils::convertJsonToMap)
+                // if we get a problem parsing the json, we just return empty map for graceful handling
+                .onErrorReturn(Collections.emptyMap())
+                .handle((bodyMap, sink) -> {
+                    final StitchResponse stitchResponse = getStitchResponse(code, headers, bodyMap);
+
+                    if (code >= 300) {
+                        // anything above 300, we treat it as error
+                        final StitchError stitchError = getStitchError(bodyMap);
+
+                        sink.error(new StitchException(stitchResponse, stitchError));
+                    } else {
+                        sink.next(stitchResponse);
+                    }
+                });
+    }
+
+    private int getStatusCode(final HttpClientResponse clientResponse) {
+        return clientResponse.status().code();
+    }
+
+    private Map<String, Object> getHeaders(final HttpClientResponse clientResponse) {
+        return clientResponse.responseHeaders()
+                .entries()
+                .stream()
+                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+    }
+
+    private StitchResponse getStitchResponse(
+            final int code, final Map<String, Object> headers, final Map<String, Object> bodyMap) {
+        final String status = (String) bodyMap.getOrDefault("status", "");
+        final String message = (String) bodyMap.getOrDefault("message", "");
+
+        return new StitchResponse(code, headers, status, message);
+    }
+
+    @SuppressWarnings("unchecked")
+    private StitchError getStitchError(final Map<String, Object> bodyMap) {
+        final String error = (String) bodyMap.getOrDefault("error", "");
+        final List<Object> errors = (List<Object>) bodyMap.getOrDefault("errors", Collections.emptyList());
+
+        return new StitchError(error, errors);
+    }
+}
diff --git a/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/StitchRegion.java b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/StitchRegion.java
new file mode 100644
index 0000000..3b90f79
--- /dev/null
+++ b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/StitchRegion.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client;
+
+public enum StitchRegion {
+    NORTH_AMERICA("api.stitchdata.com"),
+    EUROPE("api.eu-central-1.stitchdata.com");
+
+    private final String url;
+
+    StitchRegion(final String url) {
+        this.url = url;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public static StitchRegion fromString(final String regionAsText) {
+        for (StitchRegion stitchRegion : StitchRegion.values()) {
+            if (regionAsText.equalsIgnoreCase(stitchRegion.toString())) {
+                return stitchRegion;
+            }
+        }
+        throw new IllegalArgumentException(String.format("'%s' does not exist!", regionAsText));
+    }
+}
diff --git a/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchError.java b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchError.java
new file mode 100644
index 0000000..fc77391
--- /dev/null
+++ b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchError.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client.models;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This represents the Error Object: https://www.stitchdata.com/docs/developers/import-api/api#error-object
+ */
+public class StitchError implements StitchModel {
+    // property names
+    public static final String ERROR = "error";
+    public static final String ERRORS = "errors";
+
+    private final String error;
+    private final List<Object> errors;
+
+    public StitchError(String error, List<Object> errors) {
+        this.error = error;
+        this.errors = errors;
+    }
+
+    public String getError() {
+        return error;
+    }
+
+    public List<Object> getErrors() {
+        return errors;
+    }
+
+    @Override
+    public Map<String, Object> toMap() {
+        final Map<String, Object> resultAsMap = new LinkedHashMap<>();
+
+        resultAsMap.put(ERROR, error);
+        resultAsMap.put(ERRORS, errors);
+
+        return resultAsMap;
+    }
+}
diff --git a/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchException.java b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchException.java
new file mode 100644
index 0000000..458bfd2
--- /dev/null
+++ b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchException.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client.models;
+
+public class StitchException extends RuntimeException {
+    private final StitchResponse response;
+    private final StitchError error;
+
+    public StitchException(StitchResponse response, Throwable cause) {
+        super(response.getMessage(), cause);
+        this.response = response;
+        this.error = null;
+    }
+
+    public StitchException(StitchResponse response) {
+        super(response.getMessage());
+        this.response = response;
+        this.error = null;
+    }
+
+    public StitchException(StitchResponse response, StitchError error, Throwable cause) {
+        super(error.getError(), cause);
+        this.response = response;
+        this.error = error;
+    }
+
+    public StitchException(StitchResponse response, StitchError error) {
+        super(error.getError());
+        this.response = response;
+        this.error = error;
+    }
+
+    public StitchResponse getResponse() {
+        return response;
+    }
+
+    public StitchError getError() {
+        return error;
+    }
+}
diff --git a/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchMessage.java b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchMessage.java
new file mode 100644
index 0000000..71085bf
--- /dev/null
+++ b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchMessage.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client.models;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * This represents the schema here: https://www.stitchdata.com/docs/developers/import-api/api#message-object
+ */
+public final class StitchMessage implements StitchModel {
+    // property names
+    public static final String ACTION = "action";
+    public static final String SEQUENCE = "sequence";
+    public static final String DATA = "data";
+
+    private static final Action DEFAULT_ACTION = Action.UPSERT;
+    private static final long DEFAULT_SEQUENCE = System.currentTimeMillis();
+
+    public enum Action {
+        UPSERT
+    };
+
+    private final Action action;
+    private final long sequence;
+    private final Map<String, Object> data;
+
+    private StitchMessage(Action action, long sequence, Map<String, Object> data) {
+        this.action = action;
+        this.sequence = sequence;
+        this.data = data;
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    @SuppressWarnings("unchecked")
+    public static Builder fromMap(final Map<String, Object> data) {
+        final Action action = Action.valueOf(data.getOrDefault(ACTION, DEFAULT_ACTION.name()).toString().toUpperCase());
+        final long sequence = ObjectHelper.cast(Long.class, data.getOrDefault(SEQUENCE, DEFAULT_SEQUENCE));
+        final Map<String, Object> inputData = ObjectHelper.cast(Map.class, data.getOrDefault(DATA, Collections.emptyMap()));
+
+        return new Builder()
+                .withAction(action)
+                .withData(inputData)
+                .withSequence(sequence);
+    }
+
+    public Action getAction() {
+        return action;
+    }
+
+    public long getSequence() {
+        return sequence;
+    }
+
+    public Map<String, Object> getData() {
+        return data;
+    }
+
+    @Override
+    public Map<String, Object> toMap() {
+        final Map<String, Object> resultAsMap = new LinkedHashMap<>();
+
+        resultAsMap.put(ACTION, action.name().toLowerCase());
+        resultAsMap.put(SEQUENCE, sequence);
+        resultAsMap.put(DATA, data);
+
+        return resultAsMap;
+    }
+
+    public static final class Builder {
+        private Action action;
+        private Long sequence;
+        private Map<String, Object> data = new LinkedHashMap<>();
+
+        private Builder() {
+        }
+
+        /**
+         * This will always be upsert.
+         *
+         * Default: upsert
+         *
+         * @param action
+         */
+        public Builder withAction(final Action action) {
+            if (ObjectHelper.isNotEmpty(action)) {
+                this.action = action;
+            }
+            return this;
+        }
+
+        /**
+         * An integer that tells the Import API the order in which data points in the request body should be considered
+         * for loading. This data will be stored in the destination table in the _sdc_sequence column. In other Stitch
+         * integrations, Stitch uses a Unix epoch (in milliseconds) as the value for this property. Note: This value
+         * cannot exceed the maximum of 9223372036854775807.
+         *
+         * Default: System.currentTimeMillis();
+         *
+         * @param sequence
+         */
+        public Builder withSequence(final long sequence) {
+            if (ObjectHelper.isNotEmpty(sequence)) {
+                this.sequence = sequence;
+            }
+            return this;
+        }
+
+        /**
+         * The record to be upserted into a table. The record data must conform to the JSON schema contained in the
+         * request’s Schema object.
+         *
+         * @param data
+         */
+        public Builder withData(final Map<String, Object> data) {
+            if (ObjectHelper.isNotEmpty(data)) {
+                this.data.putAll(data);
+            }
+            return this;
+        }
+
+        public Builder withData(final String key, final Object data) {
+            if (ObjectHelper.isNotEmpty(key)) {
+                this.data.put(key, data);
+            }
+            return this;
+        }
+
+        public StitchMessage build() {
+            if (ObjectHelper.isEmpty(data)) {
+                throw new IllegalArgumentException("Data cannot be empty.");
+            }
+
+            if (ObjectHelper.isEmpty(action)) {
+                action = DEFAULT_ACTION;
+            }
+
+            if (ObjectHelper.isEmpty(sequence)) {
+                sequence = DEFAULT_SEQUENCE;
+            }
+
+            return new StitchMessage(action, sequence, data);
+        }
+    }
+}
diff --git a/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchModel.java b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchModel.java
new file mode 100644
index 0000000..e34890c
--- /dev/null
+++ b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchModel.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client.models;
+
+import java.util.Map;
+
+public interface StitchModel {
+
+    /**
+     * Create a map representation of the model which is essentially the JSON representation of the model.
+     *
+     * @return {@link Map<String,Object>}
+     */
+    Map<String, Object> toMap();
+}
diff --git a/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchRequestBody.java b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchRequestBody.java
new file mode 100644
index 0000000..5d0dcb7
--- /dev/null
+++ b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchRequestBody.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client.models;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * This represents the schema here: https://www.stitchdata.com/docs/developers/import-api/api#batch-data--arguments
+ */
+public final class StitchRequestBody implements StitchModel {
+    // property names
+    public static final String TABLE_NAME = "table_name";
+    public static final String SCHEMA = "schema";
+    public static final String MESSAGES = "messages";
+    public static final String KEY_NAMES = "key_names";
+
+    private final String tableName;
+    private final StitchSchema schema;
+    private final Collection<StitchMessage> messages;
+    private final Collection<String> keyNames;
+
+    private StitchRequestBody(String tableName, StitchSchema schema, Collection<StitchMessage> messages,
+                              Collection<String> keyNames) {
+        this.tableName = tableName;
+        this.schema = schema;
+        this.messages = messages;
+        this.keyNames = keyNames;
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static Builder fromStitchRequestBody(final StitchRequestBody body) {
+        return new Builder()
+                .withSchema(body.getSchema())
+                .withTableName(body.getTableName())
+                .withKeyNames(body.getKeyNames())
+                .addMessages(body.getMessages());
+
+    }
+
+    @SuppressWarnings("unchecked")
+    public static Builder fromMap(final Map<String, Object> data) {
+        final String tableName = ObjectHelper.cast(String.class, data.get(TABLE_NAME));
+        final StitchSchema schema = StitchSchema.builder()
+                .addKeywords(ObjectHelper.cast(Map.class, data.getOrDefault(SCHEMA, Collections.emptyMap())))
+                .build();
+        final Collection<StitchMessage> messages = (Collection<StitchMessage>) ObjectHelper
+                .cast(Collection.class, data.getOrDefault(MESSAGES, Collections.emptyList()))
+                .stream()
+                .filter(ObjectHelper::isNotEmpty)
+                .map(message -> StitchMessage
+                        .fromMap(ObjectHelper.cast(Map.class, message))
+                        .build())
+                .collect(Collectors.toList());
+        final Collection<String> keyNames = ObjectHelper.cast(Collection.class, data.get(KEY_NAMES));
+
+        return new Builder()
+                .withSchema(schema)
+                .withTableName(tableName)
+                .withKeyNames(keyNames)
+                .withSchema(schema)
+                .addMessages(messages);
+    }
+
+    public String getTableName() {
+        return tableName;
+    }
+
+    public StitchSchema getSchema() {
+        return schema;
+    }
+
+    public Collection<StitchMessage> getMessages() {
+        return messages;
+    }
+
+    public Collection<String> getKeyNames() {
+        return keyNames;
+    }
+
+    @Override
+    public Map<String, Object> toMap() {
+        final Map<String, Object> resultAsMap = new LinkedHashMap<>();
+
+        resultAsMap.put(TABLE_NAME, tableName);
+        resultAsMap.put(SCHEMA, schema.toMap());
+        resultAsMap.put(MESSAGES, messages.stream().map(StitchMessage::toMap).collect(Collectors.toList()));
+        resultAsMap.put(KEY_NAMES, keyNames);
+
+        return resultAsMap;
+    }
+
+    public static final class Builder {
+        private String tableName;
+        private StitchSchema schema;
+        private Collection<StitchMessage> messages = new LinkedList<>();
+        private Collection<String> keyNames = new LinkedHashSet<>();
+
+        private Builder() {
+        }
+
+        /**
+         * The name of the destination table the data is being pushed to. Table names must be unique in each destination
+         * schema, or loading issues will occur. REQUIRED
+         *
+         * @param tableName
+         */
+        public Builder withTableName(final String tableName) {
+            if (ObjectHelper.isNotEmpty(tableName)) {
+                this.tableName = tableName;
+            }
+            return this;
+        }
+
+        /**
+         * A Schema object containing the JSON schema describing the record(s) in the Message object’s data property.
+         * Records must conform to this schema or an error will be returned when the request is sent. REQUIRED
+         *
+         * @param schema
+         */
+        public Builder withSchema(final StitchSchema schema) {
+            if (ObjectHelper.isNotEmpty(schema)) {
+                this.schema = schema;
+            }
+            return this;
+        }
+
+        /**
+         * An array of Message objects, each representing a record to be upserted into the table. REQUIRED
+         *
+         * @param messages
+         */
+        public Builder addMessages(final Collection<StitchMessage> messages) {
+            if (ObjectHelper.isNotEmpty(messages)) {
+                this.messages.addAll(messages);
+            }
+            return this;
+        }
+
+        public Builder addMessage(final StitchMessage messages) {
+            if (ObjectHelper.isNotEmpty(messages)) {
+                this.messages.add(messages);
+            }
+            return this;
+        }
+
+        /**
+         * An array of strings representing the Primary Key fields in the source table. Stitch use these Primary Keys to
+         * de-dupe data during loading. If not provided, the table will be loaded in an append-only manner. Note: If
+         * included, a value must be provided. However, it may be an empty list to indicate that the source table
+         * doesn’t have a Primary Key. If fields are provided, they must adhere to the following: 1. Each field in the
+         * list must be the name of a top-level property defined in the Schema object. Primary Key fields cannot be
+         * contained in an object or an array. 2. Fields in the list may not be null in the source. 3. If a field is a
+         * string, its value must be less than 256 characters. OPTIONAL
+         *
+         * @param keyNames
+         */
+        public Builder withKeyNames(final String... keyNames) {
+            if (ObjectHelper.isNotEmpty(keyNames)) {
+                this.keyNames.addAll(Arrays.stream(keyNames).collect(Collectors.toSet()));
+            }
+            return this;
+        }
+
+        public Builder withKeyNames(final Collection<String> keyNames) {
+            if (ObjectHelper.isNotEmpty(keyNames)) {
+                this.keyNames.addAll(keyNames);
+            }
+            return this;
+        }
+
+        public StitchRequestBody build() {
+            if (ObjectHelper.isEmpty(tableName) || ObjectHelper.isEmpty(schema) || ObjectHelper.isEmpty(messages)) {
+                throw new IllegalArgumentException(
+                        "One of the required arguments 'tableName', 'schema' or 'messages' is not set.");
+            }
+            return new StitchRequestBody(tableName, schema, messages, keyNames);
+        }
+    }
+}
diff --git a/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchResponse.java b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchResponse.java
new file mode 100644
index 0000000..da2639f
--- /dev/null
+++ b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchResponse.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client.models;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class StitchResponse implements StitchModel {
+    // property names
+    public static final String CODE = "code";
+    public static final String HEADERS = "headers";
+    public static final String STATUS = "status";
+    public static final String MESSAGE = "message";
+
+    private final int httpStatusCode;
+    private final Map<String, Object> headers;
+    private final String status;
+    private final String message;
+
+    public StitchResponse(int httpStatusCode, Map<String, Object> headers, String status, String message) {
+        this.httpStatusCode = httpStatusCode;
+        this.headers = headers;
+        this.status = status;
+        this.message = message;
+    }
+
+    public int getHttpStatusCode() {
+        return httpStatusCode;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public Map<String, Object> getHeaders() {
+        return headers;
+    }
+
+    /**
+     * Returns true if the request succeeded.
+     *
+     * @return
+     *         <ul>
+     *         <li>true - if the request succeeded</li>
+     *         <li>false - if the request failed</li>
+     *         </ul>
+     */
+    public boolean isOk() {
+        return httpStatusCode < 300;
+    }
+
+    public String toString() {
+        final String result
+                = "HTTP Status Code: " + httpStatusCode + ", Response Status: " + status + ", Response Message: " + message;
+
+        return result;
+    }
+
+    @Override
+    public Map<String, Object> toMap() {
+        final Map<String, Object> resultAsMap = new LinkedHashMap<>();
+
+        resultAsMap.put(CODE, httpStatusCode);
+        resultAsMap.put(HEADERS, headers);
+        resultAsMap.put(STATUS, status);
+        resultAsMap.put(MESSAGE, message);
+
+        return resultAsMap;
+    }
+}
diff --git a/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchSchema.java b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchSchema.java
new file mode 100644
index 0000000..e4a488c
--- /dev/null
+++ b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/client/models/StitchSchema.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client.models;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * This represents the schema here: https://www.stitchdata.com/docs/developers/import-api/api#schema-object
+ */
+public final class StitchSchema implements StitchModel {
+
+    private final Map<String, Object> keywords;
+
+    private StitchSchema(Map<String, Object> keywords) {
+        this.keywords = keywords;
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public Map<String, Object> getKeywords() {
+        return keywords;
+    }
+
+    @Override
+    public Map<String, Object> toMap() {
+        return getKeywords();
+    }
+
+    public static final class Builder {
+        private Map<String, Object> keywords = new LinkedHashMap<>();
+
+        private Builder() {
+        }
+
+        /**
+         * The JSON schema that records in the data property must conform to. Refer to the JSON schema docs for more
+         * info about JSON schemas.
+         */
+        public Builder addKeywords(final Map<String, Object> keywords) {
+            if (ObjectHelper.isNotEmpty(keywords)) {
+                this.keywords.putAll(keywords);
+            }
+            return this;
+        }
+
+        public Builder addKeyword(final String key, final Object value) {
+            if (ObjectHelper.isNotEmpty(key)) {
+                this.keywords.put(key, value);
+            }
+            return this;
+        }
+
+        public StitchSchema build() {
+            return new StitchSchema(keywords);
+        }
+    }
+}
diff --git a/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/operations/StitchProducerOperations.java b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/operations/StitchProducerOperations.java
new file mode 100644
index 0000000..1c2ab63
--- /dev/null
+++ b/components/camel-stitch/src/main/java/org/apache/camel/component/stitch/operations/StitchProducerOperations.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.operations;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+import org.apache.camel.AsyncCallback;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.component.stitch.StitchConfiguration;
+import org.apache.camel.component.stitch.StitchConstants;
+import org.apache.camel.component.stitch.client.StitchClient;
+import org.apache.camel.component.stitch.client.models.StitchMessage;
+import org.apache.camel.component.stitch.client.models.StitchRequestBody;
+import org.apache.camel.component.stitch.client.models.StitchResponse;
+import org.apache.camel.component.stitch.client.models.StitchSchema;
+import org.apache.camel.util.ObjectHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import reactor.core.publisher.Mono;
+
+public class StitchProducerOperations {
+
+    private static final Logger LOG = LoggerFactory.getLogger(StitchProducerOperations.class);
+
+    private final StitchClient client;
+    private final StitchConfiguration configuration;
+
+    public StitchProducerOperations(StitchClient client, StitchConfiguration configuration) {
+        ObjectHelper.notNull(client, "client");
+        ObjectHelper.notNull(configuration, "configuration");
+
+        this.client = client;
+        this.configuration = configuration;
+    }
+
+    public boolean sendEvents(
+            final Message inMessage, final Consumer<StitchResponse> resultCallback, final AsyncCallback callback) {
+        sendAsyncEvents(inMessage)
+                .subscribe(resultCallback, error -> {
+                    // error but we continue
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("Error processing async exchange with error: {}", error.getMessage());
+                    }
+                    inMessage.getExchange().setException(error);
+                    callback.done(false);
+                }, () -> {
+                    // we are done from everything, so mark it as sync done
+                    LOG.trace("All events with exchange have been sent successfully.");
+                    callback.done(false);
+                });
+
+        return false;
+    }
+
+    private Mono<StitchResponse> sendAsyncEvents(final Message inMessage) {
+        return client.batch(createStitchRequestBody(inMessage));
+    }
+
+    @SuppressWarnings("unchecked")
+    // visible for testing
+    public StitchRequestBody createStitchRequestBody(final Message inMessage) {
+        if (inMessage.getBody() instanceof StitchRequestBody) {
+            return createStitchRequestBodyFromStitchRequestBody(inMessage.getBody(StitchRequestBody.class), inMessage);
+        }
+
+        if (inMessage.getBody() instanceof StitchMessage) {
+            return createStitchRequestBodyFromStitchMessages(Collections.singletonList(inMessage.getBody(StitchMessage.class)),
+                    inMessage);
+        }
+
+        if (inMessage.getBody() instanceof Iterable) {
+            return createStitchRequestBodyFromIterable(inMessage.getBody(Iterable.class), inMessage);
+        }
+
+        if (inMessage.getBody() instanceof Map) {
+            return createStitchRecordFromMap(inMessage.getBody(Map.class), inMessage);
+        }
+
+        throw new IllegalArgumentException("Message body data `" + inMessage.getBody() + "` type is not supported");
+    }
+
+    private StitchRequestBody createStitchRequestBodyFromStitchRequestBody(
+            final StitchRequestBody requestBody, final Message message) {
+        return createStitchRecordFromBuilder(StitchRequestBody.fromStitchRequestBody(requestBody), message);
+    }
+
+    private StitchRequestBody createStitchRequestBodyFromStitchMessages(
+            final Collection<StitchMessage> stitchMessages, final Message message) {
+        final StitchRequestBody.Builder builder = StitchRequestBody.builder()
+                .addMessages(stitchMessages);
+
+        return createStitchRecordFromBuilder(builder, message);
+    }
+
+    @SuppressWarnings("unchecked")
+    private StitchRequestBody createStitchRequestBodyFromIterable(final Iterable<Object> inputData, final Message message) {
+        final Collection<StitchMessage> stitchMessages = new LinkedList<>();
+
+        inputData.forEach(data -> {
+            if (data instanceof StitchMessage) {
+                stitchMessages.add((StitchMessage) data);
+            } else if (data instanceof Map) {
+                stitchMessages.add(StitchMessage.fromMap(ObjectHelper.cast(Map.class, data)).build());
+            } else if (data instanceof StitchRequestBody) {
+                stitchMessages.addAll(((StitchRequestBody) data).getMessages());
+            } else if (data instanceof Message) {
+                final Message camelNestedMessage = (Message) data;
+                // set all the headers from parent message
+                camelNestedMessage.setHeaders(message.getHeaders());
+                stitchMessages.addAll(createStitchRequestBody(camelNestedMessage).getMessages());
+            } else if (data instanceof Exchange) {
+                final Message camelNestedMessage = ((Exchange) data).getMessage();
+                // set all the headers from parent message
+                camelNestedMessage.setHeaders(message.getHeaders());
+                stitchMessages.addAll(createStitchRequestBody(camelNestedMessage).getMessages());
+            } else {
+                throw new IllegalArgumentException("Input data `" + data + "` type is not supported");
+            }
+        });
+
+        return createStitchRequestBodyFromStitchMessages(stitchMessages, message);
+    }
+
+    private StitchRequestBody createStitchRecordFromMap(final Map<String, Object> data, final Message message) {
+        return createStitchRecordFromBuilder(StitchRequestBody.fromMap(data), message);
+    }
+
+    private StitchRequestBody createStitchRecordFromBuilder(final StitchRequestBody.Builder builder, final Message message) {
+        return builder
+                .withSchema(getStitchSchema(message))
+                .withTableName(getTableName(message))
+                .withKeyNames(getKeyNames(message))
+                .build();
+    }
+
+    private String getTableName(final Message message) {
+        return getOption(message, StitchConstants.TABLE_NAME, configuration::getTableName, String.class);
+    }
+
+    @SuppressWarnings("unchecked")
+    private StitchSchema getStitchSchema(final Message message) {
+        // if we have header set, then we try first that
+        if (ObjectHelper.isNotEmpty(message.getHeader(StitchConstants.SCHEMA))) {
+            if (message.getHeader(StitchConstants.SCHEMA) instanceof StitchSchema) {
+                return message.getHeader(StitchConstants.SCHEMA, StitchSchema.class);
+            }
+            if (message.getHeader(StitchConstants.SCHEMA) instanceof Map) {
+                return StitchSchema.builder().addKeywords(message.getHeader(StitchConstants.SCHEMA, Map.class)).build();
+            }
+        }
+        // otherwise we just get whatever we have in the config
+        return configuration.getStitchSchema();
+    }
+
+    private Collection<String> getKeyNames(final Message message) {
+        final String keys = getOption(message, StitchConstants.KEY_NAMES, configuration::getKeyNames, String.class);
+
+        if (ObjectHelper.isNotEmpty(keys)) {
+            return Arrays.asList(keys.split(",").clone());
+        }
+
+        return Collections.emptyList();
+    }
+
+    private <R> R getOption(
+            final Message message, final String headerName, final Supplier<R> fallbackFn, final Class<R> type) {
+        // we first try to look if our value in exchange otherwise fallback to fallbackFn which could be either a function or constant
+        return ObjectHelper.isEmpty(message) || ObjectHelper.isEmpty(getObjectFromHeaders(message, headerName, type))
+                ? fallbackFn.get()
+                : getObjectFromHeaders(message, headerName, type);
+    }
+
+    private <T> T getObjectFromHeaders(final Message message, final String headerName, final Class<T> classType) {
+        return message.getHeader(headerName, classType);
+    }
+}
diff --git a/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/StitchComponentTest.java b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/StitchComponentTest.java
new file mode 100644
index 0000000..b057023
--- /dev/null
+++ b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/StitchComponentTest.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch;
+
+import org.apache.camel.ResolveEndpointFailedException;
+import org.apache.camel.component.stitch.client.StitchRegion;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class StitchComponentTest extends CamelTestSupport {
+
+    @Test
+    void testNormalProperties() {
+        final String uri = "stitch:my_table?token=mytoken&region=north_america";
+
+        final StitchEndpoint endpoint = context.getEndpoint(uri, StitchEndpoint.class);
+
+        assertEquals("my_table", endpoint.getConfiguration().getTableName());
+        assertEquals("mytoken", endpoint.getConfiguration().getToken());
+        assertEquals(StitchRegion.NORTH_AMERICA, endpoint.getConfiguration().getRegion());
+    }
+
+    @Test
+    void testIfNotAllProperties() {
+        final String uri2 = "stitch:my_table?region=north_america";
+
+        assertThrows(ResolveEndpointFailedException.class, () -> context.getEndpoint(uri2, StitchEndpoint.class));
+    }
+}
diff --git a/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/StitchProducerIT.java b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/StitchProducerIT.java
new file mode 100644
index 0000000..c7d02ee
--- /dev/null
+++ b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/StitchProducerIT.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.component.stitch.client.models.StitchMessage;
+import org.apache.camel.component.stitch.client.models.StitchRequestBody;
+import org.apache.camel.component.stitch.client.models.StitchSchema;
+import org.apache.camel.support.DefaultExchange;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+class StitchProducerIT extends CamelTestSupport {
+
+    @EndpointInject
+    private ProducerTemplate template;
+
+    @EndpointInject("mock:result")
+    private MockEndpoint result;
+
+    @Test
+    void testIfSendSingleMessage() throws InterruptedException {
+        result.reset();
+
+        result.expectedMessageCount(1);
+        result.setAssertPeriod(1000);
+
+        template.send("direct:sendStitch", exchange -> {
+            exchange.getMessage().setHeader(StitchConstants.SCHEMA,
+                    StitchSchema.builder().addKeyword("field_1", "string").build());
+            exchange.getMessage().setHeader(StitchConstants.KEY_NAMES, "field_1");
+
+            exchange.getMessage().setBody(StitchMessage.builder()
+                    .withData("field_1", "data")
+                    .build());
+        });
+
+        result.assertIsSatisfied();
+
+        final Message message = result.getExchanges().get(0).getMessage();
+
+        assertNotNull(message.getHeader(StitchConstants.CODE));
+        assertNotNull(message.getHeader(StitchConstants.STATUS));
+        assertNotNull(message.getHeader(StitchConstants.HEADERS));
+        assertNotNull(message.getBody());
+    }
+
+    @Test
+    void testIfSendMultipleMessages() throws InterruptedException {
+        result.reset();
+
+        result.expectedMessageCount(1);
+        result.setAssertPeriod(1000);
+
+        final StitchMessage stitchMessage1 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage1")
+                .build();
+
+        final StitchMessage stitchMessage2 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage2-1")
+                .build();
+
+        final StitchRequestBody stitchMessage2RequestBody = StitchRequestBody.builder()
+                .addMessage(stitchMessage2)
+                .withSchema(StitchSchema.builder().addKeyword("field_1", "integer").build())
+                .withTableName("table_1")
+                .withKeyNames(Collections.singleton("field_1"))
+                .build();
+
+        final Map<String, Object> stitchMessage3 = new LinkedHashMap<>();
+        stitchMessage3.put(StitchMessage.DATA, Collections.singletonMap("field_1", "stitchMessage3"));
+
+        final StitchMessage stitchMessage4 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage4")
+                .build();
+
+        final Exchange stitchMessage4Exchange = new DefaultExchange(context);
+        stitchMessage4Exchange.getMessage().setBody(stitchMessage4);
+
+        final StitchMessage stitchMessage5 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage5")
+                .build();
+
+        final Message stitchMessage5Message = new DefaultExchange(context).getMessage();
+        stitchMessage5Message.setBody(stitchMessage5);
+
+        final List<Object> inputMessages = new LinkedList<>();
+        inputMessages.add(stitchMessage1);
+        inputMessages.add(stitchMessage2RequestBody);
+        inputMessages.add(stitchMessage3);
+        inputMessages.add(stitchMessage4Exchange);
+        inputMessages.add(stitchMessage5Message);
+
+        template.send("direct:sendStitch", exchange -> {
+            exchange.getMessage().setHeader(StitchConstants.SCHEMA,
+                    StitchSchema.builder().addKeyword("field_1", "string").build());
+            exchange.getMessage().setHeader(StitchConstants.KEY_NAMES, "field_1");
+
+            exchange.getMessage().setBody(inputMessages);
+        });
+
+        result.assertIsSatisfied();
+
+        final Message message = result.getExchanges().get(0).getMessage();
+
+        assertNotNull(message.getHeader(StitchConstants.CODE));
+        assertNotNull(message.getHeader(StitchConstants.STATUS));
+        assertNotNull(message.getHeader(StitchConstants.HEADERS));
+        assertNotNull(message.getBody());
+    }
+
+    @Test
+    void testIfSendWithError() throws InterruptedException {
+        result.reset();
+        result.setAssertPeriod(1000);
+
+        final Exchange exchange = new DefaultExchange(context);
+        exchange.getMessage().setHeader(StitchConstants.SCHEMA,
+                StitchSchema.builder().addKeyword("field_1", "string").build());
+        exchange.getMessage().setHeader(StitchConstants.KEY_NAMES, "field_1");
+
+        exchange.getMessage().setBody(StitchMessage.builder()
+                .withData("field_2", "data")
+                .build());
+
+        template.send("direct:sendStitch", exchange);
+
+        result.assertIsSatisfied();
+
+        assertNotNull(exchange.getException());
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:sendStitch")
+                        .to("stitch:table_1?token=RAW({{token}})")
+                        .to(result);
+            }
+        };
+    }
+}
diff --git a/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/StitchProducerTest.java b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/StitchProducerTest.java
new file mode 100644
index 0000000..821425a
--- /dev/null
+++ b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/StitchProducerTest.java
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.component.stitch.client.StitchClient;
+import org.apache.camel.component.stitch.client.models.StitchException;
+import org.apache.camel.component.stitch.client.models.StitchMessage;
+import org.apache.camel.component.stitch.client.models.StitchRequestBody;
+import org.apache.camel.component.stitch.client.models.StitchResponse;
+import org.apache.camel.component.stitch.client.models.StitchSchema;
+import org.apache.camel.support.DefaultExchange;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import reactor.core.publisher.Mono;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+class StitchProducerTest extends CamelTestSupport {
+
+    @EndpointInject
+    private ProducerTemplate template;
+
+    @EndpointInject("mock:result")
+    private MockEndpoint result;
+
+    @Test
+    void testIfSendSingleMessage() throws InterruptedException {
+        result.reset();
+
+        result.expectedMessageCount(1);
+        result.expectedHeaderReceived(StitchConstants.CODE, 200);
+        result.expectedHeaderReceived(StitchConstants.STATUS, "OK");
+        result.expectedBodiesReceived("All good!");
+        result.setAssertPeriod(1000);
+
+        template.send("direct:sendStitch", exchange -> {
+            exchange.getMessage().setHeader(StitchConstants.SCHEMA,
+                    StitchSchema.builder().addKeyword("field_1", "string").build());
+            exchange.getMessage().setHeader(StitchConstants.KEY_NAMES, "field_1");
+
+            exchange.getMessage().setBody(StitchMessage.builder()
+                    .withData("field_1", "data")
+                    .build());
+        });
+
+        result.assertIsSatisfied();
+
+        final Message message = result.getExchanges().get(0).getMessage();
+
+        assertEquals(message.getHeader(StitchConstants.HEADERS, Map.class).get("header-1"), "test");
+    }
+
+    @Test
+    void testIfSendMultipleMessages() throws InterruptedException {
+        result.reset();
+
+        result.expectedMessageCount(1);
+        result.expectedHeaderReceived(StitchConstants.CODE, 200);
+        result.expectedHeaderReceived(StitchConstants.STATUS, "OK");
+        result.expectedBodiesReceived("All good!");
+        result.setAssertPeriod(1000);
+
+        final StitchMessage stitchMessage1 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage1")
+                .build();
+
+        final StitchMessage stitchMessage2 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage2-1")
+                .build();
+
+        final StitchRequestBody stitchMessage2RequestBody = StitchRequestBody.builder()
+                .addMessage(stitchMessage2)
+                .withSchema(StitchSchema.builder().addKeyword("field_1", "integer").build())
+                .withTableName("table_1")
+                .withKeyNames(Collections.singleton("field_1"))
+                .build();
+
+        final Map<String, Object> stitchMessage3 = new LinkedHashMap<>();
+        stitchMessage3.put(StitchMessage.DATA, Collections.singletonMap("field_1", "stitchMessage3"));
+
+        final StitchMessage stitchMessage4 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage4")
+                .build();
+
+        final Exchange stitchMessage4Exchange = new DefaultExchange(context);
+        stitchMessage4Exchange.getMessage().setBody(stitchMessage4);
+
+        final StitchMessage stitchMessage5 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage5")
+                .build();
+
+        final Message stitchMessage5Message = new DefaultExchange(context).getMessage();
+        stitchMessage5Message.setBody(stitchMessage5);
+
+        final List<Object> inputMessages = new LinkedList<>();
+        inputMessages.add(stitchMessage1);
+        inputMessages.add(stitchMessage2RequestBody);
+        inputMessages.add(stitchMessage3);
+        inputMessages.add(stitchMessage4Exchange);
+        inputMessages.add(stitchMessage5Message);
+
+        template.send("direct:sendStitch", exchange -> {
+            exchange.getMessage().setHeader(StitchConstants.SCHEMA,
+                    StitchSchema.builder().addKeyword("field_1", "string").build());
+            exchange.getMessage().setHeader(StitchConstants.KEY_NAMES, "field_1");
+
+            exchange.getMessage().setBody(inputMessages);
+        });
+
+        result.assertIsSatisfied();
+
+        final Message message = result.getExchanges().get(0).getMessage();
+
+        assertEquals(message.getHeader(StitchConstants.HEADERS, Map.class).get("header-1"), "test");
+    }
+
+    @Test
+    void testIfSendSingleMessageWithError() throws InterruptedException {
+        result.reset();
+        result.setAssertPeriod(1000);
+
+        final Exchange exchange = new DefaultExchange(context);
+        exchange.getMessage().setHeader(StitchConstants.SCHEMA,
+                StitchSchema.builder().addKeyword("field_1", "string").build());
+        exchange.getMessage().setHeader(StitchConstants.KEY_NAMES, "field_1");
+
+        exchange.getMessage().setBody(StitchMessage.builder()
+                .withData("field_2", "data")
+                .build());
+
+        template.send("direct:sendErrorStitch", exchange);
+
+        result.assertIsSatisfied();
+
+        assertNotNull(exchange.getException());
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+        context.getRegistry().bind("client", new TestClient());
+        context.getRegistry().bind("errorClient", new TestErrorClient());
+        return context;
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:sendStitch")
+                        .to("stitch:table_1?stitchClient=#client&token=dummy")
+                        .to(result);
+
+                from("direct:sendErrorStitch")
+                        .to("stitch:table_1?stitchClient=#errorClient&token=dummy")
+                        .to(result);
+            }
+        };
+    }
+
+    static class TestClient implements StitchClient {
+
+        @Override
+        public Mono<StitchResponse> batch(StitchRequestBody requestBody) {
+            final StitchResponse response = new StitchResponse(
+                    200,
+                    Collections.singletonMap("header-1", "test"),
+                    "OK",
+                    "All good!");
+
+            return Mono.just(response);
+        }
+
+        @Override
+        public void close() throws IOException {
+            // noop
+        }
+    }
+
+    static class TestErrorClient implements StitchClient {
+
+        @Override
+        public Mono<StitchResponse> batch(StitchRequestBody requestBody) {
+            final StitchResponse response = new StitchResponse(
+                    400,
+                    Collections.singletonMap("header-1", "test"),
+                    "Error",
+                    "Not good!");
+
+            final StitchException exception = new StitchException(response);
+
+            return Mono.error(exception);
+        }
+
+        @Override
+        public void close() throws IOException {
+            // noop
+        }
+    }
+}
diff --git a/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/StitchTestUtils.java b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/StitchTestUtils.java
new file mode 100644
index 0000000..5a83397
--- /dev/null
+++ b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/StitchTestUtils.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch;
+
+import java.util.Properties;
+
+public final class StitchTestUtils {
+
+    private StitchTestUtils() {
+    }
+
+    public static Properties loadStitchTokenFromJvmEnv() throws Exception {
+        final Properties properties = new Properties();
+        if (System.getProperty("token") == null) {
+            throw new Exception(
+                    "Make sure to supply Stitch token, e.g:  mvn verify -PfullTests -Dtoken=mytoken");
+        }
+        properties.setProperty("token", System.getProperty("token"));
+
+        return properties;
+    }
+
+}
diff --git a/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/StitchClientBuilderTest.java b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/StitchClientBuilderTest.java
new file mode 100644
index 0000000..7e5f6fe
--- /dev/null
+++ b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/StitchClientBuilderTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class StitchClientBuilderTest {
+
+    @Test
+    void shouldNotCreateClientIfTokenOrRegionIsMissing() {
+        assertThrows(IllegalArgumentException.class, () -> {
+            StitchClientBuilder.builder().build();
+        });
+
+        assertThrows(IllegalArgumentException.class, () -> {
+            StitchClientBuilder.builder().withToken("test").build();
+        });
+
+        assertThrows(IllegalArgumentException.class, () -> {
+            StitchClientBuilder.builder().withRegion(StitchRegion.EUROPE).build();
+        });
+    }
+
+    @Test
+    void shouldCreateTheClient() {
+        final StitchClient stitchClient = StitchClientBuilder
+                .builder()
+                .withToken("testtoken")
+                .withRegion(StitchRegion.EUROPE)
+                .build();
+
+        assertNotNull(stitchClient);
+    }
+}
diff --git a/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/StitchClientImplIT.java b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/StitchClientImplIT.java
new file mode 100644
index 0000000..0b1c119
--- /dev/null
+++ b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/StitchClientImplIT.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.camel.component.stitch.StitchTestUtils;
+import org.apache.camel.component.stitch.client.models.StitchException;
+import org.apache.camel.component.stitch.client.models.StitchMessage;
+import org.apache.camel.component.stitch.client.models.StitchRequestBody;
+import org.apache.camel.component.stitch.client.models.StitchResponse;
+import org.apache.camel.component.stitch.client.models.StitchSchema;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class StitchClientImplIT {
+
+    @Test
+    void testIfCreateBatch() throws Exception {
+        final StitchClient client = StitchClientBuilder.builder()
+                .withRegion(StitchRegion.EUROPE)
+                .withToken(StitchTestUtils.loadStitchTokenFromJvmEnv().getProperty("token"))
+                .build();
+
+        final StitchMessage message1 = StitchMessage.builder()
+                .withData("id", 2)
+                .withData("name", "Jake")
+                .withData("age", 6)
+                .withData("has_magic", true)
+                .withSequence(1565881320)
+                .build();
+
+        final Map<String, Object> properties = new LinkedHashMap<>();
+        properties.put("id", Collections.singletonMap("type", "integer"));
+        properties.put("name", Collections.singletonMap("type", "string"));
+        properties.put("age", Collections.singletonMap("type", "integer"));
+        properties.put("has_magic", Collections.singletonMap("type", "boolean"));
+
+        final StitchSchema schema = StitchSchema.builder()
+                .addKeyword("properties", properties)
+                .build();
+
+        final StitchRequestBody body = StitchRequestBody.builder()
+                .withTableName("test")
+                .addMessage(message1)
+                .withSchema(schema)
+                .withKeyNames("id")
+                .build();
+
+        StitchResponse response = client.batch(body).block();
+
+        assertNotNull(response);
+        assertEquals(201, response.getHttpStatusCode());
+    }
+
+    @Test
+    void testIfThrowError() throws Exception {
+        final StitchClient client = StitchClientBuilder.builder()
+                .withRegion(StitchRegion.EUROPE)
+                .withToken(StitchTestUtils.loadStitchTokenFromJvmEnv().getProperty("token"))
+                .build();
+
+        final StitchMessage message1 = StitchMessage.builder()
+                .withData("id", 2)
+                .withData("name", "Jake")
+                .withData("age", 6)
+                .withData("has_magic", true)
+                .withSequence(1565881320)
+                .build();
+
+        final Map<String, Object> properties = new LinkedHashMap<>();
+        properties.put("id", Collections.singletonMap("type", "integer"));
+        properties.put("name", Collections.singletonMap("type", "string"));
+        properties.put("age", Collections.singletonMap("type", "integer"));
+        properties.put("has_magic", Collections.singletonMap("type", "integerr"));
+
+        final StitchSchema schema = StitchSchema.builder()
+                .addKeyword("properties", properties)
+                .build();
+
+        final StitchRequestBody body = StitchRequestBody.builder()
+                .withTableName("test")
+                .addMessage(message1)
+                .withSchema(schema)
+                .withKeyNames("id")
+                .build();
+
+        assertThrows(StitchException.class, () -> client.batch(body).block());
+    }
+}
diff --git a/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/StitchRegionTest.java b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/StitchRegionTest.java
new file mode 100644
index 0000000..f5f0978
--- /dev/null
+++ b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/StitchRegionTest.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class StitchRegionTest {
+
+    @Test
+    void testCreateRegion() {
+        assertEquals(StitchRegion.EUROPE, StitchRegion.fromString("europe"));
+        assertEquals(StitchRegion.EUROPE, StitchRegion.fromString("eurOpe"));
+        assertEquals(StitchRegion.NORTH_AMERICA, StitchRegion.fromString("north_america"));
+        assertEquals(StitchRegion.NORTH_AMERICA, StitchRegion.fromString("NORTH_AMERICA"));
+    }
+}
diff --git a/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/models/StitchMessageTest.java b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/models/StitchMessageTest.java
new file mode 100644
index 0000000..8300243
--- /dev/null
+++ b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/models/StitchMessageTest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client.models;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.camel.component.stitch.client.JsonUtils;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class StitchMessageTest {
+
+    @Test
+    void testIfCreateNormalMessage() {
+        final StitchMessage message = StitchMessage.builder()
+                .withData("id", 2)
+                .withData("name", "Jake")
+                .withData("age", 6)
+                .withData("has_magic", true)
+                .withData("modified_at", "2020-01-13T21:25:03+0000")
+                .withSequence(1565881320)
+                .withAction(StitchMessage.Action.UPSERT)
+                .build();
+
+        final String messageAsJson = JsonUtils.convertMapToJson(message.toMap());
+
+        assertEquals("{\"action\":\"upsert\",\"sequence\":1565881320,\"data\":"
+                     + "{\"id\":2,\"name\":\"Jake\",\"age\":6,\"has_magic\":true,\"modified_at\":\"2020-01-13T21:25:03+0000\"}}",
+                messageAsJson);
+    }
+
+    @Test
+    void testIfNotCreateFromMapFromInvalidData() {
+        final Map<String, Object> data = new LinkedHashMap<>();
+        data.put(StitchMessage.ACTION, "upsert");
+        data.put(StitchMessage.DATA, 1);
+        data.put(StitchMessage.SEQUENCE, 1122544L);
+
+        assertThrows(IllegalArgumentException.class, () -> StitchMessage
+                .fromMap(data)
+                .build());
+    }
+
+    @Test
+    void testIfCreateMap() {
+        final Map<String, Object> data = new LinkedHashMap<>();
+        data.put("id", 2);
+        data.put("name", "Jake");
+
+        final Map<String, Object> message = new LinkedHashMap<>();
+        message.put(StitchMessage.SEQUENCE, 123456L);
+        message.put(StitchMessage.DATA, data);
+
+        final StitchMessage stitchMessage = StitchMessage
+                .fromMap(message)
+                .build();
+
+        assertEquals(StitchMessage.Action.UPSERT, stitchMessage.getAction());
+        assertEquals(data, stitchMessage.getData());
+        assertEquals(123456L, stitchMessage.getSequence());
+    }
+}
diff --git a/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/models/StitchRequestBodyTest.java b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/models/StitchRequestBodyTest.java
new file mode 100644
index 0000000..39a6e2d
--- /dev/null
+++ b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/models/StitchRequestBodyTest.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client.models;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class StitchRequestBodyTest {
+
+    @Test
+    public void testNormalRequestBodyToJson() throws JsonProcessingException {
+        final StitchMessage message1 = StitchMessage.builder()
+                .withData("id", 2)
+                .withData("name", "Jake")
+                .withData("age", 6)
+                .withData("has_magic", true)
+                .withData("modified_at", "2020-01-13T21:25:03+0000")
+                .withSequence(1565881320)
+                .build();
+
+        final StitchMessage message2 = StitchMessage.builder()
+                .withData("id", 3)
+                .withData("name", "Bubblegum")
+                .withData("age", 17)
+                .withData("has_magic", true)
+                .withData("modified_at", "2020-01-14T13:34:25+0000")
+                .withSequence(1565838645)
+                .build();
+
+        final Map<String, String> modifiedAtSchema = new LinkedHashMap<>();
+        modifiedAtSchema.put("type", "string");
+        modifiedAtSchema.put("format", "date-time");
+
+        final Map<String, Object> properties = new LinkedHashMap<>();
+        properties.put("id", Collections.singletonMap("type", "integer"));
+        properties.put("name", Collections.singletonMap("type", "string"));
+        properties.put("age", Collections.singletonMap("type", "integer"));
+        properties.put("has_magic", Collections.singletonMap("type", "boolean"));
+        properties.put("modified_at", modifiedAtSchema);
+
+        final StitchSchema schema = StitchSchema.builder()
+                .addKeyword("properties", properties)
+                .build();
+
+        final StitchRequestBody body = StitchRequestBody.builder()
+                .withTableName("customers")
+                .addMessage(message1)
+                .addMessage(message2)
+                .withSchema(schema)
+                .withKeyNames("id")
+                .build();
+
+        final String expectedJson
+                = "{\"table_name\":\"customers\",\"schema\":{\"properties\":{\"id\":{\"type\":\"integer\"},\"name\":{\"type\":\"string\"},\"age\":{\"type\":\"integer\"},\""
+                  + "has_magic\":{\"type\":\"boolean\"},\"modified_at\":{\"type\":\"string\",\"format\":\"date-time\"}}},\"messages\":[{\"action\":\"upsert\",\"sequence\":1565881320,\"data\":"
+                  + "{\"id\":2,\"name\":\"Jake\",\"age\":6,\"has_magic\":true,\"modified_at\":\"2020-01-13T21:25:03+0000\"}},{\"action\":\"upsert\",\"sequence\":1565838645,\"data\":{\"id\":3,"
+                  + "\"name\":\"Bubblegum\",\"age\":17,\"has_magic\":true,\"modified_at\":\"2020-01-14T13:34:25+0000\"}}],\"key_names\":[\"id\"]}";
+
+        assertEquals(expectedJson, new ObjectMapper().writeValueAsString(body.toMap()));
+    }
+
+    @Test
+    void testIfNotCreateRequestBodyFromInvalidMap() {
+        final Map<String, Object> data = new LinkedHashMap<>();
+        data.put(StitchRequestBody.TABLE_NAME, "table");
+        data.put(StitchRequestBody.SCHEMA, 1);
+        data.put(StitchRequestBody.MESSAGES, Collections.emptyList());
+        data.put(StitchRequestBody.KEY_NAMES, Collections.emptySet());
+
+        assertThrows(IllegalArgumentException.class, () -> StitchRequestBody.fromMap(data));
+
+        final Map<String, Object> data2 = new LinkedHashMap<>();
+        data2.put(StitchRequestBody.TABLE_NAME, "table");
+        data2.put(StitchRequestBody.SCHEMA, Collections.emptyMap());
+        data2.put(StitchRequestBody.MESSAGES, 12);
+        data2.put(StitchRequestBody.KEY_NAMES, Collections.emptySet());
+
+        assertThrows(IllegalArgumentException.class, () -> StitchRequestBody.fromMap(data2));
+    }
+
+    @Test
+    void testIfCreateRequestBodyFromMap() {
+        final Map<String, Object> properties = new LinkedHashMap<>();
+        properties.put("id", Collections.singletonMap("type", "integer"));
+        properties.put("name", Collections.singletonMap("type", "string"));
+        properties.put("age", Collections.singletonMap("type", "integer"));
+        properties.put("has_magic", Collections.singletonMap("type", "boolean"));
+
+        final Map<String, Object> data = new LinkedHashMap<>();
+        data.put(StitchRequestBody.TABLE_NAME, "my_table");
+        data.put(StitchRequestBody.SCHEMA, Collections.singletonMap("properties", properties));
+        data.put(StitchRequestBody.MESSAGES,
+                Collections.singletonList(Collections.singletonMap("data", Collections.singletonMap("id", 2))));
+        data.put(StitchRequestBody.KEY_NAMES, Collections.singletonList("test_key"));
+
+        final StitchRequestBody requestBody = StitchRequestBody
+                .fromMap(data)
+                .build();
+
+        assertEquals("my_table", requestBody.getTableName());
+        assertEquals(Collections.singletonMap("properties", properties), requestBody.getSchema().getKeywords());
+        assertEquals(Collections.singletonMap("id", 2), requestBody.getMessages().stream().findFirst().get().getData());
+        assertEquals("test_key", requestBody.getKeyNames().stream().findFirst().get());
+    }
+
+}
diff --git a/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/models/StitchResponseTest.java b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/models/StitchResponseTest.java
new file mode 100644
index 0000000..9aee19f
--- /dev/null
+++ b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/models/StitchResponseTest.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client.models;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.camel.component.stitch.client.JsonUtils;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class StitchResponseTest {
+
+    @Test
+    void testIfCreateStitchResponse() {
+        final Map<String, Object> headers = new LinkedHashMap<>();
+        headers.put("test_header_1", "test1");
+        headers.put("test_header_2", "test2");
+
+        final StitchResponse response = new StitchResponse(200, headers, "test", "testing");
+
+        final String responseAsJson = JsonUtils.convertMapToJson(response.toMap());
+
+        assertEquals("{\"code\":200,\"headers\":{\"test_header_1\":\"test1\",\"test_header_2\":\"test2\"},"
+                     + "\"status\":\"test\",\"message\":\"testing\"}",
+                responseAsJson);
+    }
+}
diff --git a/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/models/StitchSchemaTest.java b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/models/StitchSchemaTest.java
new file mode 100644
index 0000000..035739a
--- /dev/null
+++ b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/client/models/StitchSchemaTest.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.client.models;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.camel.component.stitch.client.JsonUtils;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class StitchSchemaTest {
+
+    @Test
+    void testIfCreateSchema() {
+        final Map<String, String> modifiedAtSchema = new LinkedHashMap<>();
+        modifiedAtSchema.put("type", "string");
+        modifiedAtSchema.put("format", "date-time");
+
+        final Map<String, Object> properties = new LinkedHashMap<>();
+        properties.put("id", Collections.singletonMap("type", "integer"));
+        properties.put("name", Collections.singletonMap("type", "string"));
+        properties.put("age", Collections.singletonMap("type", "integer"));
+        properties.put("has_magic", Collections.singletonMap("type", "boolean"));
+        properties.put("modified_at", modifiedAtSchema);
+
+        final StitchSchema schema = StitchSchema.builder()
+                .addKeyword("properties", properties)
+                .addKeyword("has_map", true)
+                .build();
+
+        final String schemaAsJson = JsonUtils.convertMapToJson(schema.toMap());
+
+        assertEquals("{\"properties\":{\"id\":{\"type\":\"integer\"},\"name\":{\"type\":\"string\"},"
+                     + "\"age\":{\"type\":\"integer\"},\"has_magic\":"
+                     + "{\"type\":\"boolean\"},\"modified_at\":{\"type\":\"string\",\"format\":\"date-time\"}},\"has_map\":true}",
+                schemaAsJson);
+    }
+}
diff --git a/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/operations/StitchProducerOperationsIT.java b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/operations/StitchProducerOperationsIT.java
new file mode 100644
index 0000000..f3deaf5
--- /dev/null
+++ b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/operations/StitchProducerOperationsIT.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.operations;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.component.stitch.StitchConfiguration;
+import org.apache.camel.component.stitch.StitchTestUtils;
+import org.apache.camel.component.stitch.client.StitchClient;
+import org.apache.camel.component.stitch.client.StitchClientBuilder;
+import org.apache.camel.component.stitch.client.StitchRegion;
+import org.apache.camel.component.stitch.client.models.StitchException;
+import org.apache.camel.component.stitch.client.models.StitchMessage;
+import org.apache.camel.component.stitch.client.models.StitchRequestBody;
+import org.apache.camel.component.stitch.client.models.StitchResponse;
+import org.apache.camel.component.stitch.client.models.StitchSchema;
+import org.apache.camel.support.DefaultExchange;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import reactor.core.publisher.Mono;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class StitchProducerOperationsIT extends CamelTestSupport {
+
+    private static final Logger LOG = LoggerFactory.getLogger(StitchProducerOperationsIT.class);
+
+    private static StitchClient client;
+
+    @BeforeAll
+    static void prepare() throws Exception {
+        client = StitchClientBuilder.builder()
+                .withRegion(StitchRegion.EUROPE)
+                .withToken(StitchTestUtils.loadStitchTokenFromJvmEnv().getProperty("token"))
+                .build();
+    }
+
+    @Test
+    void testIfSendIfStitchMessagesSet() {
+        final StitchConfiguration configuration = new StitchConfiguration();
+        configuration.setTableName("test_table");
+        configuration.setStitchSchema(StitchSchema.builder().addKeyword("field_1", "string").build());
+        configuration.setKeyNames("field_1");
+
+        final StitchMessage message = StitchMessage.builder()
+                .withData("field_1", "data")
+                .build();
+
+        final Exchange exchange = new DefaultExchange(context);
+        exchange.getMessage().setBody(message);
+
+        sendEventAndAssert(exchange.getMessage(), configuration, response -> {
+            assertNotNull(response);
+            assertTrue(response.getHttpStatusCode() == 200 || response.getHttpStatusCode() == 201);
+        });
+    }
+
+    @Test
+    void testIfSendFromIterable() {
+        final StitchConfiguration configuration = new StitchConfiguration();
+        configuration.setTableName("table_1");
+        configuration.setStitchSchema(StitchSchema.builder().addKeyword("field_1", "string").build());
+        configuration.setKeyNames("field_1");
+
+        final StitchMessage stitchMessage1 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage1")
+                .build();
+
+        final StitchMessage stitchMessage2 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage2-1")
+                .build();
+
+        final StitchRequestBody stitchMessage2RequestBody = StitchRequestBody.builder()
+                .addMessage(stitchMessage2)
+                .withSchema(StitchSchema.builder().addKeyword("field_1", "integer").build())
+                .withTableName("table_1")
+                .withKeyNames(Collections.singleton("field_1"))
+                .build();
+
+        final Map<String, Object> stitchMessage3 = new LinkedHashMap<>();
+        stitchMessage3.put(StitchMessage.DATA, Collections.singletonMap("field_1", "stitchMessage3"));
+
+        final StitchMessage stitchMessage4 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage4")
+                .build();
+
+        final Exchange stitchMessage4Exchange = new DefaultExchange(context);
+        stitchMessage4Exchange.getMessage().setBody(stitchMessage4);
+
+        final StitchMessage stitchMessage5 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage5")
+                .build();
+
+        final Message stitchMessage5Message = new DefaultExchange(context).getMessage();
+        stitchMessage5Message.setBody(stitchMessage5);
+
+        final List<Object> inputMessages = new LinkedList<>();
+        inputMessages.add(stitchMessage1);
+        inputMessages.add(stitchMessage2RequestBody);
+        inputMessages.add(stitchMessage3);
+        inputMessages.add(stitchMessage4Exchange);
+        inputMessages.add(stitchMessage5Message);
+
+        final Exchange exchange = new DefaultExchange(context);
+        exchange.getMessage().setBody(inputMessages);
+
+        sendEventAndAssert(exchange.getMessage(), configuration, response -> {
+            assertNotNull(response);
+            assertTrue(response.getHttpStatusCode() == 200 || response.getHttpStatusCode() == 201);
+        });
+
+    }
+
+    private void sendEventAndAssert(
+            final Message message, final StitchConfiguration configuration, final Consumer<StitchResponse> fn) {
+        try {
+            final StitchResponse response = sendEventMono(message, configuration).block();
+            fn.accept(response);
+        } catch (StitchException exception) {
+            LOG.error(exception.getMessage());
+            throw exception;
+        }
+    }
+
+    private Mono<StitchResponse> sendEventMono(final Message message, final StitchConfiguration configuration) {
+        final StitchProducerOperations operations = new StitchProducerOperations(client, configuration);
+
+        return Mono.create(sink -> operations.sendEvents(message, sink::success, doneSync -> {
+            if (message.getExchange().getException() != null) {
+                sink.error(message.getExchange().getException());
+            }
+        }));
+    }
+}
diff --git a/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/operations/StitchProducerOperationsTest.java b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/operations/StitchProducerOperationsTest.java
new file mode 100644
index 0000000..49d43c4
--- /dev/null
+++ b/components/camel-stitch/src/test/java/org/apache/camel/component/stitch/operations/StitchProducerOperationsTest.java
@@ -0,0 +1,322 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stitch.operations;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.component.stitch.StitchConfiguration;
+import org.apache.camel.component.stitch.StitchConstants;
+import org.apache.camel.component.stitch.client.JsonUtils;
+import org.apache.camel.component.stitch.client.StitchClient;
+import org.apache.camel.component.stitch.client.models.StitchException;
+import org.apache.camel.component.stitch.client.models.StitchMessage;
+import org.apache.camel.component.stitch.client.models.StitchRequestBody;
+import org.apache.camel.component.stitch.client.models.StitchResponse;
+import org.apache.camel.component.stitch.client.models.StitchSchema;
+import org.apache.camel.support.DefaultExchange;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.awaitility.Awaitility;
+import org.junit.jupiter.api.Test;
+import reactor.core.publisher.Mono;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class StitchProducerOperationsTest extends CamelTestSupport {
+
+    @Test
+    void testIfCreateIfStitchMessagesSet() {
+        final StitchConfiguration configuration = new StitchConfiguration();
+        configuration.setTableName("test_table");
+        configuration.setStitchSchema(StitchSchema.builder().addKeyword("field_1", "integer").build());
+        configuration.setKeyNames("field_1,field_2");
+
+        final StitchMessage message = StitchMessage.builder()
+                .withData("field_1", "data")
+                .withSequence(0)
+                .build();
+
+        final Exchange exchange = new DefaultExchange(context);
+        exchange.getMessage().setBody(message);
+
+        final StitchProducerOperations operations = new StitchProducerOperations(new TestClient(), configuration);
+
+        assertEquals("{\"table_name\":\"test_table\",\"schema\":{\"field_1\":\"integer\"},\"messages\":[{\"action\":\"upsert\","
+                     + "\"sequence\":0,\"data\":{\"field_1\":\"data\"}}],\"key_names\":[\"field_1\",\"field_2\"]}",
+                JsonUtils.convertMapToJson(operations.createStitchRequestBody(exchange.getMessage()).toMap()));
+
+        final StitchMessage message1 = StitchMessage.builder()
+                .withData("field_1", "test_2")
+                .withSequence(0)
+                .build();
+
+        exchange.getMessage().setHeader(StitchConstants.SCHEMA,
+                StitchSchema.builder().addKeyword("field_1", "integer").addKeyword("field_2", "string").build());
+        exchange.getMessage().setHeader(StitchConstants.TABLE_NAME, "test_table_2");
+        exchange.getMessage().setHeader(StitchConstants.KEY_NAMES, "field_1,field_2");
+
+        exchange.getMessage().setBody(message1);
+
+        assertEquals("{\"table_name\":\"test_table_2\",\"schema\":{\"field_1\":\"integer\",\"field_2\":\"string\"},"
+                     + "\"messages\":[{\"action\":\"upsert\",\"sequence\":0,\"data\":{\"field_1\":\"test_2\"}}],\"key_names\":[\"field_1\",\"field_2\"]}",
+                JsonUtils.convertMapToJson(operations.createStitchRequestBody(exchange.getMessage()).toMap()));
+    }
+
+    @Test
+    void testIfCreateIfStitchRequestBodySet() {
+        final StitchConfiguration configuration = new StitchConfiguration();
+        configuration.setTableName("table_2");
+
+        final StitchMessage message1 = StitchMessage.builder()
+                .withData("field_1", "test_2")
+                .withSequence(0)
+                .build();
+
+        final StitchRequestBody requestBody = StitchRequestBody.builder()
+                .addMessage(message1)
+                .withSchema(StitchSchema.builder().addKeyword("field_1", "integer").build())
+                .withTableName("table_1")
+                .withKeyNames(Collections.singleton("field_1"))
+                .build();
+
+        final Exchange exchange = new DefaultExchange(context);
+        exchange.getMessage().setBody(requestBody);
+
+        final StitchProducerOperations operations = new StitchProducerOperations(new TestClient(), configuration);
+
+        assertEquals("{\"table_name\":\"table_2\",\"schema\":{\"field_1\":\"integer\"},\"messages\":"
+                     + "[{\"action\":\"upsert\",\"sequence\":0,\"data\":{\"field_1\":\"test_2\"}}],\"key_names\":[\"field_1\"]}",
+                JsonUtils.convertMapToJson(operations.createStitchRequestBody(exchange.getMessage()).toMap()));
+    }
+
+    @Test
+    void testIfCreateIfMapSet() {
+        final StitchConfiguration configuration = new StitchConfiguration();
+
+        final Map<String, Object> properties = new LinkedHashMap<>();
+        properties.put("id", Collections.singletonMap("type", "integer"));
+        properties.put("name", Collections.singletonMap("type", "string"));
+        properties.put("age", Collections.singletonMap("type", "integer"));
+        properties.put("has_magic", Collections.singletonMap("type", "boolean"));
+
+        final Map<String, Object> message = new LinkedHashMap<>();
+        message.put(StitchMessage.DATA, Collections.singletonMap("id", 2));
+        message.put(StitchMessage.SEQUENCE, 1L);
+
+        final Map<String, Object> data = new LinkedHashMap<>();
+        data.put(StitchRequestBody.TABLE_NAME, "my_table");
+        data.put(StitchRequestBody.SCHEMA, Collections.singletonMap("properties", properties));
+        data.put(StitchRequestBody.MESSAGES,
+                Collections.singletonList(message));
+        data.put(StitchRequestBody.KEY_NAMES, Collections.singletonList("test_key"));
+
+        final Exchange exchange = new DefaultExchange(context);
+        exchange.getMessage().setBody(data);
+
+        final StitchProducerOperations operations = new StitchProducerOperations(new TestClient(), configuration);
+
+        final String createdJson
+                = JsonUtils.convertMapToJson(operations.createStitchRequestBody(exchange.getMessage()).toMap());
+
+        assertEquals("{\"table_name\":\"my_table\",\"schema\":{\"properties\":{\"id\":{\"type\":\"integer\"},"
+                     + "\"name\":{\"type\":\"string\"},\"age\":{\"type\":\"integer\"},\"has_magic\""
+                     + ":{\"type\":\"boolean\"}}},\"messages\":[{\"action\":\"upsert\",\"sequence\":1,"
+                     + "\"data\":{\"id\":2}}],\"key_names\":[\"test_key\"]}",
+                createdJson);
+    }
+
+    @Test
+    void testIfCreateFromIterable() {
+        final StitchConfiguration configuration = new StitchConfiguration();
+        configuration.setTableName("table_1");
+        configuration.setStitchSchema(StitchSchema.builder().addKeyword("field_1", "string").build());
+        configuration.setKeyNames("field_1");
+
+        final StitchMessage stitchMessage1 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage1")
+                .withSequence(1)
+                .build();
+
+        final StitchMessage stitchMessage2 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage2-1")
+                .withData("field_2", "stitchMessage2-2")
+                .withSequence(2)
+                .build();
+
+        final StitchRequestBody stitchMessage2RequestBody = StitchRequestBody.builder()
+                .addMessage(stitchMessage2)
+                .withSchema(StitchSchema.builder().addKeyword("field_1", "integer").build())
+                .withTableName("table_1")
+                .withKeyNames(Collections.singleton("field_1"))
+                .build();
+
+        final Map<String, Object> stitchMessage3 = new LinkedHashMap<>();
+        stitchMessage3.put(StitchMessage.DATA, Collections.singletonMap("field_1", "stitchMessage3"));
+        stitchMessage3.put(StitchMessage.SEQUENCE, 3L);
+
+        final StitchMessage stitchMessage4 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage4")
+                .withSequence(4)
+                .build();
+
+        final Exchange stitchMessage4Exchange = new DefaultExchange(context);
+        stitchMessage4Exchange.getMessage().setBody(stitchMessage4);
+
+        final StitchMessage stitchMessage5 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage5")
+                .withSequence(5)
+                .build();
+
+        final Message stitchMessage5Message = new DefaultExchange(context).getMessage();
+        stitchMessage5Message.setBody(stitchMessage5);
+
+        final List<Object> inputMessages = new LinkedList<>();
+        inputMessages.add(stitchMessage1);
+        inputMessages.add(stitchMessage2RequestBody);
+        inputMessages.add(stitchMessage3);
+        inputMessages.add(stitchMessage4Exchange);
+        inputMessages.add(stitchMessage5Message);
+
+        final StitchProducerOperations operations = new StitchProducerOperations(new TestClient(), configuration);
+
+        final Exchange exchange = new DefaultExchange(context);
+        exchange.getMessage().setBody(inputMessages);
+
+        final String createdJson
+                = JsonUtils.convertMapToJson(operations.createStitchRequestBody(exchange.getMessage()).toMap());
+
+        assertEquals(
+                "{\"table_name\":\"table_1\",\"schema\":{\"field_1\":\"string\"},\"messages\":[{\"action\":\"upsert\",\"sequence\":1,\"data\":{\"field_1\":\"stitchMessage1\"}},"
+                     + "{\"action\":\"upsert\",\"sequence\":2,\"data\":{\"field_1\":\"stitchMessage2-1\",\"field_2\":\"stitchMessage2-2\"}},{\"action\":\"upsert\",\"sequence\":3,\"data\":{\"field_1\":"
+                     + "\"stitchMessage3\"}},{\"action\":\"upsert\",\"sequence\":4,\"data\":{\"field_1\":\"stitchMessage4\"}},{\"action\":\"upsert\",\"sequence\":5,\"data\":{\"field_1\":\"stitchMessage5\"}}],"
+                     + "\"key_names\":[\"field_1\"]}",
+                createdJson);
+    }
+
+    @Test
+    void testNormalSend() {
+        final StitchConfiguration configuration = new StitchConfiguration();
+        configuration.setTableName("table_1");
+        configuration.setStitchSchema(StitchSchema.builder().addKeyword("field_1", "string").build());
+        configuration.setKeyNames("field_1");
+
+        final StitchMessage message = StitchMessage.builder()
+                .withData("field_1", "data")
+                .withSequence(0)
+                .build();
+
+        final Exchange exchange = new DefaultExchange(context);
+        exchange.getMessage().setBody(message);
+
+        final StitchProducerOperations operations = new StitchProducerOperations(new TestClient(), configuration);
+        final AtomicBoolean done = new AtomicBoolean(false);
+
+        operations.sendEvents(exchange.getMessage(), response -> {
+            assertEquals(200, response.getHttpStatusCode());
+            assertEquals("OK", response.getStatus());
+            assertEquals("All good!", response.getMessage());
+            assertEquals(Collections.singletonMap("header-1", "test"), response.getHeaders());
+            done.set(true);
+        }, doneSync -> {
+        });
+
+        Awaitility
+                .await()
+                .atMost(1, TimeUnit.SECONDS)
+                .pollInterval(10, TimeUnit.MILLISECONDS)
+                .untilTrue(done);
+    }
+
+    @Test
+    void testErrorHandle() {
+        final StitchConfiguration configuration = new StitchConfiguration();
+        configuration.setTableName("table_1");
+        configuration.setStitchSchema(StitchSchema.builder().addKeyword("field_1", "string").build());
+        configuration.setKeyNames("field_1");
+
+        final StitchMessage message = StitchMessage.builder()
+                .withData("field_1", "data")
+                .withSequence(0)
+                .build();
+
+        final Exchange exchange = new DefaultExchange(context);
+        exchange.getMessage().setBody(message);
+
+        final StitchProducerOperations operations = new StitchProducerOperations(new TestErrorClient(), configuration);
+
+        operations.sendEvents(exchange.getMessage(), response -> {
+        }, doneSync -> {
+        });
+
+        assertNotNull(exchange.getException());
+        assertTrue(exchange.getException() instanceof StitchException);
+        assertNotNull(((StitchException) exchange.getException()).getResponse());
+        assertEquals(400, ((StitchException) exchange.getException()).getResponse().getHttpStatusCode());
+        assertEquals("Error", ((StitchException) exchange.getException()).getResponse().getStatus());
+        assertEquals("Not good!", ((StitchException) exchange.getException()).getResponse().getMessage());
+    }
+
+    static class TestClient implements StitchClient {
+
+        @Override
+        public Mono<StitchResponse> batch(StitchRequestBody requestBody) {
+            final StitchResponse response = new StitchResponse(
+                    200,
+                    Collections.singletonMap("header-1", "test"),
+                    "OK",
+                    "All good!");
+
+            return Mono.just(response);
+        }
+
+        @Override
+        public void close() throws IOException {
+            // noop
+        }
+    }
+
+    static class TestErrorClient implements StitchClient {
+
+        @Override
+        public Mono<StitchResponse> batch(StitchRequestBody requestBody) {
+            final StitchResponse response = new StitchResponse(
+                    400,
+                    Collections.singletonMap("header-1", "test"),
+                    "Error",
+                    "Not good!");
+
+            final StitchException exception = new StitchException(response);
+
+            return Mono.error(exception);
+        }
+
+        @Override
+        public void close() throws IOException {
+            // noop
+        }
+    }
+}
diff --git a/components/camel-stitch/src/test/resources/log4j2.properties b/components/camel-stitch/src/test/resources/log4j2.properties
new file mode 100644
index 0000000..9e12cb1
--- /dev/null
+++ b/components/camel-stitch/src/test/resources/log4j2.properties
@@ -0,0 +1,27 @@
+## ---------------------------------------------------------------------------
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements.  See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License.  You may obtain a copy of the License at
+##
+##      http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+## ---------------------------------------------------------------------------
+appender.file.type = File
+appender.file.name = file
+appender.file.fileName = target/camel-stitch-test.log
+appender.file.layout.type = PatternLayout
+appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+appender.out.type = Console
+appender.out.name = out
+appender.out.layout.type = PatternLayout
+appender.out.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+rootLogger.level = INFO
+rootLogger.appenderRef.file.ref = file
\ No newline at end of file
diff --git a/components/pom.xml b/components/pom.xml
index 863d28a..ca255f2 100644
--- a/components/pom.xml
+++ b/components/pom.xml
@@ -370,6 +370,7 @@
         <module>camel-stomp</module>
         <module>camel-stream</module>
         <module>camel-stringtemplate</module>
+        <module>camel-stitch</module>
         <module>camel-swagger-java</module>
         <module>camel-openapi-java</module>
         <module>camel-syslog</module>
diff --git a/core/camel-allcomponents/pom.xml b/core/camel-allcomponents/pom.xml
index 450620e..8164935 100644
--- a/core/camel-allcomponents/pom.xml
+++ b/core/camel-allcomponents/pom.xml
@@ -1303,6 +1303,10 @@
 		</dependency>
 		<dependency>
 			<groupId>org.apache.camel</groupId>
+			<artifactId>camel-stitch</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.camel</groupId>
 			<artifactId>camel-stomp</artifactId>
 		</dependency>
 		<dependency>
diff --git a/core/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/ComponentsBuilderFactory.java b/core/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/ComponentsBuilderFactory.java
index 39d2e74..b7ef2e3 100644
--- a/core/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/ComponentsBuilderFactory.java
+++ b/core/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/ComponentsBuilderFactory.java
@@ -4359,6 +4359,20 @@ public interface ComponentsBuilderFactory {
         return org.apache.camel.builder.component.dsl.StaxComponentBuilderFactory.stax();
     }
     /**
+     * Stitch (camel-stitch)
+     * Stitch is a cloud ETL service that integrates various data sources into a
+     * central data warehouse through various integrations.
+     * 
+     * Category: cloud,api,compute,bigdata
+     * Since: 3.8
+     * Maven coordinates: org.apache.camel:camel-stitch
+     * 
+     * @return the dsl builder
+     */
+    static org.apache.camel.builder.component.dsl.StitchComponentBuilderFactory.StitchComponentBuilder stitch() {
+        return org.apache.camel.builder.component.dsl.StitchComponentBuilderFactory.stitch();
+    }
+    /**
      * Stomp (camel-stomp)
      * Send and rececive messages to/from STOMP (Simple Text Oriented Messaging
      * Protocol) compliant message brokers.
diff --git a/core/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/StitchComponentBuilderFactory.java b/core/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/StitchComponentBuilderFactory.java
new file mode 100644
index 0000000..8853dff
--- /dev/null
+++ b/core/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/StitchComponentBuilderFactory.java
@@ -0,0 +1,271 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.builder.component.dsl;
+
+import javax.annotation.Generated;
+import org.apache.camel.Component;
+import org.apache.camel.builder.component.AbstractComponentBuilder;
+import org.apache.camel.builder.component.ComponentBuilder;
+import org.apache.camel.component.stitch.StitchComponent;
+
+/**
+ * Stitch is a cloud ETL service that integrates various data sources into a
+ * central data warehouse through various integrations.
+ * 
+ * Generated by camel-package-maven-plugin - do not edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.ComponentDslMojo")
+public interface StitchComponentBuilderFactory {
+
+    /**
+     * Stitch (camel-stitch)
+     * Stitch is a cloud ETL service that integrates various data sources into a
+     * central data warehouse through various integrations.
+     * 
+     * Category: cloud,api,compute,bigdata
+     * Since: 3.8
+     * Maven coordinates: org.apache.camel:camel-stitch
+     * 
+     * @return the dsl builder
+     */
+    static StitchComponentBuilder stitch() {
+        return new StitchComponentBuilderImpl();
+    }
+
+    /**
+     * Builder for the Stitch component.
+     */
+    interface StitchComponentBuilder
+            extends
+                ComponentBuilder<StitchComponent> {
+        /**
+         * The component configurations.
+         * 
+         * The option is a:
+         * &lt;code&gt;org.apache.camel.component.stitch.StitchConfiguration&lt;/code&gt; type.
+         * 
+         * Group: producer
+         * 
+         * @param configuration the value to set
+         * @return the dsl builder
+         */
+        default StitchComponentBuilder configuration(
+                org.apache.camel.component.stitch.StitchConfiguration configuration) {
+            doSetProperty("configuration", configuration);
+            return this;
+        }
+        /**
+         * A collection of comma separated strings representing the Primary Key
+         * fields in the source table. Stitch use these Primary Keys to de-dupe
+         * data during loading If not provided, the table will be loaded in an
+         * append-only manner.
+         * 
+         * The option is a: &lt;code&gt;java.lang.String&lt;/code&gt; type.
+         * 
+         * Group: producer
+         * 
+         * @param keyNames the value to set
+         * @return the dsl builder
+         */
+        default StitchComponentBuilder keyNames(java.lang.String keyNames) {
+            doSetProperty("keyNames", keyNames);
+            return this;
+        }
+        /**
+         * Whether the producer should be started lazy (on the first message).
+         * By starting lazy you can use this to allow CamelContext and routes to
+         * startup in situations where a producer may otherwise fail during
+         * starting and cause the route to fail being started. By deferring this
+         * startup to be lazy then the startup failure can be handled during
+         * routing messages via Camel's routing error handlers. Beware that when
+         * the first message is processed then creating and starting the
+         * producer may take a little time and prolong the total processing time
+         * of the processing.
+         * 
+         * The option is a: &lt;code&gt;boolean&lt;/code&gt; type.
+         * 
+         * Default: false
+         * Group: producer
+         * 
+         * @param lazyStartProducer the value to set
+         * @return the dsl builder
+         */
+        default StitchComponentBuilder lazyStartProducer(
+                boolean lazyStartProducer) {
+            doSetProperty("lazyStartProducer", lazyStartProducer);
+            return this;
+        }
+        /**
+         * Stitch account region, e.g: europe.
+         * 
+         * The option is a:
+         * &lt;code&gt;org.apache.camel.component.stitch.client.StitchRegion&lt;/code&gt; type.
+         * 
+         * Default: europe
+         * Group: producer
+         * 
+         * @param region the value to set
+         * @return the dsl builder
+         */
+        default StitchComponentBuilder region(
+                org.apache.camel.component.stitch.client.StitchRegion region) {
+            doSetProperty("region", region);
+            return this;
+        }
+        /**
+         * A schema that describes the record(s).
+         * 
+         * The option is a:
+         * &lt;code&gt;org.apache.camel.component.stitch.client.models.StitchSchema&lt;/code&gt; type.
+         * 
+         * Group: producer
+         * 
+         * @param stitchSchema the value to set
+         * @return the dsl builder
+         */
+        default StitchComponentBuilder stitchSchema(
+                org.apache.camel.component.stitch.client.models.StitchSchema stitchSchema) {
+            doSetProperty("stitchSchema", stitchSchema);
+            return this;
+        }
+        /**
+         * ConnectionProvider contain configuration for the HttpClient like
+         * Maximum connection limit .. etc, you can inject this
+         * ConnectionProvider and the StitchClient will initialize HttpClient
+         * with this ConnectionProvider.
+         * 
+         * The option is a:
+         * &lt;code&gt;reactor.netty.resources.ConnectionProvider&lt;/code&gt;
+         * type.
+         * 
+         * Group: producer (advanced)
+         * 
+         * @param connectionProvider the value to set
+         * @return the dsl builder
+         */
+        default StitchComponentBuilder connectionProvider(
+                reactor.netty.resources.ConnectionProvider connectionProvider) {
+            doSetProperty("connectionProvider", connectionProvider);
+            return this;
+        }
+        /**
+         * Reactor Netty HttpClient, you can injected it if you want to have
+         * custom HttpClient.
+         * 
+         * The option is a:
+         * &lt;code&gt;reactor.netty.http.client.HttpClient&lt;/code&gt; type.
+         * 
+         * Group: producer (advanced)
+         * 
+         * @param httpClient the value to set
+         * @return the dsl builder
+         */
+        default StitchComponentBuilder httpClient(
+                reactor.netty.http.client.HttpClient httpClient) {
+            doSetProperty("httpClient", httpClient);
+            return this;
+        }
+        /**
+         * Whether autowiring is enabled. This is used for automatic autowiring
+         * options (the option must be marked as autowired) by looking up in the
+         * registry to find if there is a single instance of matching type,
+         * which then gets configured on the component. This can be used for
+         * automatic configuring JDBC data sources, JMS connection factories,
+         * AWS Clients, etc.
+         * 
+         * The option is a: &lt;code&gt;boolean&lt;/code&gt; type.
+         * 
+         * Default: true
+         * Group: advanced
+         * 
+         * @param autowiredEnabled the value to set
+         * @return the dsl builder
+         */
+        default StitchComponentBuilder autowiredEnabled(boolean autowiredEnabled) {
+            doSetProperty("autowiredEnabled", autowiredEnabled);
+            return this;
+        }
+        /**
+         * Set a custom StitchClient that implements
+         * org.apache.camel.component.stitch.client.StitchClient interface.
+         * 
+         * The option is a:
+         * &lt;code&gt;org.apache.camel.component.stitch.client.StitchClient&lt;/code&gt; type.
+         * 
+         * Group: advanced
+         * 
+         * @param stitchClient the value to set
+         * @return the dsl builder
+         */
+        default StitchComponentBuilder stitchClient(
+                org.apache.camel.component.stitch.client.StitchClient stitchClient) {
+            doSetProperty("stitchClient", stitchClient);
+            return this;
+        }
+        /**
+         * Stitch access token for the Stitch Import API.
+         * 
+         * The option is a: &lt;code&gt;java.lang.String&lt;/code&gt; type.
+         * 
+         * Group: security
+         * 
+         * @param token the value to set
+         * @return the dsl builder
+         */
+        default StitchComponentBuilder token(java.lang.String token) {
+            doSetProperty("token", token);
+            return this;
+        }
+    }
+
+    class StitchComponentBuilderImpl
+            extends
+                AbstractComponentBuilder<StitchComponent>
+            implements
+                StitchComponentBuilder {
+        @Override
+        protected StitchComponent buildConcreteComponent() {
+            return new StitchComponent();
+        }
+        private org.apache.camel.component.stitch.StitchConfiguration getOrCreateConfiguration(
+                org.apache.camel.component.stitch.StitchComponent component) {
+            if (component.getConfiguration() == null) {
+                component.setConfiguration(new org.apache.camel.component.stitch.StitchConfiguration());
+            }
+            return component.getConfiguration();
+        }
+        @Override
+        protected boolean setPropertyOnComponent(
+                Component component,
+                String name,
+                Object value) {
+            switch (name) {
+            case "configuration": ((StitchComponent) component).setConfiguration((org.apache.camel.component.stitch.StitchConfiguration) value); return true;
+            case "keyNames": getOrCreateConfiguration((StitchComponent) component).setKeyNames((java.lang.String) value); return true;
+            case "lazyStartProducer": ((StitchComponent) component).setLazyStartProducer((boolean) value); return true;
+            case "region": getOrCreateConfiguration((StitchComponent) component).setRegion((org.apache.camel.component.stitch.client.StitchRegion) value); return true;
+            case "stitchSchema": getOrCreateConfiguration((StitchComponent) component).setStitchSchema((org.apache.camel.component.stitch.client.models.StitchSchema) value); return true;
+            case "connectionProvider": getOrCreateConfiguration((StitchComponent) component).setConnectionProvider((reactor.netty.resources.ConnectionProvider) value); return true;
+            case "httpClient": getOrCreateConfiguration((StitchComponent) component).setHttpClient((reactor.netty.http.client.HttpClient) value); return true;
+            case "autowiredEnabled": ((StitchComponent) component).setAutowiredEnabled((boolean) value); return true;
+            case "stitchClient": getOrCreateConfiguration((StitchComponent) component).setStitchClient((org.apache.camel.component.stitch.client.StitchClient) value); return true;
+            case "token": getOrCreateConfiguration((StitchComponent) component).setToken((java.lang.String) value); return true;
+            default: return false;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/camel-componentdsl/src/generated/resources/metadata.json b/core/camel-componentdsl/src/generated/resources/metadata.json
index 8be7968..142733a 100644
--- a/core/camel-componentdsl/src/generated/resources/metadata.json
+++ b/core/camel-componentdsl/src/generated/resources/metadata.json
@@ -7208,6 +7208,28 @@
     "producerOnly": true,
     "lenientProperties": false
   },
+  "StitchComponentBuilderFactory": {
+    "kind": "component",
+    "name": "stitch",
+    "title": "Stitch",
+    "description": "Stitch is a cloud ETL service that integrates various data sources into a central data warehouse through various integrations.",
+    "deprecated": false,
+    "firstVersion": "3.8.0",
+    "label": "cloud,api,compute,bigdata",
+    "javaType": "org.apache.camel.component.stitch.StitchComponent",
+    "supportLevel": "Preview",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-stitch",
+    "version": "3.8.0-SNAPSHOT",
+    "scheme": "stitch",
+    "extendsScheme": "",
+    "syntax": "stitch:tableName",
+    "async": false,
+    "api": false,
+    "consumerOnly": false,
+    "producerOnly": true,
+    "lenientProperties": false
+  },
   "StompComponentBuilderFactory": {
     "kind": "component",
     "name": "stomp",
diff --git a/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilderFactory.java b/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilderFactory.java
index a168bbc..ab6bf03 100644
--- a/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilderFactory.java
+++ b/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilderFactory.java
@@ -335,6 +335,7 @@ public interface EndpointBuilderFactory
             org.apache.camel.builder.endpoint.dsl.SqsEndpointBuilderFactory.SqsBuilders,
             org.apache.camel.builder.endpoint.dsl.SshEndpointBuilderFactory.SshBuilders,
             org.apache.camel.builder.endpoint.dsl.StAXEndpointBuilderFactory.StAXBuilders,
+            org.apache.camel.builder.endpoint.dsl.StitchEndpointBuilderFactory.StitchBuilders,
             org.apache.camel.builder.endpoint.dsl.StompEndpointBuilderFactory.StompBuilders,
             org.apache.camel.builder.endpoint.dsl.StreamEndpointBuilderFactory.StreamBuilders,
             org.apache.camel.builder.endpoint.dsl.StringTemplateEndpointBuilderFactory.StringTemplateBuilders,
diff --git a/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilders.java b/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilders.java
index a911ebc..558d439 100644
--- a/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilders.java
+++ b/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilders.java
@@ -332,6 +332,7 @@ public interface EndpointBuilders
             org.apache.camel.builder.endpoint.dsl.SqsEndpointBuilderFactory,
             org.apache.camel.builder.endpoint.dsl.SshEndpointBuilderFactory,
             org.apache.camel.builder.endpoint.dsl.StAXEndpointBuilderFactory,
+            org.apache.camel.builder.endpoint.dsl.StitchEndpointBuilderFactory,
             org.apache.camel.builder.endpoint.dsl.StompEndpointBuilderFactory,
             org.apache.camel.builder.endpoint.dsl.StreamEndpointBuilderFactory,
             org.apache.camel.builder.endpoint.dsl.StringTemplateEndpointBuilderFactory,
diff --git a/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java b/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java
index eb6ef5f..961f622 100644
--- a/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java
+++ b/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java
@@ -14896,6 +14896,57 @@ public class StaticEndpointBuilders {
         return org.apache.camel.builder.endpoint.dsl.StAXEndpointBuilderFactory.endpointBuilder(componentName, path);
     }
     /**
+     * Stitch (camel-stitch)
+     * Stitch is a cloud ETL service that integrates various data sources into a
+     * central data warehouse through various integrations.
+     * 
+     * Category: cloud,api,compute,bigdata
+     * Since: 3.8
+     * Maven coordinates: org.apache.camel:camel-stitch
+     * 
+     * Syntax: <code>stitch:tableName</code>
+     * 
+     * Path parameter: tableName
+     * The name of the destination table the data is being pushed to. Table
+     * names must be unique in each destination schema, or loading issues will
+     * occur. Note: The number of characters in the table name should be within
+     * the destinations allowed limits or data will rejected.
+     * 
+     * @param path tableName
+     * @return the dsl builder
+     */
+    public static org.apache.camel.builder.endpoint.dsl.StitchEndpointBuilderFactory.StitchEndpointBuilder stitch(
+            String path) {
+        return org.apache.camel.builder.endpoint.dsl.StitchEndpointBuilderFactory.endpointBuilder("stitch", path);
+    }
+    /**
+     * Stitch (camel-stitch)
+     * Stitch is a cloud ETL service that integrates various data sources into a
+     * central data warehouse through various integrations.
+     * 
+     * Category: cloud,api,compute,bigdata
+     * Since: 3.8
+     * Maven coordinates: org.apache.camel:camel-stitch
+     * 
+     * Syntax: <code>stitch:tableName</code>
+     * 
+     * Path parameter: tableName
+     * The name of the destination table the data is being pushed to. Table
+     * names must be unique in each destination schema, or loading issues will
+     * occur. Note: The number of characters in the table name should be within
+     * the destinations allowed limits or data will rejected.
+     * 
+     * @param componentName to use a custom component name for the endpoint
+     * instead of the default name
+     * @param path tableName
+     * @return the dsl builder
+     */
+    public static org.apache.camel.builder.endpoint.dsl.StitchEndpointBuilderFactory.StitchEndpointBuilder stitch(
+            String componentName,
+            String path) {
+        return org.apache.camel.builder.endpoint.dsl.StitchEndpointBuilderFactory.endpointBuilder(componentName, path);
+    }
+    /**
      * Stomp (camel-stomp)
      * Send and rececive messages to/from STOMP (Simple Text Oriented Messaging
      * Protocol) compliant message brokers.
diff --git a/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/StitchEndpointBuilderFactory.java b/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/StitchEndpointBuilderFactory.java
new file mode 100644
index 0000000..1a9069b
--- /dev/null
+++ b/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/StitchEndpointBuilderFactory.java
@@ -0,0 +1,369 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.builder.endpoint.dsl;
+
+import javax.annotation.Generated;
+import org.apache.camel.builder.EndpointConsumerBuilder;
+import org.apache.camel.builder.EndpointProducerBuilder;
+import org.apache.camel.builder.endpoint.AbstractEndpointBuilder;
+
+/**
+ * Stitch is a cloud ETL service that integrates various data sources into a
+ * central data warehouse through various integrations.
+ * 
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.EndpointDslMojo")
+public interface StitchEndpointBuilderFactory {
+
+
+    /**
+     * Builder for endpoint for the Stitch component.
+     */
+    public interface StitchEndpointBuilder extends EndpointProducerBuilder {
+        default AdvancedStitchEndpointBuilder advanced() {
+            return (AdvancedStitchEndpointBuilder) this;
+        }
+        /**
+         * A collection of comma separated strings representing the Primary Key
+         * fields in the source table. Stitch use these Primary Keys to de-dupe
+         * data during loading If not provided, the table will be loaded in an
+         * append-only manner.
+         * 
+         * The option is a: &lt;code&gt;java.lang.String&lt;/code&gt; type.
+         * 
+         * Group: producer
+         * 
+         * @param keyNames the value to set
+         * @return the dsl builder
+         */
+        default StitchEndpointBuilder keyNames(String keyNames) {
+            doSetProperty("keyNames", keyNames);
+            return this;
+        }
+        /**
+         * Whether the producer should be started lazy (on the first message).
+         * By starting lazy you can use this to allow CamelContext and routes to
+         * startup in situations where a producer may otherwise fail during
+         * starting and cause the route to fail being started. By deferring this
+         * startup to be lazy then the startup failure can be handled during
+         * routing messages via Camel's routing error handlers. Beware that when
+         * the first message is processed then creating and starting the
+         * producer may take a little time and prolong the total processing time
+         * of the processing.
+         * 
+         * The option is a: &lt;code&gt;boolean&lt;/code&gt; type.
+         * 
+         * Default: false
+         * Group: producer
+         * 
+         * @param lazyStartProducer the value to set
+         * @return the dsl builder
+         */
+        default StitchEndpointBuilder lazyStartProducer(
+                boolean lazyStartProducer) {
+            doSetProperty("lazyStartProducer", lazyStartProducer);
+            return this;
+        }
+        /**
+         * Whether the producer should be started lazy (on the first message).
+         * By starting lazy you can use this to allow CamelContext and routes to
+         * startup in situations where a producer may otherwise fail during
+         * starting and cause the route to fail being started. By deferring this
+         * startup to be lazy then the startup failure can be handled during
+         * routing messages via Camel's routing error handlers. Beware that when
+         * the first message is processed then creating and starting the
+         * producer may take a little time and prolong the total processing time
+         * of the processing.
+         * 
+         * The option will be converted to a &lt;code&gt;boolean&lt;/code&gt;
+         * type.
+         * 
+         * Default: false
+         * Group: producer
+         * 
+         * @param lazyStartProducer the value to set
+         * @return the dsl builder
+         */
+        default StitchEndpointBuilder lazyStartProducer(String lazyStartProducer) {
+            doSetProperty("lazyStartProducer", lazyStartProducer);
+            return this;
+        }
+        /**
+         * Stitch account region, e.g: europe.
+         * 
+         * The option is a:
+         * &lt;code&gt;org.apache.camel.component.stitch.client.StitchRegion&lt;/code&gt; type.
+         * 
+         * Default: europe
+         * Group: producer
+         * 
+         * @param region the value to set
+         * @return the dsl builder
+         */
+        default StitchEndpointBuilder region(StitchRegion region) {
+            doSetProperty("region", region);
+            return this;
+        }
+        /**
+         * Stitch account region, e.g: europe.
+         * 
+         * The option will be converted to a
+         * &lt;code&gt;org.apache.camel.component.stitch.client.StitchRegion&lt;/code&gt; type.
+         * 
+         * Default: europe
+         * Group: producer
+         * 
+         * @param region the value to set
+         * @return the dsl builder
+         */
+        default StitchEndpointBuilder region(String region) {
+            doSetProperty("region", region);
+            return this;
+        }
+        /**
+         * A schema that describes the record(s).
+         * 
+         * The option is a:
+         * &lt;code&gt;org.apache.camel.component.stitch.client.models.StitchSchema&lt;/code&gt; type.
+         * 
+         * Group: producer
+         * 
+         * @param stitchSchema the value to set
+         * @return the dsl builder
+         */
+        default StitchEndpointBuilder stitchSchema(Object stitchSchema) {
+            doSetProperty("stitchSchema", stitchSchema);
+            return this;
+        }
+        /**
+         * A schema that describes the record(s).
+         * 
+         * The option will be converted to a
+         * &lt;code&gt;org.apache.camel.component.stitch.client.models.StitchSchema&lt;/code&gt; type.
+         * 
+         * Group: producer
+         * 
+         * @param stitchSchema the value to set
+         * @return the dsl builder
+         */
+        default StitchEndpointBuilder stitchSchema(String stitchSchema) {
+            doSetProperty("stitchSchema", stitchSchema);
+            return this;
+        }
+        /**
+         * Stitch access token for the Stitch Import API.
+         * 
+         * The option is a: &lt;code&gt;java.lang.String&lt;/code&gt; type.
+         * 
+         * Required: true
+         * Group: security
+         * 
+         * @param token the value to set
+         * @return the dsl builder
+         */
+        default StitchEndpointBuilder token(String token) {
+            doSetProperty("token", token);
+            return this;
+        }
+    }
+
+    /**
+     * Advanced builder for endpoint for the Stitch component.
+     */
+    public interface AdvancedStitchEndpointBuilder
+            extends
+                EndpointProducerBuilder {
+        default StitchEndpointBuilder basic() {
+            return (StitchEndpointBuilder) this;
+        }
+        /**
+         * ConnectionProvider contain configuration for the HttpClient like
+         * Maximum connection limit .. etc, you can inject this
+         * ConnectionProvider and the StitchClient will initialize HttpClient
+         * with this ConnectionProvider.
+         * 
+         * The option is a:
+         * &lt;code&gt;reactor.netty.resources.ConnectionProvider&lt;/code&gt;
+         * type.
+         * 
+         * Group: producer (advanced)
+         * 
+         * @param connectionProvider the value to set
+         * @return the dsl builder
+         */
+        default AdvancedStitchEndpointBuilder connectionProvider(
+                Object connectionProvider) {
+            doSetProperty("connectionProvider", connectionProvider);
+            return this;
+        }
+        /**
+         * ConnectionProvider contain configuration for the HttpClient like
+         * Maximum connection limit .. etc, you can inject this
+         * ConnectionProvider and the StitchClient will initialize HttpClient
+         * with this ConnectionProvider.
+         * 
+         * The option will be converted to a
+         * &lt;code&gt;reactor.netty.resources.ConnectionProvider&lt;/code&gt;
+         * type.
+         * 
+         * Group: producer (advanced)
+         * 
+         * @param connectionProvider the value to set
+         * @return the dsl builder
+         */
+        default AdvancedStitchEndpointBuilder connectionProvider(
+                String connectionProvider) {
+            doSetProperty("connectionProvider", connectionProvider);
+            return this;
+        }
+        /**
+         * Reactor Netty HttpClient, you can injected it if you want to have
+         * custom HttpClient.
+         * 
+         * The option is a:
+         * &lt;code&gt;reactor.netty.http.client.HttpClient&lt;/code&gt; type.
+         * 
+         * Group: producer (advanced)
+         * 
+         * @param httpClient the value to set
+         * @return the dsl builder
+         */
+        default AdvancedStitchEndpointBuilder httpClient(Object httpClient) {
+            doSetProperty("httpClient", httpClient);
+            return this;
+        }
+        /**
+         * Reactor Netty HttpClient, you can injected it if you want to have
+         * custom HttpClient.
+         * 
+         * The option will be converted to a
+         * &lt;code&gt;reactor.netty.http.client.HttpClient&lt;/code&gt; type.
+         * 
+         * Group: producer (advanced)
+         * 
+         * @param httpClient the value to set
+         * @return the dsl builder
+         */
+        default AdvancedStitchEndpointBuilder httpClient(String httpClient) {
+            doSetProperty("httpClient", httpClient);
+            return this;
+        }
+        /**
+         * Set a custom StitchClient that implements
+         * org.apache.camel.component.stitch.client.StitchClient interface.
+         * 
+         * The option is a:
+         * &lt;code&gt;org.apache.camel.component.stitch.client.StitchClient&lt;/code&gt; type.
+         * 
+         * Group: advanced
+         * 
+         * @param stitchClient the value to set
+         * @return the dsl builder
+         */
+        default AdvancedStitchEndpointBuilder stitchClient(Object stitchClient) {
+            doSetProperty("stitchClient", stitchClient);
+            return this;
+        }
+        /**
+         * Set a custom StitchClient that implements
+         * org.apache.camel.component.stitch.client.StitchClient interface.
+         * 
+         * The option will be converted to a
+         * &lt;code&gt;org.apache.camel.component.stitch.client.StitchClient&lt;/code&gt; type.
+         * 
+         * Group: advanced
+         * 
+         * @param stitchClient the value to set
+         * @return the dsl builder
+         */
+        default AdvancedStitchEndpointBuilder stitchClient(String stitchClient) {
+            doSetProperty("stitchClient", stitchClient);
+            return this;
+        }
+    }
+
+    /**
+     * Proxy enum for
+     * <code>org.apache.camel.component.stitch.client.StitchRegion</code> enum.
+     */
+    enum StitchRegion {
+        NORTH_AMERICA,
+        EUROPE;
+    }
+
+    public interface StitchBuilders {
+        /**
+         * Stitch (camel-stitch)
+         * Stitch is a cloud ETL service that integrates various data sources
+         * into a central data warehouse through various integrations.
+         * 
+         * Category: cloud,api,compute,bigdata
+         * Since: 3.8
+         * Maven coordinates: org.apache.camel:camel-stitch
+         * 
+         * Syntax: <code>stitch:tableName</code>
+         * 
+         * Path parameter: tableName
+         * The name of the destination table the data is being pushed to. Table
+         * names must be unique in each destination schema, or loading issues
+         * will occur. Note: The number of characters in the table name should
+         * be within the destinations allowed limits or data will rejected.
+         * 
+         * @param path tableName
+         * @return the dsl builder
+         */
+        default StitchEndpointBuilder stitch(String path) {
+            return StitchEndpointBuilderFactory.endpointBuilder("stitch", path);
+        }
+        /**
+         * Stitch (camel-stitch)
+         * Stitch is a cloud ETL service that integrates various data sources
+         * into a central data warehouse through various integrations.
+         * 
+         * Category: cloud,api,compute,bigdata
+         * Since: 3.8
+         * Maven coordinates: org.apache.camel:camel-stitch
+         * 
+         * Syntax: <code>stitch:tableName</code>
+         * 
+         * Path parameter: tableName
+         * The name of the destination table the data is being pushed to. Table
+         * names must be unique in each destination schema, or loading issues
+         * will occur. Note: The number of characters in the table name should
+         * be within the destinations allowed limits or data will rejected.
+         * 
+         * @param componentName to use a custom component name for the endpoint
+         * instead of the default name
+         * @param path tableName
+         * @return the dsl builder
+         */
+        default StitchEndpointBuilder stitch(String componentName, String path) {
+            return StitchEndpointBuilderFactory.endpointBuilder(componentName, path);
+        }
+    }
+    static StitchEndpointBuilder endpointBuilder(
+            String componentName,
+            String path) {
+        class StitchEndpointBuilderImpl extends AbstractEndpointBuilder implements StitchEndpointBuilder, AdvancedStitchEndpointBuilder {
+            public StitchEndpointBuilderImpl(String path) {
+                super(componentName, path);
+            }
+        }
+        return new StitchEndpointBuilderImpl(path);
+    }
+}
\ No newline at end of file
diff --git a/docs/components/modules/ROOT/pages/stitch-component.adoc b/docs/components/modules/ROOT/pages/stitch-component.adoc
new file mode 100644
index 0000000..9f0c5bc
--- /dev/null
+++ b/docs/components/modules/ROOT/pages/stitch-component.adoc
@@ -0,0 +1,314 @@
+[[stitch-component]]
+= Stitch Component
+//THIS FILE IS COPIED: EDIT THE SOURCE FILE:
+:page-source: components/camel-stitch/src/main/docs/stitch-component.adoc
+:docTitle: Stitch
+:artifactId: camel-stitch
+:description: Stitch is a cloud ETL service that integrates various data sources into a central data warehouse through various integrations.
+:since: 3.8
+:supportLevel: Preview
+:component-header: Only producer is supported
+include::{cq-version}@camel-quarkus:ROOT:partial$reference/components/stitch.adoc[opts=optional]
+
+*Since Camel {since}*
+
+*{component-header}*
+
+Stitch is a cloud ETL service, developer-focused platform for rapidly moving and replicates data from more than 90
+applications and databases. It integrates various data sources into a central data warehouse. Stitch has integrations
+for many enterprise software data sources, and can receive data via WebHooks and an API (Stitch Import API) which
+Camel Stitch Component uses to produce the data to Stitch ETL.
+
+For more info, feel free to visit their website: https://www.stitchdata.com/
+
+Prerequisites
+
+You must have a valid Stitch account, you will need to enable Stitch Import API and generate a token for the
+integration, for more info, please find more info https://www.stitchdata.com/docs/developers/import-api/guides/quick-start[here].
+
+Maven users will need to add the following dependency to their `pom.xml`
+for this component:
+
+[source,xml]
+------------------------------------------------------------
+<dependency>
+    <groupId>org.apache.camel</groupId>
+    <artifactId>camel-stitch</artifactId>
+    <version>x.x.x</version>
+  <!-- use the same version as your Camel core version -->
+</dependency>
+------------------------------------------------------------
+
+
+== URI format
+
+[source,text]
+------------------------------
+stitch:[tableName]//[?options]
+------------------------------
+
+For example in order to produce data to Stitch from a custom processor:
+[source,java]
+--------------------------------------------------------------------------------
+from("direct:sendStitch")
+     .process(exchange -> {
+         final StitchMessage stitchMessage = StitchMessage.builder()
+               .withData("field_1", "stitchMessage2-1")
+               .build();
+
+         final StitchRequestBody stitchRequestBody = StitchRequestBody.builder()
+                .addMessage(stitchMessage)
+                .withSchema(StitchSchema.builder().addKeyword("field_1", "string").build())
+                .withTableName("table_1")
+                .withKeyNames(Collections.singleton("field_1"))
+                .build();
+
+                exchange.getMessage().setBody(stitchRequestBody);
+     })
+.to("stitch:table_1?token=RAW({{token}})");
+--------------------------------------------------------------------------------
+
+== URI Options
+// endpoint options: START
+The Stitch endpoint is configured using URI syntax:
+
+----
+stitch:tableName
+----
+
+with the following path and query parameters:
+
+=== Path Parameters (1 parameters):
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *tableName* | The name of the destination table the data is being pushed to. Table names must be unique in each destination schema, or loading issues will occur. Note: The number of characters in the table name should be within the destinations allowed limits or data will rejected. |  | String
+|===
+
+
+=== Query Parameters (8 parameters):
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *keyNames* (producer) | A collection of comma separated strings representing the Primary Key fields in the source table. Stitch use these Primary Keys to de-dupe data during loading If not provided, the table will be loaded in an append-only manner. |  | String
+| *lazyStartProducer* (producer) | Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and [...]
+| *region* (producer) | Stitch account region, e.g: europe. There are 2 enums and the value can be one of: NORTH_AMERICA, EUROPE | europe | StitchRegion
+| *stitchSchema* (producer) | *Autowired* A schema that describes the record(s) |  | StitchSchema
+| *connectionProvider* (producer) | *Autowired* ConnectionProvider contain configuration for the HttpClient like Maximum connection limit .. etc, you can inject this ConnectionProvider and the StitchClient will initialize HttpClient with this ConnectionProvider |  | ConnectionProvider
+| *httpClient* (producer) | *Autowired* Reactor Netty HttpClient, you can injected it if you want to have custom HttpClient |  | HttpClient
+| *stitchClient* (advanced) | *Autowired* Set a custom StitchClient that implements org.apache.camel.component.stitch.client.StitchClient interface |  | StitchClient
+| *token* (security) | *Required* Stitch access token for the Stitch Import API |  | String
+|===
+// endpoint options: END
+
+== Component Options
+// component options: START
+The Stitch component supports 10 options, which are listed below.
+
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *configuration* (producer) | The component configurations |  | StitchConfiguration
+| *keyNames* (producer) | A collection of comma separated strings representing the Primary Key fields in the source table. Stitch use these Primary Keys to de-dupe data during loading If not provided, the table will be loaded in an append-only manner. |  | String
+| *lazyStartProducer* (producer) | Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and [...]
+| *region* (producer) | Stitch account region, e.g: europe. There are 2 enums and the value can be one of: NORTH_AMERICA, EUROPE | europe | StitchRegion
+| *stitchSchema* (producer) | *Autowired* A schema that describes the record(s) |  | StitchSchema
+| *connectionProvider* (producer) | *Autowired* ConnectionProvider contain configuration for the HttpClient like Maximum connection limit .. etc, you can inject this ConnectionProvider and the StitchClient will initialize HttpClient with this ConnectionProvider |  | ConnectionProvider
+| *httpClient* (producer) | *Autowired* Reactor Netty HttpClient, you can injected it if you want to have custom HttpClient |  | HttpClient
+| *autowiredEnabled* (advanced) | Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc. | true | boolean
+| *stitchClient* (advanced) | *Autowired* Set a custom StitchClient that implements org.apache.camel.component.stitch.client.StitchClient interface |  | StitchClient
+| *token* (security) | *Required* Stitch access token for the Stitch Import API |  | String
+|===
+// component options: END
+
+
+== Async Producer
+
+This component implements the async Consumer and producer.
+
+This allows camel route to consume and produce events asynchronously without blocking any threads.
+
+== Usage
+
+=== Message headers evaluated by the component producer
+Before sending a message to Stitch component you can configure the following headers.
+
+[width="100%",cols="10%,10%,10%,70%",options="header",]
+|=======================================================================
+|Header |Variable Name |Type |Description
+
+|`CamelStitchTableName`| `StitchConstants.TABLE_NAME`|`String`| The name of the destination table the data is being pushed to. Table names must be unique in each destination schema, or loading issues will occur. Note: The number of characters in the table name should be within the destinations allowed limits or data will rejected.
+|`CamelStitchSchema`| `StitchConstants.SCHEMA`|`StitchSchema`| The schema that describes the Stitch message of type `org.apache.camel.component.stitch.client.models.StitchSchema`
+|`CamelStitchKeyNames`| `StitchConstants.KEY_NAMES`|`Collection<String>`| A collection of strings representing the Primary Key fields in the source table. Stitch use these Primary Keys to de-dupe data during loading If not provided, the table will be loaded in an append-only manner.
+|=======================================================================
+
+=== Message headers set by the component producer
+After the message is sent to Stitch, the following headers are available
+
+[width="100%",cols="10%,10%,10%,70%",options="header",]
+|=======================================================================
+|Header |Variable Name |Type |Description
+
+|`CamelStitchCode`| `StitchConstants.CODE`|`Integer`| HTTP Status code that is returned from Stitch Import HTTP API.
+|`CamelStitchHeaders`| `StitchConstants.HEADERS`|`Map`| HTTP headers that are returned from Stitch Import HTTP API.
+|`CamelStitchStatus`| `StitchConstants.STATUS`|`String`| The status message that Stitch returns after sending the data through Stitch Import API.
+|=======================================================================
+
+=== Message body type
+Currently, the component supports the following types for the body message on the producer side when producing a message to Stitch component:
+
+* `org.apache.camel.component.stitch.client.models.StitchRequestBody`: This represents this Stitch https://www.stitchdata.com/docs/developers/import-api/api#batch-data--arguments[JSON Message]. However,
+`StitchRequestBody` includes a type safe builder that helps on building the request body. Please note that, `tableName`, `keyNames` and `schema` options are no longer required if you send the
+data with `StitchRequestBody`, if you still set these options, they override whatever being set in message body `StitchRequestBody`.
+
+* `org.apache.camel.component.stitch.client.models.StitchMessage`: This represents https://www.stitchdata.com/docs/developers/import-api/api#message-object[this Stitch message structure].
+If you choose to send your message as `StitchMessage`, *you will need* to add `tableName`, `keyNames` and `schema` options to either the Exchange headers or through the endpoint options.
+
+* `Map`: You can also send the data as `Map`, the data structure must follow this https://www.stitchdata.com/docs/developers/import-api/api#batch-data--arguments[JSON Message] structure which is similar to
+`StitchRequestBody` but with drawback losing on all the type safety builder that is included with `StitchRequestBody`.
+
+* `Iterable`: You can send multiple Stitch messages that are aggregated by Camel or aggregated through custom processor. These aggregated messages
+can be type of `StitchMessage`, `StitchRequestBody` or `Map` but this Map here is similar to `StitchMessage`.
+
+
+
+=== Examples
+
+Here are list of examples of data that can be proceeded to Stitch:
+
+===== Input body type `org.apache.camel.component.stitch.client.models.StitchRequestBody`:
+[source,java]
+--------------------------------------------------------------------------------
+from("direct:sendStitch")
+     .process(exchange -> {
+         final StitchMessage stitchMessage = StitchMessage.builder()
+               .withData("field_1", "stitchMessage2-1")
+               .build();
+
+         final StitchRequestBody stitchRequestBody = StitchRequestBody.builder()
+                .addMessage(stitchMessage)
+                .withSchema(StitchSchema.builder().addKeyword("field_1", "string").build())
+                .withTableName("table_1")
+                .withKeyNames(Collections.singleton("field_1"))
+                .build();
+
+                exchange.getMessage().setBody(stitchRequestBody);
+     })
+.to("stitch:table_1?token=RAW({{token}})");
+--------------------------------------------------------------------------------
+
+
+===== Input body type `org.apache.camel.component.stitch.client.models.StitchMessage`:
+[source,java]
+--------------------------------------------------------------------------------
+from("direct:sendStitch")
+     .process(exchange -> {
+         exchange.getMessage().setHeader(StitchConstants.SCHEMA, StitchSchema.builder().addKeyword("field_1", "string").build());
+         exchange.getMessage().setHeader(StitchConstants.KEY_NAMES, Collections.singleton("field_1"));
+         exchange.getMessage().setHeader(StitchConstants.TABLE_NAME, "table_1");
+
+         final StitchMessage stitchMessage = StitchMessage.builder()
+               .withData("field_1", "stitchMessage2-1")
+               .build();
+
+                exchange.getMessage().setBody(stitchMessage);
+     })
+.to("stitch:table_1?token=RAW({{token}})");
+--------------------------------------------------------------------------------
+
+
+===== Input body type `Map`:
+[source,java]
+--------------------------------------------------------------------------------
+from("direct:sendStitch")
+     .process(exchange -> {
+        final Map<String, Object> properties = new LinkedHashMap<>();
+        properties.put("id", Collections.singletonMap("type", "integer"));
+        properties.put("name", Collections.singletonMap("type", "string"));
+        properties.put("age", Collections.singletonMap("type", "integer"));
+        properties.put("has_magic", Collections.singletonMap("type", "boolean"));
+
+        final Map<String, Object> data = new LinkedHashMap<>();
+        data.put(StitchRequestBody.TABLE_NAME, "my_table");
+        data.put(StitchRequestBody.SCHEMA, Collections.singletonMap("properties", properties));
+        data.put(StitchRequestBody.MESSAGES,
+                Collections.singletonList(Collections.singletonMap("data", Collections.singletonMap("id", 2))));
+        data.put(StitchRequestBody.KEY_NAMES, Collections.singletonList("test_key"));
+
+        exchange.getMessage().setBody(data);
+     })
+.to("stitch:table_1?token=RAW({{token}})");
+--------------------------------------------------------------------------------
+
+
+
+===== Input body type `Iterable`:
+[source,java]
+--------------------------------------------------------------------------------
+from("direct:sendStitch")
+     .process(exchange -> {
+         exchange.getMessage().setHeader(StitchConstants.SCHEMA, StitchSchema.builder().addKeyword("field_1", "string").build());
+         exchange.getMessage().setHeader(StitchConstants.KEY_NAMES, Collections.singleton("field_1"));
+         exchange.getMessage().setHeader(StitchConstants.TABLE_NAME, "table_1");
+
+        final StitchMessage stitchMessage1 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage1")
+                .build();
+
+        final StitchMessage stitchMessage2 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage2-1")
+                .build();
+
+        final StitchRequestBody stitchMessage2RequestBody = StitchRequestBody.builder()
+                .addMessage(stitchMessage2)
+                .withSchema(StitchSchema.builder().addKeyword("field_1", "integer").build())
+                .withTableName("table_1")
+                .withKeyNames(Collections.singleton("field_1"))
+                .build();
+
+        final Map<String, Object> stitchMessage3 = new LinkedHashMap<>();
+        stitchMessage3.put(StitchMessage.DATA, Collections.singletonMap("field_1", "stitchMessage3"));
+
+        final StitchMessage stitchMessage4 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage4")
+                .build();
+
+        final Exchange stitchMessage4Exchange = new DefaultExchange(context);
+        stitchMessage4Exchange.getMessage().setBody(stitchMessage4);
+
+        final StitchMessage stitchMessage5 = StitchMessage.builder()
+                .withData("field_1", "stitchMessage5")
+                .build();
+
+        final Message stitchMessage5Message = new DefaultExchange(context).getMessage();
+        stitchMessage5Message.setBody(stitchMessage5);
+
+        final List<Object> inputMessages = new LinkedList<>();
+        inputMessages.add(stitchMessage1);
+        inputMessages.add(stitchMessage2RequestBody);
+        inputMessages.add(stitchMessage3);
+        inputMessages.add(stitchMessage4Exchange);
+        inputMessages.add(stitchMessage5Message);
+
+        exchange.getMessage().setBody(inputMessages);
+     })
+.to("stitch:table_1?token=RAW({{token}})");
+--------------------------------------------------------------------------------
+
+=== Development Notes (Important)
+When developing on this component, you will need to obtain your Stitch token in order to run the integration tests. In addition to the mocked unit tests
+you *will need to run the integration tests with every change you make*
+To run the integration tests, on this component directory, run the following maven command:
+----
+mvn verify -PfullTests -Dtoken=stitchToken
+----
+Whereby `token` is your Stitch token that is generated for Stitch Import API integration.
+
+
+include::camel-spring-boot::page$stitch-starter.adoc[]
diff --git a/parent/pom.xml b/parent/pom.xml
index ecb9331..1494859 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -469,6 +469,7 @@
         <rdf4j-model-version>2.4.4</rdf4j-model-version>
         <reactive-streams-version>1.0.3</reactive-streams-version>
         <reactor-version>3.2.16.RELEASE</reactor-version>
+        <reactor-netty-version>1.0.2</reactor-netty-version>
         <redisson-version>3.14.0</redisson-version>
         <rescu-version>2.0.2</rescu-version>
         <resilience4j-version>1.6.1</resilience4j-version>
@@ -2407,6 +2408,11 @@
 			</dependency>
 			<dependency>
 				<groupId>org.apache.camel</groupId>
+				<artifactId>camel-stitch</artifactId>
+				<version>${project.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.camel</groupId>
 				<artifactId>camel-stomp</artifactId>
 				<version>${project.version}</version>
 			</dependency>