diff --git a/pulsar-client-api-v5/build.gradle.kts b/pulsar-client-api-v5/build.gradle.kts
new file mode 100644
index 0000000000000..fa77751849180
--- /dev/null
+++ b/pulsar-client-api-v5/build.gradle.kts
@@ -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.
+ */
+
+plugins {
+ id("pulsar.java-conventions")
+}
+
+dependencies {
+ compileOnly(libs.protobuf.java)
+ compileOnly(libs.opentelemetry.api)
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/Checkpoint.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/Checkpoint.java
new file mode 100644
index 0000000000000..6b24fe8943ddd
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/Checkpoint.java
@@ -0,0 +1,95 @@
+/*
+ * 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.pulsar.client.api.v5;
+
+import java.io.IOException;
+import java.time.Instant;
+import org.apache.pulsar.client.api.v5.internal.PulsarClientProvider;
+
+/**
+ * An opaque, serializable position vector representing a consistent point across all
+ * internal hash-range segments of a topic.
+ *
+ *
Checkpoints are created via {@link CheckpointConsumer#checkpoint()} and can be
+ * serialized for external storage (e.g. Flink state, S3) using {@link #toByteArray()}.
+ *
+ *
This is the sole position type used with {@link CheckpointConsumer} — for initial
+ * positioning use the static factories {@link #earliest()}, {@link #latest()},
+ * {@link #atTimestamp(Instant)}, or {@link #fromByteArray(byte[])} to restore from
+ * a previously saved checkpoint.
+ */
+public interface Checkpoint {
+
+ /**
+ * Serialize this checkpoint for external storage.
+ *
+ * @return a serializable byte representation of this checkpoint that can be restored
+ * via {@link #fromByteArray(byte[])}
+ */
+ byte[] toByteArray();
+
+ /**
+ * The time at which this checkpoint was created.
+ *
+ * @return the creation timestamp of this checkpoint as an {@link Instant}
+ */
+ Instant creationTime();
+
+ // --- Static factories ---
+
+ /**
+ * A sentinel checkpoint representing the beginning of the topic (oldest available data).
+ *
+ * @return a sentinel {@link Checkpoint} representing the earliest position in the topic
+ */
+ static Checkpoint earliest() {
+ return PulsarClientProvider.get().earliestCheckpoint();
+ }
+
+ /**
+ * A sentinel checkpoint representing the end of the topic (next message to be published).
+ *
+ * @return a sentinel {@link Checkpoint} representing the latest position in the topic
+ */
+ static Checkpoint latest() {
+ return PulsarClientProvider.get().latestCheckpoint();
+ }
+
+ /**
+ * A checkpoint that positions at the first message published at or after the given timestamp.
+ *
+ * @param timestamp the timestamp to position at
+ * @return a {@link Checkpoint} that will start consuming from the first message at or after
+ * the given timestamp
+ */
+ static Checkpoint atTimestamp(Instant timestamp) {
+ return PulsarClientProvider.get().checkpointAtTimestamp(timestamp);
+ }
+
+ /**
+ * Deserialize a checkpoint from a byte array previously obtained via {@link #toByteArray()}.
+ *
+ * @param data the byte array previously obtained from {@link #toByteArray()}
+ * @return the deserialized {@link Checkpoint}
+ * @throws IOException if the byte array is malformed or cannot be deserialized
+ */
+ static Checkpoint fromByteArray(byte[] data) throws IOException {
+ return PulsarClientProvider.get().checkpointFromBytes(data);
+ }
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/CheckpointConsumer.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/CheckpointConsumer.java
new file mode 100644
index 0000000000000..0d278fb3eb56e
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/CheckpointConsumer.java
@@ -0,0 +1,123 @@
+/*
+ * 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.pulsar.client.api.v5;
+
+import java.io.Closeable;
+import java.time.Duration;
+import org.apache.pulsar.client.api.v5.async.AsyncCheckpointConsumer;
+
+/**
+ * An unmanaged consumer designed for connector frameworks (Flink, Spark, etc.).
+ *
+ *
Unlike {@link StreamConsumer} and {@link QueueConsumer}, this consumer has no
+ * broker-managed subscription — position tracking is entirely external. The connector
+ * framework stores checkpoints in its own state backend and uses them to restore
+ * on failure.
+ *
+ *
Internally, the consumer reads from all hash-range segments of a topic.
+ * {@link #checkpoint()} creates an atomic snapshot of positions across all segments,
+ * returned as an opaque {@link Checkpoint} that can be serialized and stored externally.
+ *
+ *
This interface provides synchronous (blocking) operations. For non-blocking
+ * usage, obtain an {@link AsyncCheckpointConsumer} via {@link #async()}.
+ *
+ * @param the type of message values
+ */
+public interface CheckpointConsumer extends Closeable {
+
+ /**
+ * The topic this consumer reads from.
+ *
+ * @return the fully qualified topic name
+ */
+ String topic();
+
+ // --- Receive ---
+
+ /**
+ * Receive a single message, blocking indefinitely.
+ *
+ * @return the received {@link Message}
+ * @throws PulsarClientException if the consumer is closed or a connection error occurs
+ */
+ Message receive() throws PulsarClientException;
+
+ /**
+ * Receive a single message, blocking up to the given timeout.
+ * Returns {@code null} if the timeout elapses without a message.
+ *
+ * @param timeout the maximum time to wait for a message
+ * @return the received {@link Message}, or {@code null} if the timeout elapses
+ * @throws PulsarClientException if the consumer is closed or a connection error occurs
+ */
+ Message receive(Duration timeout) throws PulsarClientException;
+
+ /**
+ * Receive a batch of messages, blocking up to the given timeout.
+ *
+ * @param maxMessages the maximum number of messages to return
+ * @param timeout the maximum time to wait for messages
+ * @return the received {@link Messages} batch
+ * @throws PulsarClientException if the consumer is closed or a connection error occurs
+ */
+ Messages receiveMulti(int maxMessages, Duration timeout) throws PulsarClientException;
+
+ // --- Checkpoint ---
+
+ /**
+ * Create a consistent checkpoint — an atomic snapshot of positions across all
+ * internal hash-range segments.
+ *
+ * The returned {@link Checkpoint} can be serialized via {@link Checkpoint#toByteArray()}
+ * and stored in the connector framework's state backend.
+ *
+ * @return an opaque {@link Checkpoint} representing the current read positions
+ */
+ Checkpoint checkpoint();
+
+ // --- Seek ---
+
+ /**
+ * Seek to a previously saved checkpoint, or to a sentinel position such as
+ * {@link Checkpoint#earliest()} or {@link Checkpoint#latest()}.
+ *
+ * @param checkpoint the checkpoint to seek to
+ * @throws PulsarClientException if the seek fails or a connection error occurs
+ */
+ void seek(Checkpoint checkpoint) throws PulsarClientException;
+
+ // --- Async ---
+
+ /**
+ * Return the asynchronous view of this consumer.
+ *
+ * @return the {@link AsyncCheckpointConsumer} counterpart of this consumer
+ */
+ AsyncCheckpointConsumer async();
+
+ // --- Lifecycle ---
+
+ /**
+ * Close the consumer and release all resources.
+ *
+ * @throws PulsarClientException if an error occurs while closing the consumer
+ */
+ @Override
+ void close() throws PulsarClientException;
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/CheckpointConsumerBuilder.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/CheckpointConsumerBuilder.java
new file mode 100644
index 0000000000000..615473a382c45
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/CheckpointConsumerBuilder.java
@@ -0,0 +1,113 @@
+/*
+ * 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.pulsar.client.api.v5;
+
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import org.apache.pulsar.client.api.v5.config.EncryptionPolicy;
+
+/**
+ * Builder for configuring and creating a {@link CheckpointConsumer}.
+ *
+ * Since this is an unmanaged consumer (no subscription), the terminal method is
+ * {@link #create()} rather than {@code subscribe()}.
+ *
+ * @param the type of message values the consumer will receive
+ */
+public interface CheckpointConsumerBuilder {
+
+ /**
+ * Create the checkpoint consumer, blocking until it is ready.
+ *
+ * @return the created {@link CheckpointConsumer}
+ * @throws PulsarClientException if the creation fails or a connection error occurs
+ */
+ CheckpointConsumer create() throws PulsarClientException;
+
+ /**
+ * Create the checkpoint consumer asynchronously.
+ *
+ * @return a {@link CompletableFuture} that completes with the created {@link CheckpointConsumer}
+ */
+ CompletableFuture> createAsync();
+
+ // --- Required ---
+
+ /**
+ * The topic to consume from.
+ *
+ * @param topicName the topic name
+ * @return this builder instance for chaining
+ */
+ CheckpointConsumerBuilder topic(String topicName);
+
+ // --- Start position ---
+
+ /**
+ * Set the initial position for this consumer.
+ *
+ * Use {@link Checkpoint#earliest()}, {@link Checkpoint#latest()},
+ * {@link Checkpoint#atTimestamp}, or {@link Checkpoint#fromByteArray} to
+ * create the appropriate starting position.
+ *
+ *
Defaults to {@link Checkpoint#latest()} if not specified.
+ *
+ * @param checkpoint the checkpoint representing the desired start position
+ * @return this builder instance for chaining
+ */
+ CheckpointConsumerBuilder startPosition(Checkpoint checkpoint);
+
+ // --- Optional ---
+
+ /**
+ * A custom name for this consumer instance.
+ *
+ * @param name the consumer name
+ * @return this builder instance for chaining
+ */
+ CheckpointConsumerBuilder consumerName(String name);
+
+ /**
+ * Configure end-to-end message encryption for decryption.
+ *
+ * @param policy the encryption policy to use
+ * @return this builder instance for chaining
+ * @see EncryptionPolicy#forConsumer
+ */
+ CheckpointConsumerBuilder encryptionPolicy(EncryptionPolicy policy);
+
+ // --- Metadata ---
+
+ /**
+ * Add a single property to the consumer metadata.
+ *
+ * @param key the property key
+ * @param value the property value
+ * @return this builder instance for chaining
+ */
+ CheckpointConsumerBuilder property(String key, String value);
+
+ /**
+ * Add multiple properties to the consumer metadata.
+ *
+ * @param properties the properties to add
+ * @return this builder instance for chaining
+ */
+ CheckpointConsumerBuilder properties(Map properties);
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/Message.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/Message.java
new file mode 100644
index 0000000000000..e7bfad0d49a9e
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/Message.java
@@ -0,0 +1,123 @@
+/*
+ * 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.pulsar.client.api.v5;
+
+import java.time.Instant;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * An immutable message received from a Pulsar topic.
+ *
+ * @param the type of the deserialized message value
+ */
+public interface Message {
+
+ /**
+ * The deserialized value of the message according to the schema.
+ *
+ * @return the deserialized message value
+ */
+ T value();
+
+ /**
+ * The raw bytes of the message payload.
+ *
+ * @return the raw payload as a byte array
+ */
+ byte[] data();
+
+ /**
+ * The unique identifier of this message within the topic.
+ *
+ * @return the {@link MessageId} of this message
+ */
+ MessageId id();
+
+ /**
+ * The message key, used for per-key ordering.
+ *
+ * @return an {@link Optional} containing the message key, or empty if no key was set
+ */
+ Optional key();
+
+ /**
+ * Application-defined properties attached to the message.
+ *
+ * @return an unmodifiable map of property key-value pairs
+ */
+ Map properties();
+
+ /**
+ * The timestamp when the message was published by the broker.
+ *
+ * @return the publish timestamp as an {@link Instant}
+ */
+ Instant publishTime();
+
+ /**
+ * The event time set by the producer, if any.
+ *
+ * @return an {@link Optional} containing the event time, or empty if not set by the producer
+ */
+ Optional eventTime();
+
+ /**
+ * The producer-assigned sequence ID for deduplication.
+ *
+ * @return the sequence ID of this message
+ */
+ long sequenceId();
+
+ /**
+ * The name of the producer that published this message.
+ *
+ * @return an {@link Optional} containing the producer name, or empty if not available
+ */
+ Optional producerName();
+
+ /**
+ * The topic this message was published to.
+ *
+ * @return the fully qualified topic name
+ */
+ String topic();
+
+ /**
+ * The number of times the broker has redelivered this message.
+ *
+ * @return the redelivery count, starting at 0 for the first delivery
+ */
+ int redeliveryCount();
+
+ /**
+ * The uncompressed size of the message payload in bytes.
+ *
+ * @return the payload size in bytes
+ */
+ int size();
+
+ /**
+ * The cluster from which this message was replicated, if applicable.
+ *
+ * @return an {@link Optional} containing the source cluster name, or empty if the message
+ * is not replicated
+ */
+ Optional replicatedFrom();
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/MessageBuilder.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/MessageBuilder.java
new file mode 100644
index 0000000000000..e657edf788ad6
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/MessageBuilder.java
@@ -0,0 +1,39 @@
+/*
+ * 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.pulsar.client.api.v5;
+
+/**
+ * Synchronous message builder, obtained from {@link Producer#newMessage()}.
+ *
+ * Inherits all metadata setters from {@link MessageMetadata} and adds a
+ * blocking {@link #send()} terminal operation.
+ *
+ * @param the type of the message value
+ */
+public interface MessageBuilder extends MessageMetadata> {
+
+ /**
+ * Send the message synchronously and return its message ID.
+ *
+ * @return the {@link MessageId} assigned to the published message by the broker
+ * @throws PulsarClientException if the message could not be sent (e.g., connection failure,
+ * send timeout, or topic authorization error)
+ */
+ MessageId send() throws PulsarClientException;
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/MessageId.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/MessageId.java
new file mode 100644
index 0000000000000..fc539e0266dc9
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/MessageId.java
@@ -0,0 +1,68 @@
+/*
+ * 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.pulsar.client.api.v5;
+
+import java.io.IOException;
+import org.apache.pulsar.client.api.v5.internal.PulsarClientProvider;
+
+/**
+ * Opaque, immutable identifier for a message within a topic.
+ *
+ * No internal structure (ledger ID, entry ID, partition index) is exposed.
+ * Message IDs can be serialized to bytes for external storage and restored later.
+ */
+public interface MessageId extends Comparable {
+
+ /**
+ * Serialize this message ID to a byte array for external storage.
+ *
+ * @return a byte array representation of this message ID that can be restored
+ * via {@link #fromByteArray(byte[])}
+ */
+ byte[] toByteArray();
+
+ /**
+ * Deserialize a message ID from bytes previously produced by {@link #toByteArray()}.
+ *
+ * @param data the byte array previously obtained from {@link #toByteArray()}
+ * @return the deserialized {@link MessageId}
+ * @throws IOException if the byte array is malformed or cannot be deserialized
+ */
+ static MessageId fromByteArray(byte[] data) throws IOException {
+ return PulsarClientProvider.get().messageIdFromBytes(data);
+ }
+
+ /**
+ * Sentinel representing the oldest available message in the topic.
+ *
+ * @return a sentinel {@link MessageId} representing the earliest position in the topic
+ */
+ static MessageId earliest() {
+ return PulsarClientProvider.get().earliestMessageId();
+ }
+
+ /**
+ * Sentinel representing the next message to be published (i.e., the end of the topic).
+ *
+ * @return a sentinel {@link MessageId} representing the latest position in the topic
+ */
+ static MessageId latest() {
+ return PulsarClientProvider.get().latestMessageId();
+ }
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/MessageMetadata.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/MessageMetadata.java
new file mode 100644
index 0000000000000..2de48c59ce9f6
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/MessageMetadata.java
@@ -0,0 +1,120 @@
+/*
+ * 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.pulsar.client.api.v5;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Common message metadata that can be set on any outgoing message.
+ *
+ * This is the shared base for {@link MessageBuilder} (sync) and
+ * {@link org.apache.pulsar.client.api.v5.async.AsyncMessageBuilder AsyncMessageBuilder} (async).
+ * The self-referential type parameter {@code BuilderT} enables fluent chaining
+ * on both subtypes.
+ *
+ * @param the type of the message value
+ * @param the concrete builder type (for fluent returns)
+ */
+public interface MessageMetadata> {
+
+ /**
+ * Set the message value.
+ *
+ * @param value the message payload to be serialized using the producer's schema
+ * @return this builder instance for chaining
+ */
+ BuilderT value(T value);
+
+ /**
+ * Set the message key. Messages with the same key are guaranteed to be delivered
+ * in order to stream consumers. Queue consumers may use the key for routing.
+ *
+ * @param key the message key used for ordering and routing
+ * @return this builder instance for chaining
+ */
+ BuilderT key(String key);
+
+ /**
+ * Associate this message with a transaction.
+ *
+ * @param txn the transaction to associate with this message
+ * @return this builder instance for chaining
+ */
+ BuilderT transaction(Transaction txn);
+
+ /**
+ * Add a single property to the message.
+ *
+ * @param name the property key
+ * @param value the property value
+ * @return this builder instance for chaining
+ */
+ BuilderT property(String name, String value);
+
+ /**
+ * Add multiple properties to the message.
+ *
+ * @param properties a map of property key-value pairs to attach to the message
+ * @return this builder instance for chaining
+ */
+ BuilderT properties(Map properties);
+
+ /**
+ * Set the event time of the message.
+ *
+ * @param eventTime the application-defined event time for the message
+ * @return this builder instance for chaining
+ */
+ BuilderT eventTime(Instant eventTime);
+
+ /**
+ * Set the sequence ID for producer deduplication.
+ *
+ * @param sequenceId the sequence ID to assign to the message for deduplication purposes
+ * @return this builder instance for chaining
+ */
+ BuilderT sequenceId(long sequenceId);
+
+ /**
+ * Request delayed delivery: the message becomes visible to consumers after the given delay.
+ *
+ * @param delay the duration to wait before the message becomes visible to consumers
+ * @return this builder instance for chaining
+ */
+ BuilderT deliverAfter(Duration delay);
+
+ /**
+ * Request delayed delivery: the message becomes visible to consumers at the given time.
+ *
+ * @param timestamp the absolute time at which the message becomes visible to consumers
+ * @return this builder instance for chaining
+ */
+ BuilderT deliverAt(Instant timestamp);
+
+ /**
+ * Restrict geo-replication to the specified clusters only.
+ *
+ * @param clusters the list of cluster names to which this message should be replicated
+ * @return this builder instance for chaining
+ */
+ BuilderT replicationClusters(List clusters);
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/Messages.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/Messages.java
new file mode 100644
index 0000000000000..4fcb5df17e373
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/Messages.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.pulsar.client.api.v5;
+
+/**
+ * A batch of messages received from a consumer.
+ *
+ * @param the type of the deserialized message value
+ */
+public interface Messages extends Iterable> {
+
+ /**
+ * The number of messages in this batch.
+ *
+ * @return the number of messages in this batch
+ */
+ int count();
+
+ /**
+ * The last message id in this batch.
+ *
+ * @return the {@link MessageId} of the last message in this batch
+ */
+ MessageId lastId();
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/Producer.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/Producer.java
new file mode 100644
index 0000000000000..5a49eac7d71cf
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/Producer.java
@@ -0,0 +1,80 @@
+/*
+ * 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.pulsar.client.api.v5;
+
+import org.apache.pulsar.client.api.v5.async.AsyncProducer;
+
+/**
+ * A producer publishes messages to a Pulsar topic.
+ *
+ * This interface provides synchronous (blocking) operations. For non-blocking
+ * usage, obtain an {@link AsyncProducer} via {@link #async()}.
+ *
+ * @param the type of message values this producer sends
+ */
+public interface Producer extends AutoCloseable {
+
+ /**
+ * The topic this producer is attached to.
+ *
+ * @return the fully qualified topic name (e.g. {@code topic://tenant/namespace/my-topic})
+ */
+ String topic();
+
+ /**
+ * The name of this producer (system-assigned or user-specified via
+ * {@link ProducerBuilder#producerName(String)}).
+ *
+ * @return the producer name, never {@code null}
+ */
+ String producerName();
+
+ /**
+ * Create a message builder for advanced message construction (key, properties, etc.).
+ * Use {@link MessageBuilder#send()} as the terminal operation.
+ *
+ * @return a new {@link MessageBuilder} instance bound to this producer
+ */
+ MessageBuilder newMessage();
+
+ /**
+ * The last sequence ID published by this producer. Used for deduplication tracking.
+ * Returns {@code -1} if no message has been published yet.
+ *
+ * @return the last published sequence ID, or {@code -1} if none
+ */
+ long lastSequenceId();
+
+ /**
+ * Return the asynchronous view of this producer. The returned object shares the same
+ * underlying connection and resources.
+ *
+ * @return the {@link AsyncProducer} counterpart of this producer
+ */
+ AsyncProducer async();
+
+ /**
+ * Close this producer and release all associated resources. Pending send operations
+ * are completed before the producer is closed.
+ *
+ * @throws PulsarClientException if an error occurs while closing
+ */
+ @Override
+ void close() throws PulsarClientException;
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/ProducerBuilder.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/ProducerBuilder.java
new file mode 100644
index 0000000000000..65800756c19f0
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/ProducerBuilder.java
@@ -0,0 +1,170 @@
+/*
+ * 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.pulsar.client.api.v5;
+
+import java.time.Duration;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import org.apache.pulsar.client.api.v5.config.BatchingPolicy;
+import org.apache.pulsar.client.api.v5.config.ChunkingPolicy;
+import org.apache.pulsar.client.api.v5.config.CompressionPolicy;
+import org.apache.pulsar.client.api.v5.config.EncryptionPolicy;
+import org.apache.pulsar.client.api.v5.config.ProducerAccessMode;
+
+/**
+ * Builder for configuring and creating a {@link Producer}.
+ *
+ * @param the type of message values the producer will send
+ */
+public interface ProducerBuilder {
+
+ /**
+ * Create the producer, blocking until it is ready.
+ *
+ * @return the configured {@link Producer} instance
+ * @throws PulsarClientException if the producer cannot be created (e.g. topic does not exist,
+ * authorization failure, or connection error)
+ */
+ Producer create() throws PulsarClientException;
+
+ /**
+ * Create the producer asynchronously.
+ *
+ * @return a {@link CompletableFuture} that completes with the configured {@link Producer},
+ * or completes exceptionally with {@link PulsarClientException} on failure
+ */
+ CompletableFuture> createAsync();
+
+ // --- Required ---
+
+ /**
+ * The topic to produce to. This is required and must be set before calling {@link #create()}.
+ *
+ * @param topicName the fully qualified topic name (e.g. {@code topic://tenant/namespace/my-topic})
+ * @return this builder instance for chaining
+ */
+ ProducerBuilder topic(String topicName);
+
+ // --- Optional ---
+
+ /**
+ * Set a custom producer name. If not set, the broker assigns a unique name.
+ * The producer name is used for message deduplication and appears in broker logs.
+ *
+ * @param producerName the producer name
+ * @return this builder instance for chaining
+ */
+ ProducerBuilder producerName(String producerName);
+
+ /**
+ * Access mode for this producer on the topic.
+ *
+ * @param accessMode the access mode (e.g. {@link ProducerAccessMode#SHARED},
+ * {@link ProducerAccessMode#EXCLUSIVE})
+ * @return this builder instance for chaining
+ */
+ ProducerBuilder accessMode(ProducerAccessMode accessMode);
+
+ /**
+ * Timeout for a send operation. If the message is not acknowledged by the broker within
+ * this duration, the send future completes exceptionally. A value of {@link Duration#ZERO}
+ * disables the timeout.
+ *
+ * @param timeout the send timeout duration
+ * @return this builder instance for chaining
+ */
+ ProducerBuilder sendTimeout(Duration timeout);
+
+ /**
+ * Whether the producer should block when the pending message queue is full,
+ * rather than failing immediately. Default is {@code true}.
+ *
+ * @param blockIfQueueFull {@code true} to block, {@code false} to fail immediately
+ * @return this builder instance for chaining
+ */
+ ProducerBuilder blockIfQueueFull(boolean blockIfQueueFull);
+
+ /**
+ * Configure compression for message payloads.
+ *
+ * @param policy the compression policy
+ * @return this builder instance for chaining
+ * @see CompressionPolicy#of(org.apache.pulsar.client.api.v5.config.CompressionType)
+ * @see CompressionPolicy#disabled()
+ */
+ ProducerBuilder compressionPolicy(CompressionPolicy policy);
+
+ /**
+ * Configure message batching. When enabled, the producer groups multiple messages
+ * into a single broker request to improve throughput.
+ *
+ * @param policy the batching policy
+ * @return this builder instance for chaining
+ * @see BatchingPolicy#ofDefault()
+ * @see BatchingPolicy#ofDisabled()
+ * @see BatchingPolicy#of(Duration, int, int)
+ */
+ ProducerBuilder batchingPolicy(BatchingPolicy policy);
+
+ /**
+ * Enable chunking for large messages that exceed the broker's max message size.
+ *
+ * @param policy the chunking policy
+ * @return this builder instance for chaining
+ */
+ ProducerBuilder chunkingPolicy(ChunkingPolicy policy);
+
+ /**
+ * Configure end-to-end message encryption.
+ *
+ * @param policy the encryption policy for producing encrypted messages
+ * @return this builder instance for chaining
+ * @see EncryptionPolicy#forProducer(org.apache.pulsar.client.api.v5.auth.CryptoKeyReader, String...)
+ */
+ ProducerBuilder encryptionPolicy(EncryptionPolicy policy);
+
+ /**
+ * Set the initial sequence ID for producer message deduplication. Subsequent messages
+ * are assigned incrementing sequence IDs starting from this value.
+ *
+ * @param initialSequenceId the starting sequence ID
+ * @return this builder instance for chaining
+ */
+ ProducerBuilder initialSequenceId(long initialSequenceId);
+
+ // --- Metadata ---
+
+ /**
+ * Add a single property to the producer metadata. Properties are sent to the broker
+ * and can be used for filtering and identification.
+ *
+ * @param key the property key
+ * @param value the property value
+ * @return this builder instance for chaining
+ */
+ ProducerBuilder property(String key, String value);
+
+ /**
+ * Add multiple properties to the producer metadata.
+ *
+ * @param properties a map of property key-value pairs
+ * @return this builder instance for chaining
+ */
+ ProducerBuilder properties(Map properties);
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/PulsarClient.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/PulsarClient.java
new file mode 100644
index 0000000000000..51bf5748c6dc3
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/PulsarClient.java
@@ -0,0 +1,130 @@
+/*
+ * 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.pulsar.client.api.v5;
+
+import java.util.concurrent.CompletableFuture;
+import org.apache.pulsar.client.api.v5.internal.PulsarClientProvider;
+import org.apache.pulsar.client.api.v5.schema.Schema;
+
+/**
+ * Entry point for the Pulsar client. Provides factory methods for creating producers,
+ * consumers, and transactions.
+ *
+ * Instances are created via {@link #builder()}.
+ *
+ *
A {@code PulsarClient} manages internal resources such as connections, threads,
+ * and memory buffers. It must be closed when no longer needed.
+ */
+public interface PulsarClient extends AutoCloseable {
+
+ /**
+ * Create a new client builder.
+ *
+ * @return a new {@link PulsarClientBuilder} for configuring the client
+ */
+ static PulsarClientBuilder builder() {
+ return PulsarClientProvider.get().newClientBuilder();
+ }
+
+ /**
+ * Create a producer builder with a specific schema.
+ *
+ * @param the message value type
+ * @param schema the schema used for serialization/deserialization
+ * @return a new {@link ProducerBuilder} for configuring the producer
+ */
+ ProducerBuilder newProducer(Schema schema);
+
+ /**
+ * Create a stream consumer builder with a specific schema.
+ *
+ * @param the message value type
+ * @param schema the schema used for serialization/deserialization
+ * @return a new {@link StreamConsumerBuilder} for configuring the stream consumer
+ */
+ StreamConsumerBuilder newStreamConsumer(Schema schema);
+
+ /**
+ * Create a queue consumer builder with a specific schema.
+ *
+ * @param the message value type
+ * @param schema the schema used for serialization/deserialization
+ * @return a new {@link QueueConsumerBuilder} for configuring the queue consumer
+ */
+ QueueConsumerBuilder newQueueConsumer(Schema schema);
+
+ /**
+ * Create a checkpoint consumer builder with a specific schema.
+ *
+ * Checkpoint consumers are unmanaged — position tracking is external.
+ * Designed for connector frameworks (Flink, Spark) that manage their own state.
+ *
+ * @param the message value type
+ * @param schema the schema used for serialization/deserialization
+ * @return a new {@link CheckpointConsumerBuilder} for configuring the checkpoint consumer
+ */
+ CheckpointConsumerBuilder newCheckpointConsumer(Schema schema);
+
+ // --- Transactions ---
+
+ /**
+ * Create a new transaction, blocking until it is ready. The transaction timeout is taken
+ * from the client-wide {@link org.apache.pulsar.client.api.v5.config.TransactionPolicy}
+ * configured on {@link PulsarClientBuilder#transactionPolicy}.
+ *
+ * @return a new {@link Transaction} in the {@link Transaction.State#OPEN} state
+ * @throws PulsarClientException if the transaction cannot be created (e.g., transaction
+ * coordinator unavailable or the client is closed)
+ */
+ Transaction newTransaction() throws PulsarClientException;
+
+ /**
+ * Asynchronous counterpart of {@link #newTransaction()}.
+ *
+ * @return a {@link CompletableFuture} that completes with a new {@link Transaction} in the
+ * {@link Transaction.State#OPEN} state, or completes exceptionally with
+ * {@link PulsarClientException} on failure
+ */
+ CompletableFuture newTransactionAsync();
+
+ // --- Lifecycle ---
+
+ /**
+ * Close the client and release all resources, waiting for pending operations to complete.
+ *
+ * @throws PulsarClientException if an error occurs while closing the client
+ */
+ @Override
+ void close() throws PulsarClientException;
+
+ /**
+ * Asynchronous counterpart of {@link #close()}.
+ *
+ * @return a {@link CompletableFuture} that completes when the client has finished closing,
+ * or completes exceptionally with {@link PulsarClientException} on failure
+ */
+ CompletableFuture closeAsync();
+
+ /**
+ * Shutdown the client instance.
+ *
+ * Release all resources used by the client, without waiting for pending operations to complete.
+ */
+ void shutdown();
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/PulsarClientBuilder.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/PulsarClientBuilder.java
new file mode 100644
index 0000000000000..101355e1182c2
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/PulsarClientBuilder.java
@@ -0,0 +1,156 @@
+/*
+ * 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.pulsar.client.api.v5;
+
+import io.opentelemetry.api.OpenTelemetry;
+import java.time.Duration;
+import org.apache.pulsar.client.api.v5.auth.Authentication;
+import org.apache.pulsar.client.api.v5.config.ConnectionPolicy;
+import org.apache.pulsar.client.api.v5.config.MemorySize;
+import org.apache.pulsar.client.api.v5.config.TlsPolicy;
+import org.apache.pulsar.client.api.v5.config.TransactionPolicy;
+
+/**
+ * Builder for configuring and creating a {@link PulsarClient}.
+ */
+public interface PulsarClientBuilder {
+
+ /**
+ * Build and return the configured client.
+ *
+ * @return the configured {@link PulsarClient} instance
+ * @throws PulsarClientException if the client cannot be created (e.g., invalid configuration
+ * or connection failure)
+ */
+ PulsarClient build() throws PulsarClientException;
+
+ /**
+ * Set the Pulsar service URL (e.g., {@code pulsar://localhost:6650}).
+ *
+ * @param serviceUrl the Pulsar service URL to connect to
+ * @return this builder instance for chaining
+ */
+ PulsarClientBuilder serviceUrl(String serviceUrl);
+
+ /**
+ * Set the authentication provider.
+ *
+ * @param authentication the authentication provider to use for connecting to the broker
+ * @return this builder instance for chaining
+ */
+ PulsarClientBuilder authentication(Authentication authentication);
+
+ /**
+ * Set authentication by plugin class name and parameter string.
+ *
+ * @param authPluginClassName the fully qualified class name of the authentication plugin
+ * @param authParamsString the authentication parameters as a serialized string
+ * @return this builder instance for chaining
+ * @throws PulsarClientException if the authentication plugin cannot be loaded or configured
+ */
+ PulsarClientBuilder authentication(String authPluginClassName, String authParamsString)
+ throws PulsarClientException;
+
+ /**
+ * Timeout for client operations (e.g., creating producers/consumers).
+ *
+ * @param timeout the maximum duration to wait for an operation to complete
+ * @return this builder instance for chaining
+ */
+ PulsarClientBuilder operationTimeout(Duration timeout);
+
+ /**
+ * Configure connection-level settings such as timeouts, pool size, threading,
+ * keep-alive, and proxy configuration.
+ *
+ * @param policy the connection policy
+ * @return this builder instance for chaining
+ * @see ConnectionPolicy#builder()
+ */
+ PulsarClientBuilder connectionPolicy(ConnectionPolicy policy);
+
+ /**
+ * Set the transaction policy.
+ *
+ * @param policy the transaction policy controlling transaction behavior and timeouts
+ * @return this builder instance for chaining
+ */
+ PulsarClientBuilder transactionPolicy(TransactionPolicy policy);
+
+ /**
+ * Configure TLS for the client connection.
+ *
+ * @param policy the TLS policy to apply to broker connections
+ * @return this builder instance for chaining
+ * @see TlsPolicy#of(String)
+ * @see TlsPolicy#ofMutualTls(String, String, String)
+ * @see TlsPolicy#ofInsecure()
+ */
+ PulsarClientBuilder tlsPolicy(TlsPolicy policy);
+
+ /**
+ * Provide a custom {@link OpenTelemetry} instance for metrics and tracing.
+ *
+ *
If not set, the client creates its own internal instance that exports metrics
+ * (via a Prometheus-compatible endpoint) with tracing disabled.
+ *
+ *
When a custom instance is provided, the client uses whatever {@code MeterProvider}
+ * and {@code TracerProvider} it contains. This means:
+ *
+ * - To keep metrics only (no tracing), configure the instance with a
+ * {@code MeterProvider} and leave the {@code TracerProvider} as no-op.
+ * - To enable distributed tracing, configure the instance with both a
+ * {@code MeterProvider} and a {@code TracerProvider}.
+ * - To disable all telemetry, pass {@link OpenTelemetry#noop()}.
+ *
+ *
+ * @param openTelemetry the OpenTelemetry instance to use
+ * @return this builder instance for chaining
+ */
+ PulsarClientBuilder openTelemetry(OpenTelemetry openTelemetry);
+
+ /**
+ * Maximum amount of direct memory the client can use for pending messages.
+ *
+ * @param size the memory limit for pending messages across all producers
+ * @return this builder instance for chaining
+ * @see MemorySize#ofMegabytes(long)
+ * @see MemorySize#ofGigabytes(long)
+ */
+ PulsarClientBuilder memoryLimit(MemorySize size);
+
+ // --- Misc ---
+
+ /**
+ * Set the listener name for multi-listener brokers.
+ *
+ * @param name the listener name to use when connecting to brokers that advertise
+ * multiple listener endpoints
+ * @return this builder instance for chaining
+ */
+ PulsarClientBuilder listenerName(String name);
+
+ /**
+ * A human-readable description of this client (for logging and debugging).
+ *
+ * @param description a descriptive label for this client instance
+ * @return this builder instance for chaining
+ */
+ PulsarClientBuilder description(String description);
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/PulsarClientException.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/PulsarClientException.java
new file mode 100644
index 0000000000000..0e6005cec3a82
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/PulsarClientException.java
@@ -0,0 +1,161 @@
+/*
+ * 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.pulsar.client.api.v5;
+
+import java.io.IOException;
+
+/**
+ * Base exception for all Pulsar client operations.
+ */
+public class PulsarClientException extends IOException {
+
+ public PulsarClientException(String message) {
+ super(message);
+ }
+
+ public PulsarClientException(Throwable cause) {
+ super(cause);
+ }
+
+ public PulsarClientException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public static final class InvalidServiceURLException extends PulsarClientException {
+ public InvalidServiceURLException(String message) {
+ super(message);
+ }
+
+ public InvalidServiceURLException(Throwable cause) {
+ super(cause);
+ }
+ }
+
+ public static final class InvalidConfigurationException extends PulsarClientException {
+ public InvalidConfigurationException(String message) {
+ super(message);
+ }
+
+ public InvalidConfigurationException(Throwable cause) {
+ super(cause);
+ }
+ }
+
+ public static final class NotFoundException extends PulsarClientException {
+ public NotFoundException(String message) {
+ super(message);
+ }
+ }
+
+ public static final class TimeoutException extends PulsarClientException {
+ public TimeoutException(String message) {
+ super(message);
+ }
+
+ public TimeoutException(Throwable cause) {
+ super(cause);
+ }
+ }
+
+ public static final class AlreadyClosedException extends PulsarClientException {
+ public AlreadyClosedException(String message) {
+ super(message);
+ }
+ }
+
+ public static final class AuthenticationException extends PulsarClientException {
+ public AuthenticationException(String message) {
+ super(message);
+ }
+ }
+
+ public static final class AuthorizationException extends PulsarClientException {
+ public AuthorizationException(String message) {
+ super(message);
+ }
+ }
+
+ public static final class ConnectException extends PulsarClientException {
+ public ConnectException(String message) {
+ super(message);
+ }
+
+ public ConnectException(Throwable cause) {
+ super(cause);
+ }
+ }
+
+ public static final class ProducerBusyException extends PulsarClientException {
+ public ProducerBusyException(String message) {
+ super(message);
+ }
+ }
+
+ public static final class ConsumerBusyException extends PulsarClientException {
+ public ConsumerBusyException(String message) {
+ super(message);
+ }
+ }
+
+ public static final class ProducerQueueIsFullException extends PulsarClientException {
+ public ProducerQueueIsFullException(String message) {
+ super(message);
+ }
+ }
+
+ public static final class IncompatibleSchemaException extends PulsarClientException {
+ public IncompatibleSchemaException(String message) {
+ super(message);
+ }
+ }
+
+ public static final class TopicTerminatedException extends PulsarClientException {
+ public TopicTerminatedException(String message) {
+ super(message);
+ }
+ }
+
+ public static final class CryptoException extends PulsarClientException {
+ public CryptoException(String message) {
+ super(message);
+ }
+ }
+
+ public static final class TransactionConflictException extends PulsarClientException {
+ public TransactionConflictException(String message) {
+ super(message);
+ }
+ }
+
+ public static final class NotConnectedException extends PulsarClientException {
+ public NotConnectedException() {
+ super("Not connected");
+ }
+
+ public NotConnectedException(String message) {
+ super(message);
+ }
+ }
+
+ public static final class MemoryBufferIsFullException extends PulsarClientException {
+ public MemoryBufferIsFullException(String message) {
+ super(message);
+ }
+ }
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/QueueConsumer.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/QueueConsumer.java
new file mode 100644
index 0000000000000..e3a613e7672b7
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/QueueConsumer.java
@@ -0,0 +1,117 @@
+/*
+ * 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.pulsar.client.api.v5;
+
+import java.time.Duration;
+import org.apache.pulsar.client.api.v5.async.AsyncQueueConsumer;
+
+/**
+ * A consumer for queue (unordered) consumption with broker-managed position tracking.
+ *
+ * Messages are distributed to available consumers for parallel processing.
+ * Acknowledgment is individual: each message must be acknowledged separately.
+ *
+ *
This interface provides synchronous (blocking) operations. For non-blocking
+ * usage, obtain an {@link AsyncQueueConsumer} via {@link #async()}.
+ *
+ *
This maps to the Shared/Key_Shared subscription model in the Pulsar v4 API.
+ *
+ * @param the type of message values
+ */
+public interface QueueConsumer extends AutoCloseable {
+
+ /**
+ * The topic this consumer is subscribed to.
+ *
+ * @return the fully qualified topic name
+ */
+ String topic();
+
+ /**
+ * The subscription name.
+ *
+ * @return the subscription name
+ */
+ String subscription();
+
+ /**
+ * The consumer name (system-assigned or user-specified).
+ *
+ * @return the consumer name, never {@code null}
+ */
+ String consumerName();
+
+ // --- Receive ---
+
+ /**
+ * Receive a single message, blocking indefinitely.
+ *
+ * @return the received {@link Message}
+ * @throws PulsarClientException if the consumer is closed or a connection error occurs
+ */
+ Message receive() throws PulsarClientException;
+
+ /**
+ * Receive a single message, blocking up to the given timeout.
+ * Returns {@code null} if the timeout elapses without a message.
+ *
+ * @param timeout the maximum time to wait for a message
+ * @return the received {@link Message}, or {@code null} if the timeout elapses
+ * @throws PulsarClientException if the consumer is closed or a connection error occurs
+ */
+ Message receive(Duration timeout) throws PulsarClientException;
+
+ /**
+ * Acknowledge a single message by its ID.
+ *
+ * @param messageId the ID of the message to acknowledge
+ */
+ void acknowledge(MessageId messageId);
+
+ /**
+ * Acknowledge within a transaction. The acknowledgment becomes effective when the
+ * transaction is committed.
+ *
+ * @param messageId the ID of the message to acknowledge
+ * @param txn the transaction to associate the acknowledgment with
+ */
+ void acknowledge(MessageId messageId, Transaction txn);
+
+ /**
+ * Signal that the message with this ID could not be processed.
+ *
+ * @param messageId the ID of the message to negatively acknowledge
+ */
+ void negativeAcknowledge(MessageId messageId);
+
+ /**
+ * Return the asynchronous view of this consumer.
+ *
+ * @return the {@link AsyncQueueConsumer} counterpart of this consumer
+ */
+ AsyncQueueConsumer async();
+
+ /**
+ * Close the consumer and release all resources.
+ *
+ * @throws PulsarClientException if an error occurs while closing the consumer
+ */
+ @Override
+ void close() throws PulsarClientException;
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/QueueConsumerBuilder.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/QueueConsumerBuilder.java
new file mode 100644
index 0000000000000..a3f728e127e82
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/QueueConsumerBuilder.java
@@ -0,0 +1,239 @@
+/*
+ * 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.pulsar.client.api.v5;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.regex.Pattern;
+import org.apache.pulsar.client.api.v5.config.BackoffPolicy;
+import org.apache.pulsar.client.api.v5.config.DeadLetterPolicy;
+import org.apache.pulsar.client.api.v5.config.EncryptionPolicy;
+import org.apache.pulsar.client.api.v5.config.SubscriptionInitialPosition;
+
+/**
+ * Builder for configuring and creating a {@link QueueConsumer}.
+ *
+ * @param the type of message values the consumer will receive
+ */
+public interface QueueConsumerBuilder {
+
+ /**
+ * Subscribe and create the queue consumer, blocking until ready.
+ *
+ * @return the created {@link QueueConsumer}
+ * @throws PulsarClientException if the subscription fails or a connection error occurs
+ */
+ QueueConsumer subscribe() throws PulsarClientException;
+
+ /**
+ * Subscribe and create the queue consumer asynchronously.
+ *
+ * @return a {@link CompletableFuture} that completes with the created {@link QueueConsumer}
+ */
+ CompletableFuture> subscribeAsync();
+
+ // --- Topic selection ---
+
+ /**
+ * The topic(s) to subscribe to.
+ *
+ * @param topicNames one or more topic names
+ * @return this builder instance for chaining
+ */
+ QueueConsumerBuilder topic(String... topicNames);
+
+ /**
+ * The topics to subscribe to.
+ *
+ * @param topicNames the list of topic names
+ * @return this builder instance for chaining
+ */
+ QueueConsumerBuilder topics(List topicNames);
+
+ /**
+ * Subscribe to all topics matching a regex pattern.
+ *
+ * @param pattern the compiled regex pattern to match topic names against
+ * @return this builder instance for chaining
+ */
+ QueueConsumerBuilder topicsPattern(Pattern pattern);
+
+ /**
+ * Subscribe to all topics matching a regex pattern (string form).
+ *
+ * @param regex the regex pattern string to match topic names against
+ * @return this builder instance for chaining
+ */
+ QueueConsumerBuilder topicsPattern(String regex);
+
+ // --- Subscription ---
+
+ /**
+ * The subscription name. Required for managed consumers.
+ *
+ * @param subscriptionName the subscription name
+ * @return this builder instance for chaining
+ */
+ QueueConsumerBuilder subscriptionName(String subscriptionName);
+
+ /**
+ * Properties to attach to the subscription.
+ *
+ * @param properties the subscription properties
+ * @return this builder instance for chaining
+ */
+ QueueConsumerBuilder subscriptionProperties(Map properties);
+
+ /**
+ * Initial position when the subscription is first created.
+ *
+ * @param position the initial position
+ * @return this builder instance for chaining
+ */
+ QueueConsumerBuilder subscriptionInitialPosition(SubscriptionInitialPosition position);
+
+ // --- Consumer identity ---
+
+ /**
+ * A custom name for this consumer instance.
+ *
+ * @param consumerName the consumer name
+ * @return this builder instance for chaining
+ */
+ QueueConsumerBuilder consumerName(String consumerName);
+
+ /**
+ * Size of the receiver queue. Controls prefetch depth.
+ *
+ * @param receiverQueueSize the receiver queue size
+ * @return this builder instance for chaining
+ */
+ QueueConsumerBuilder receiverQueueSize(int receiverQueueSize);
+
+ /**
+ * Priority level for this consumer (lower values mean higher priority for
+ * message dispatch).
+ *
+ * @param priorityLevel the priority level
+ * @return this builder instance for chaining
+ */
+ QueueConsumerBuilder priorityLevel(int priorityLevel);
+
+ // --- Acknowledgment ---
+
+ /**
+ * If a message is not acknowledged within this duration, it is automatically redelivered.
+ * Set to zero to disable.
+ *
+ * @param timeout the ack timeout duration
+ * @return this builder instance for chaining
+ */
+ QueueConsumerBuilder ackTimeout(Duration timeout);
+
+ /**
+ * How frequently acknowledgments are flushed to the broker.
+ *
+ * @param delay the acknowledgment group time
+ * @return this builder instance for chaining
+ */
+ QueueConsumerBuilder acknowledgmentGroupTime(Duration delay);
+
+ /**
+ * Maximum number of acknowledgments to group before flushing.
+ *
+ * @param size the maximum acknowledgment group size
+ * @return this builder instance for chaining
+ */
+ QueueConsumerBuilder maxAcknowledgmentGroupSize(int size);
+
+ // --- Redelivery ---
+
+ /**
+ * Backoff strategy for redelivery after negative acknowledgment.
+ *
+ * @param backoff the backoff policy to use for negative ack redelivery
+ * @return this builder instance for chaining
+ * @see BackoffPolicy#fixed(Duration)
+ * @see BackoffPolicy#exponential(Duration, Duration)
+ */
+ QueueConsumerBuilder negativeAckRedeliveryBackoff(BackoffPolicy backoff);
+
+ /**
+ * Backoff strategy for redelivery after ack timeout.
+ *
+ * @param backoff the backoff policy to use for ack timeout redelivery
+ * @return this builder instance for chaining
+ * @see BackoffPolicy#fixed(Duration)
+ * @see BackoffPolicy#exponential(Duration, Duration)
+ */
+ QueueConsumerBuilder ackTimeoutRedeliveryBackoff(BackoffPolicy backoff);
+
+ // --- Dead letter queue ---
+
+ /**
+ * Configure the dead letter queue policy.
+ *
+ * @param policy the dead letter policy
+ * @return this builder instance for chaining
+ */
+ QueueConsumerBuilder deadLetterPolicy(DeadLetterPolicy policy);
+
+ // --- Pattern subscription ---
+
+ /**
+ * How often to re-discover topics matching the pattern.
+ *
+ * @param interval the auto-discovery interval
+ * @return this builder instance for chaining
+ */
+ QueueConsumerBuilder patternAutoDiscoveryPeriod(Duration interval);
+
+ // --- Encryption ---
+
+ /**
+ * Configure end-to-end message encryption for decryption.
+ *
+ * @param policy the encryption policy to use
+ * @return this builder instance for chaining
+ * @see EncryptionPolicy#forConsumer
+ */
+ QueueConsumerBuilder encryptionPolicy(EncryptionPolicy policy);
+
+
+ // --- Misc ---
+
+ /**
+ * Add a single property to the consumer metadata.
+ *
+ * @param key the property key
+ * @param value the property value
+ * @return this builder instance for chaining
+ */
+ QueueConsumerBuilder property(String key, String value);
+
+ /**
+ * Add multiple properties to the consumer metadata.
+ *
+ * @param properties the properties to add
+ * @return this builder instance for chaining
+ */
+ QueueConsumerBuilder properties(Map properties);
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/StreamConsumer.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/StreamConsumer.java
new file mode 100644
index 0000000000000..b3c29bc5c0df2
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/StreamConsumer.java
@@ -0,0 +1,121 @@
+/*
+ * 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.pulsar.client.api.v5;
+
+import java.io.Closeable;
+import java.time.Duration;
+import org.apache.pulsar.client.api.v5.async.AsyncStreamConsumer;
+
+/**
+ * A consumer for streaming (ordered) consumption with broker-managed position tracking.
+ *
+ * Messages are delivered in order (per-key if keyed). Acknowledgment is cumulative only:
+ * acknowledging a message ID means all messages up to and including that ID are acknowledged.
+ *
+ *
This interface provides synchronous (blocking) operations. For non-blocking
+ * usage, obtain an {@link AsyncStreamConsumer} via {@link #async()}.
+ *
+ *
This maps to the Exclusive/Failover subscription model in the Pulsar v4 API.
+ *
+ * @param the type of message values
+ */
+public interface StreamConsumer extends Closeable {
+
+ /**
+ * The topic this consumer is subscribed to.
+ *
+ * @return the fully qualified topic name
+ */
+ String topic();
+
+ /**
+ * The subscription name.
+ *
+ * @return the subscription name
+ */
+ String subscription();
+
+ /**
+ * The consumer name (system-assigned or user-specified).
+ *
+ * @return the consumer name, never {@code null}
+ */
+ String consumerName();
+
+ /**
+ * Receive a single message, blocking indefinitely.
+ *
+ * @return the received {@link Message}
+ * @throws PulsarClientException if the consumer is closed or a connection error occurs
+ */
+ Message receive() throws PulsarClientException;
+
+ /**
+ * Receive a single message, blocking up to the given timeout.
+ * Returns {@code null} if the timeout elapses without a message.
+ *
+ * @param timeout the maximum time to wait for a message
+ * @return the received {@link Message}, or {@code null} if the timeout elapses
+ * @throws PulsarClientException if the consumer is closed or a connection error occurs
+ */
+ Message receive(Duration timeout) throws PulsarClientException;
+
+ /**
+ * Receive a batch of messages, blocking up to the given timeout.
+ *
+ * @param maxNumMessages the maximum number of messages to return
+ * @param timeout the maximum time to wait for messages
+ * @return the received {@link Messages} batch
+ * @throws PulsarClientException if the consumer is closed or a connection error occurs
+ */
+ Messages receiveMulti(int maxNumMessages, Duration timeout) throws PulsarClientException;
+
+ /**
+ * Acknowledge all messages up to and including the given message ID.
+ *
+ * @param messageId the ID of the message to acknowledge cumulatively
+ */
+ void acknowledgeCumulative(MessageId messageId);
+
+ /**
+ * Acknowledge within a transaction. The acknowledgment becomes effective when the
+ * transaction is committed.
+ *
+ * @param messageId the ID of the message to acknowledge cumulatively
+ * @param txn the transaction to associate the acknowledgment with
+ */
+ void acknowledgeCumulative(MessageId messageId, Transaction txn);
+
+ /**
+ * Return the asynchronous view of this consumer.
+ *
+ * @return the {@link AsyncStreamConsumer} counterpart of this consumer
+ */
+ AsyncStreamConsumer async();
+
+ // --- Lifecycle ---
+
+ /**
+ * Close the consumer and release all resources.
+ *
+ * @throws PulsarClientException if an error occurs while closing the consumer
+ */
+ @Override
+ void close() throws PulsarClientException;
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/StreamConsumerBuilder.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/StreamConsumerBuilder.java
new file mode 100644
index 0000000000000..ad6f0df0da6d6
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/StreamConsumerBuilder.java
@@ -0,0 +1,166 @@
+/*
+ * 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.pulsar.client.api.v5;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import org.apache.pulsar.client.api.v5.config.EncryptionPolicy;
+import org.apache.pulsar.client.api.v5.config.SubscriptionInitialPosition;
+
+/**
+ * Builder for configuring and creating a {@link StreamConsumer}.
+ *
+ * @param the type of message values the consumer will receive
+ */
+public interface StreamConsumerBuilder {
+
+ /**
+ * Subscribe and create the stream consumer, blocking until ready.
+ *
+ * @return the created {@link StreamConsumer}
+ * @throws PulsarClientException if the subscription fails or a connection error occurs
+ */
+ StreamConsumer subscribe() throws PulsarClientException;
+
+ /**
+ * Subscribe and create the stream consumer asynchronously.
+ *
+ * @return a {@link CompletableFuture} that completes with the created {@link StreamConsumer}
+ */
+ CompletableFuture> subscribeAsync();
+
+ // --- Required ---
+
+ /**
+ * The topic(s) to subscribe to.
+ *
+ * @param topicNames one or more topic names
+ * @return this builder instance for chaining
+ */
+ StreamConsumerBuilder topic(String... topicNames);
+
+ /**
+ * The subscription name.
+ *
+ * @param subscriptionName the subscription name
+ * @return this builder instance for chaining
+ */
+ StreamConsumerBuilder subscriptionName(String subscriptionName);
+
+ // --- Seek (initial position override) ---
+
+ /**
+ * Reset the subscription to a specific message ID.
+ *
+ * @param messageId the message ID to seek to
+ * @return this builder instance for chaining
+ */
+ StreamConsumerBuilder seek(MessageId messageId);
+
+ /**
+ * Reset the subscription to a specific timestamp. The subscription
+ * will be positioned at the first message published at or after this timestamp.
+ *
+ * @param timestamp the timestamp to seek to
+ * @return this builder instance for chaining
+ */
+ StreamConsumerBuilder seek(Instant timestamp);
+
+ // --- Optional ---
+
+ /**
+ * Properties to attach to the subscription.
+ *
+ * @param properties the subscription properties
+ * @return this builder instance for chaining
+ */
+ StreamConsumerBuilder subscriptionProperties(Map properties);
+
+ /**
+ * Initial position when the subscription is first created (no existing cursor).
+ *
+ * @param position the initial position
+ * @return this builder instance for chaining
+ */
+ StreamConsumerBuilder subscriptionInitialPosition(SubscriptionInitialPosition position);
+
+ /**
+ * A custom name for this consumer instance.
+ *
+ * @param consumerName the consumer name
+ * @return this builder instance for chaining
+ */
+ StreamConsumerBuilder consumerName(String consumerName);
+
+ /**
+ * How frequently cumulative acknowledgments are flushed to the broker.
+ *
+ * @param delay the acknowledgment group time
+ * @return this builder instance for chaining
+ */
+ StreamConsumerBuilder acknowledgmentGroupTime(Duration delay);
+
+ /**
+ * Whether to read from the compacted topic (only latest value per key).
+ *
+ * @param readCompacted {@code true} to read from the compacted topic
+ * @return this builder instance for chaining
+ */
+ StreamConsumerBuilder readCompacted(boolean readCompacted);
+
+ /**
+ * Enable replication of subscription state across geo-replicated clusters.
+ *
+ * @param replicate {@code true} to replicate subscription state
+ * @return this builder instance for chaining
+ */
+ StreamConsumerBuilder replicateSubscriptionState(boolean replicate);
+
+ // --- Encryption ---
+
+ /**
+ * Configure end-to-end message encryption for decryption.
+ *
+ * @param policy the encryption policy to use
+ * @return this builder instance for chaining
+ * @see EncryptionPolicy#forConsumer
+ */
+ StreamConsumerBuilder encryptionPolicy(EncryptionPolicy policy);
+
+ // --- Metadata ---
+
+ /**
+ * Add a single property to the consumer metadata.
+ *
+ * @param key the property key
+ * @param value the property value
+ * @return this builder instance for chaining
+ */
+ StreamConsumerBuilder property(String key, String value);
+
+ /**
+ * Add multiple properties to the consumer metadata.
+ *
+ * @param properties the properties to add
+ * @return this builder instance for chaining
+ */
+ StreamConsumerBuilder properties(Map properties);
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/Transaction.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/Transaction.java
new file mode 100644
index 0000000000000..c5fd42ff42f3e
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/Transaction.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.pulsar.client.api.v5;
+
+import org.apache.pulsar.client.api.v5.async.AsyncTransaction;
+
+/**
+ * A Pulsar transaction handle.
+ *
+ * Transactions provide exactly-once semantics across multiple topics and subscriptions.
+ * Messages produced and acknowledgments made within a transaction are atomically committed
+ * or aborted.
+ */
+public interface Transaction {
+
+ enum State {
+ OPEN,
+ COMMITTING,
+ ABORTING,
+ COMMITTED,
+ ABORTED,
+ ERROR,
+ TIMED_OUT
+ }
+
+ /**
+ * Commit this transaction, making all produced messages visible and all acknowledgments durable.
+ *
+ * @throws PulsarClientException if the transaction cannot be committed (e.g., it has already
+ * been aborted, timed out, or encountered an error)
+ */
+ void commit() throws PulsarClientException;
+
+ /**
+ * Abort this transaction, discarding all produced messages and rolling back acknowledgments.
+ *
+ * @throws PulsarClientException if the transaction cannot be aborted (e.g., it has already
+ * been committed or encountered an error)
+ */
+ void abort() throws PulsarClientException;
+
+ /**
+ * Return an asynchronous view of this transaction.
+ *
+ * @return the {@link AsyncTransaction} counterpart of this transaction
+ */
+ AsyncTransaction async();
+
+ /**
+ * The current state of this transaction.
+ *
+ * @return the current {@link State} of this transaction
+ */
+ State state();
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/AsyncCheckpointConsumer.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/AsyncCheckpointConsumer.java
new file mode 100644
index 0000000000000..f7bb04bfabaa7
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/AsyncCheckpointConsumer.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.pulsar.client.api.v5.async;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import org.apache.pulsar.client.api.v5.Checkpoint;
+import org.apache.pulsar.client.api.v5.CheckpointConsumer;
+import org.apache.pulsar.client.api.v5.Message;
+
+/**
+ * Asynchronous view of a {@link CheckpointConsumer}.
+ *
+ *
All operations return {@link CompletableFuture} and never block.
+ * Obtained via {@link CheckpointConsumer#async()}.
+ *
+ * @param the type of message values
+ */
+public interface AsyncCheckpointConsumer {
+
+ /**
+ * Receive a single message asynchronously.
+ *
+ * @return a {@link CompletableFuture} that completes with the next available message
+ */
+ CompletableFuture> receive();
+
+ /**
+ * Receive a single message, completing with {@code null} if the timeout elapses.
+ *
+ * @param timeout the maximum duration to wait for a message
+ * @return a {@link CompletableFuture} that completes with the next available message,
+ * or {@code null} if the timeout elapses
+ */
+ CompletableFuture> receive(Duration timeout);
+
+ /**
+ * Receive a batch of messages asynchronously.
+ *
+ * @param maxMessages maximum number of messages to return
+ * @param timeout maximum time to wait for messages
+ * @return a {@link CompletableFuture} that completes with a list of up to
+ * {@code maxMessages} messages
+ */
+ CompletableFuture>> receiveMulti(int maxMessages, Duration timeout);
+
+ /**
+ * Create a consistent checkpoint asynchronously.
+ *
+ * @return a {@link CompletableFuture} that completes with a {@link Checkpoint} representing
+ * the current position across all segments of the topic
+ */
+ CompletableFuture checkpoint();
+
+ /**
+ * Seek to a checkpoint asynchronously.
+ *
+ * @param checkpoint the checkpoint to seek to
+ * @return a {@link CompletableFuture} that completes when the consumer has been repositioned
+ * to the given checkpoint
+ */
+ CompletableFuture seek(Checkpoint checkpoint);
+
+ /**
+ * Close this consumer asynchronously.
+ *
+ * @return a {@link CompletableFuture} that completes when the consumer has been closed
+ * and all resources have been released
+ */
+ CompletableFuture close();
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/AsyncMessageBuilder.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/AsyncMessageBuilder.java
new file mode 100644
index 0000000000000..4e26f8b878860
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/AsyncMessageBuilder.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.pulsar.client.api.v5.async;
+
+import java.util.concurrent.CompletableFuture;
+import org.apache.pulsar.client.api.v5.MessageId;
+import org.apache.pulsar.client.api.v5.MessageMetadata;
+
+/**
+ * Asynchronous message builder, obtained from {@link AsyncProducer#newMessage()}.
+ *
+ * Inherits all metadata setters from {@link MessageMetadata} and adds a
+ * non-blocking {@link #send()} terminal operation.
+ *
+ * @param the type of the message value
+ */
+public interface AsyncMessageBuilder extends MessageMetadata> {
+
+ /**
+ * Send the message asynchronously.
+ *
+ * @return a {@link CompletableFuture} that completes with the {@link MessageId} assigned
+ * to the published message by the broker
+ */
+ CompletableFuture send();
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/AsyncProducer.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/AsyncProducer.java
new file mode 100644
index 0000000000000..385b0025bee02
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/AsyncProducer.java
@@ -0,0 +1,57 @@
+/*
+ * 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.pulsar.client.api.v5.async;
+
+import java.util.concurrent.CompletableFuture;
+import org.apache.pulsar.client.api.v5.Producer;
+
+/**
+ * Asynchronous view of a {@link Producer}.
+ *
+ * All operations return {@link CompletableFuture} and never block.
+ * Obtained via {@link Producer#async()}.
+ *
+ * @param the type of message values this producer sends
+ */
+public interface AsyncProducer {
+
+ /**
+ * Create a message builder for advanced message construction.
+ * Use {@link AsyncMessageBuilder#send()} as the terminal operation.
+ *
+ * @return a new {@link AsyncMessageBuilder} for configuring and sending a message
+ */
+ AsyncMessageBuilder newMessage();
+
+ /**
+ * Flush all pending messages asynchronously.
+ *
+ * @return a {@link CompletableFuture} that completes when all pending messages have been
+ * flushed to the broker
+ */
+ CompletableFuture flush();
+
+ /**
+ * Close this producer asynchronously.
+ *
+ * @return a {@link CompletableFuture} that completes when the producer has been closed
+ * and all resources have been released
+ */
+ CompletableFuture close();
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/AsyncQueueConsumer.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/AsyncQueueConsumer.java
new file mode 100644
index 0000000000000..926e632f73199
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/AsyncQueueConsumer.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.pulsar.client.api.v5.async;
+
+import java.util.concurrent.CompletableFuture;
+import org.apache.pulsar.client.api.v5.Message;
+import org.apache.pulsar.client.api.v5.MessageId;
+import org.apache.pulsar.client.api.v5.QueueConsumer;
+import org.apache.pulsar.client.api.v5.Transaction;
+
+/**
+ * Asynchronous view of a {@link QueueConsumer}.
+ *
+ * All operations return {@link CompletableFuture} and never block.
+ * Obtained via {@link QueueConsumer#async()}.
+ *
+ * @param the type of message values
+ */
+public interface AsyncQueueConsumer {
+
+ /**
+ * Receive a single message asynchronously.
+ *
+ * @return a {@link CompletableFuture} that completes with the next available message
+ */
+ CompletableFuture> receive();
+
+ /**
+ * Acknowledge a single message by its ID.
+ *
+ * @param messageId the ID of the message to acknowledge
+ */
+ void acknowledge(MessageId messageId);
+
+ /**
+ * Acknowledge a single message.
+ *
+ * @param message the message to acknowledge
+ */
+ void acknowledge(Message message);
+
+ /**
+ * Acknowledge within a transaction. The acknowledgment becomes effective when the
+ * transaction is committed.
+ *
+ * @param messageId the ID of the message to acknowledge
+ * @param txn the transaction to associate this acknowledgment with
+ */
+ void acknowledge(MessageId messageId, Transaction txn);
+
+ /**
+ * Signal that this message could not be processed. It will be redelivered later.
+ *
+ * @param messageId the ID of the message to negatively acknowledge
+ */
+ void negativeAcknowledge(MessageId messageId);
+
+ /**
+ * Signal that this message could not be processed. It will be redelivered later.
+ *
+ * @param message the message to negatively acknowledge
+ */
+ void negativeAcknowledge(Message message);
+
+ /**
+ * Close this consumer asynchronously.
+ *
+ * @return a {@link CompletableFuture} that completes when the consumer has been closed
+ * and all resources have been released
+ */
+ CompletableFuture close();
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/AsyncStreamConsumer.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/AsyncStreamConsumer.java
new file mode 100644
index 0000000000000..494c6f83f72b5
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/AsyncStreamConsumer.java
@@ -0,0 +1,89 @@
+/*
+ * 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.pulsar.client.api.v5.async;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import org.apache.pulsar.client.api.v5.Message;
+import org.apache.pulsar.client.api.v5.MessageId;
+import org.apache.pulsar.client.api.v5.StreamConsumer;
+import org.apache.pulsar.client.api.v5.Transaction;
+
+/**
+ * Asynchronous view of a {@link StreamConsumer}.
+ *
+ * All operations return {@link CompletableFuture} and never block.
+ * Obtained via {@link StreamConsumer#async()}.
+ *
+ * @param the type of message values
+ */
+public interface AsyncStreamConsumer {
+
+ /**
+ * Receive a single message asynchronously.
+ *
+ * @return a {@link CompletableFuture} that completes with the next available message
+ */
+ CompletableFuture> receive();
+
+ /**
+ * Receive a single message, completing with {@code null} if the timeout elapses
+ * without a message becoming available.
+ *
+ * @param timeout the maximum duration to wait for a message
+ * @return a {@link CompletableFuture} that completes with the next available message,
+ * or {@code null} if the timeout elapses
+ */
+ CompletableFuture> receive(Duration timeout);
+
+ /**
+ * Receive a batch of messages asynchronously.
+ *
+ * @param maxNumMessages maximum number of messages to return
+ * @param timeout maximum time to wait for messages
+ * @return a {@link CompletableFuture} that completes with a list of up to
+ * {@code maxNumMessages} messages
+ */
+ CompletableFuture>> receiveMulti(int maxNumMessages, Duration timeout);
+
+ /**
+ * Acknowledge all messages up to and including the given message ID.
+ *
+ * @param messageId the message ID up to which all messages are acknowledged (inclusive)
+ */
+ void acknowledgeCumulative(MessageId messageId);
+
+ /**
+ * Acknowledge within a transaction. The acknowledgment becomes effective when the
+ * transaction is committed.
+ *
+ * @param messageId the message ID up to which all messages are acknowledged (inclusive)
+ * @param txn the transaction to associate this acknowledgment with
+ */
+ void acknowledgeCumulative(MessageId messageId, Transaction txn);
+
+ /**
+ * Close this consumer asynchronously.
+ *
+ * @return a {@link CompletableFuture} that completes when the consumer has been closed
+ * and all resources have been released
+ */
+ CompletableFuture close();
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/AsyncTransaction.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/AsyncTransaction.java
new file mode 100644
index 0000000000000..7dfaa6f61ea65
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/AsyncTransaction.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.pulsar.client.api.v5.async;
+
+import java.util.concurrent.CompletableFuture;
+import org.apache.pulsar.client.api.v5.Transaction;
+
+/**
+ * Asynchronous view of a {@link Transaction}.
+ *
+ * All operations return {@link CompletableFuture} and never block.
+ * Obtained via {@link Transaction#async()}.
+ */
+public interface AsyncTransaction {
+
+ /**
+ * Commit this transaction, making all produced messages visible and all acknowledgments durable.
+ *
+ * @return a {@link CompletableFuture} that completes when the transaction has been
+ * successfully committed
+ */
+ CompletableFuture commit();
+
+ /**
+ * Abort this transaction, discarding all produced messages and rolling back acknowledgments.
+ *
+ * @return a {@link CompletableFuture} that completes when the transaction has been
+ * successfully aborted
+ */
+ CompletableFuture abort();
+
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/package-info.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/package-info.java
new file mode 100644
index 0000000000000..e8953c54c652b
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/async/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+/**
+ * Asynchronous views of producers, consumers, and transactions.
+ *
+ * Each async interface mirrors its synchronous counterpart in the parent package,
+ * replacing blocking return values with {@link java.util.concurrent.CompletableFuture}.
+ * Async views are obtained via the {@code async()} accessor on the synchronous type
+ * (e.g. {@link org.apache.pulsar.client.api.v5.Producer#async()}).
+ */
+package org.apache.pulsar.client.api.v5.async;
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/Authentication.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/Authentication.java
new file mode 100644
index 0000000000000..8f4f0bf19d8dc
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/Authentication.java
@@ -0,0 +1,70 @@
+/*
+ * 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.pulsar.client.api.v5.auth;
+
+import java.io.Closeable;
+import org.apache.pulsar.client.api.v5.PulsarClientException;
+
+/**
+ * Pluggable authentication provider for Pulsar clients.
+ *
+ *
Implementations must be thread-safe.
+ */
+public interface Authentication extends Closeable {
+
+ /**
+ * The authentication method name (e.g., "token", "tls").
+ *
+ * @return the authentication method identifier string
+ */
+ String authMethodName();
+
+ /**
+ * Get the authentication data to be sent to the broker.
+ *
+ * @return the authentication data containing credentials for the broker
+ * @throws PulsarClientException if the authentication data could not be obtained
+ */
+ AuthenticationData authData() throws PulsarClientException;
+
+ /**
+ * Get the authentication data for a specific broker host.
+ *
+ *
The default implementation delegates to {@link #authData()}.
+ *
+ * @param brokerHostName the hostname of the broker to authenticate against
+ * @return the authentication data containing credentials for the specified broker
+ * @throws PulsarClientException if the authentication data could not be obtained
+ */
+ default AuthenticationData authData(String brokerHostName) throws PulsarClientException {
+ return authData();
+ }
+
+ /**
+ * Initialize the authentication provider. Called once when the client is created.
+ *
+ * @throws PulsarClientException if initialization fails
+ */
+ default void initialize() throws PulsarClientException {
+ }
+
+ @Override
+ default void close() {
+ }
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/AuthenticationData.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/AuthenticationData.java
new file mode 100644
index 0000000000000..c8ab63410cf70
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/AuthenticationData.java
@@ -0,0 +1,99 @@
+/*
+ * 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.pulsar.client.api.v5.auth;
+
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Provides authentication credentials for different transport mechanisms.
+ */
+public interface AuthenticationData {
+
+ // --- HTTP authentication ---
+
+ /**
+ * Check whether this authentication data provides HTTP headers.
+ *
+ * @return {@code true} if HTTP authentication headers are available, {@code false} otherwise
+ */
+ default boolean hasDataForHttp() {
+ return false;
+ }
+
+ /**
+ * Get the HTTP authentication headers to include in requests.
+ *
+ * @return a set of header name-value entries for HTTP authentication, or an empty set if none
+ */
+ default Set> getHttpHeaders() {
+ return Set.of();
+ }
+
+ // --- TLS mutual authentication ---
+
+ /**
+ * Check whether this authentication data provides TLS client certificates.
+ *
+ * @return {@code true} if TLS certificate data is available, {@code false} otherwise
+ */
+ default boolean hasDataForTls() {
+ return false;
+ }
+
+ /**
+ * Get the TLS client certificate chain for mutual authentication.
+ *
+ * @return the client certificate chain, or {@code null} if not available
+ */
+ default Certificate[] getTlsCertificates() {
+ return null;
+ }
+
+ /**
+ * Get the TLS client private key for mutual authentication.
+ *
+ * @return the client private key, or {@code null} if not available
+ */
+ default PrivateKey getTlsPrivateKey() {
+ return null;
+ }
+
+ // --- Binary protocol authentication ---
+
+ /**
+ * Check whether this authentication data provides binary protocol command data.
+ *
+ * @return {@code true} if command data is available for the Pulsar binary protocol, {@code false} otherwise
+ */
+ default boolean hasDataFromCommand() {
+ return false;
+ }
+
+ /**
+ * Get the authentication data to include in binary protocol commands.
+ *
+ * @return the command data string for binary protocol authentication, or {@code null} if not available
+ */
+ default String getCommandData() {
+ return null;
+ }
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/AuthenticationFactory.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/AuthenticationFactory.java
new file mode 100644
index 0000000000000..705b2aa429eef
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/AuthenticationFactory.java
@@ -0,0 +1,93 @@
+/*
+ * 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.pulsar.client.api.v5.auth;
+
+import java.util.Map;
+import java.util.function.Supplier;
+import org.apache.pulsar.client.api.v5.PulsarClientException;
+import org.apache.pulsar.client.api.v5.internal.PulsarClientProvider;
+
+/**
+ * Factory for creating common authentication providers.
+ */
+public final class AuthenticationFactory {
+
+ private AuthenticationFactory() {
+ }
+
+ /**
+ * Create token-based authentication with a static token.
+ *
+ * @param token the JWT or other authentication token string
+ * @return an {@link Authentication} instance configured with the given token
+ */
+ public static Authentication token(String token) {
+ return PulsarClientProvider.get().authenticationToken(token);
+ }
+
+ /**
+ * Create token-based authentication with a dynamic token supplier.
+ *
+ * The supplier is invoked each time the client needs to authenticate,
+ * allowing for token refresh without recreating the client.
+ *
+ * @param tokenSupplier a supplier that provides the current authentication token
+ * @return an {@link Authentication} instance that retrieves tokens from the supplier
+ */
+ public static Authentication token(Supplier tokenSupplier) {
+ return PulsarClientProvider.get().authenticationToken(tokenSupplier);
+ }
+
+ /**
+ * Create TLS mutual authentication.
+ *
+ * @param certFilePath the path to the client certificate file (PEM format)
+ * @param keyFilePath the path to the client private key file (PEM format)
+ * @return an {@link Authentication} instance configured for TLS mutual authentication
+ */
+ public static Authentication tls(String certFilePath, String keyFilePath) {
+ return PulsarClientProvider.get().authenticationTls(certFilePath, keyFilePath);
+ }
+
+ /**
+ * Create an authentication provider by plugin class name and parameter string.
+ *
+ * @param authPluginClassName the fully qualified class name of the authentication plugin
+ * @param authParamsString the authentication parameters as a serialized string
+ * @return an {@link Authentication} instance created from the specified plugin
+ * @throws PulsarClientException if the plugin class cannot be loaded or instantiated
+ */
+ public static Authentication create(String authPluginClassName, String authParamsString)
+ throws PulsarClientException {
+ return PulsarClientProvider.get().createAuthentication(authPluginClassName, authParamsString);
+ }
+
+ /**
+ * Create an authentication provider by plugin class name and parameter map.
+ *
+ * @param authPluginClassName the fully qualified class name of the authentication plugin
+ * @param authParams the authentication parameters as key-value pairs
+ * @return an {@link Authentication} instance created from the specified plugin
+ * @throws PulsarClientException if the plugin class cannot be loaded or instantiated
+ */
+ public static Authentication create(String authPluginClassName, Map authParams)
+ throws PulsarClientException {
+ return PulsarClientProvider.get().createAuthentication(authPluginClassName, authParams);
+ }
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/CryptoFailureAction.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/CryptoFailureAction.java
new file mode 100644
index 0000000000000..3b48c2a46f796
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/CryptoFailureAction.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.pulsar.client.api.v5.auth;
+
+/**
+ * Action to take when a message encryption or decryption operation fails.
+ */
+public enum CryptoFailureAction {
+
+ /**
+ * Fail the operation and return an error to the caller.
+ */
+ FAIL,
+
+ /**
+ * Silently discard the message (consumer side only).
+ */
+ DISCARD,
+
+ /**
+ * Deliver the message to the consumer without decrypting (consumer side only).
+ * The message will contain encrypted payload.
+ */
+ CONSUME
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/CryptoKeyReader.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/CryptoKeyReader.java
new file mode 100644
index 0000000000000..bec3eeb6054b5
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/CryptoKeyReader.java
@@ -0,0 +1,45 @@
+/*
+ * 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.pulsar.client.api.v5.auth;
+
+import java.util.Map;
+
+/**
+ * Interface for loading encryption and decryption keys for end-to-end message encryption.
+ */
+public interface CryptoKeyReader {
+
+ /**
+ * Get the public key for encrypting messages.
+ *
+ * @param keyName the name of the key
+ * @param metadata additional metadata associated with the key
+ * @return the encryption key info containing the public key data
+ */
+ EncryptionKeyInfo getPublicKey(String keyName, Map metadata);
+
+ /**
+ * Get the private key for decrypting messages.
+ *
+ * @param keyName the name of the key
+ * @param metadata additional metadata associated with the key
+ * @return the encryption key info containing the private key data
+ */
+ EncryptionKeyInfo getPrivateKey(String keyName, Map metadata);
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/EncryptionKeyInfo.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/EncryptionKeyInfo.java
new file mode 100644
index 0000000000000..cc429bbc8c935
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/EncryptionKeyInfo.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.pulsar.client.api.v5.auth;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Holds an encryption key and associated metadata.
+ *
+ * @param key the raw key bytes
+ * @param metadata key-value metadata associated with the key
+ */
+public record EncryptionKeyInfo(byte[] key, Map metadata) {
+ public EncryptionKeyInfo {
+ Objects.requireNonNull(key, "key must not be null");
+ if (metadata == null) {
+ metadata = Map.of();
+ }
+ }
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/package-info.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/package-info.java
new file mode 100644
index 0000000000000..7d8141ad558ba
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/auth/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+/**
+ * Authentication and encryption types.
+ *
+ * Provides pluggable authentication via {@link org.apache.pulsar.client.api.v5.auth.Authentication}
+ * and convenience factories in {@link org.apache.pulsar.client.api.v5.auth.AuthenticationFactory},
+ * as well as end-to-end encryption support via
+ * {@link org.apache.pulsar.client.api.v5.auth.CryptoKeyReader}.
+ */
+package org.apache.pulsar.client.api.v5.auth;
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/BackoffPolicy.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/BackoffPolicy.java
new file mode 100644
index 0000000000000..eb7ab9a4ecde8
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/BackoffPolicy.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.pulsar.client.api.v5.config;
+
+import java.time.Duration;
+import java.util.Objects;
+
+/**
+ * Backoff configuration for broker reconnection attempts.
+ *
+ *
The delay for attempt {@code n} is {@code min(initialInterval * multiplier^(n-1), maxInterval)}.
+ *
+ * @param initialInterval the delay before the first reconnection attempt
+ * @param maxInterval the maximum delay between reconnection attempts
+ * @param multiplier the multiplier applied after each attempt
+ */
+public record BackoffPolicy(
+ Duration initialInterval,
+ Duration maxInterval,
+ double multiplier
+) {
+ public BackoffPolicy {
+ Objects.requireNonNull(initialInterval, "initialInterval must not be null");
+ Objects.requireNonNull(maxInterval, "maxInterval must not be null");
+ if (multiplier < 1.0) {
+ throw new IllegalArgumentException("multiplier must be >= 1.0");
+ }
+ }
+
+ /**
+ * Create a fixed backoff (no increase between retries).
+ *
+ * @param initialInterval the constant delay between reconnection attempts
+ * @param maxInterval the maximum delay between reconnection attempts
+ * @return a {@link BackoffPolicy} with a multiplier of 1.0
+ */
+ public static BackoffPolicy fixed(Duration initialInterval, Duration maxInterval) {
+ return new BackoffPolicy(initialInterval, maxInterval, 1.0);
+ }
+
+ /**
+ * Create an exponential backoff with the given bounds and a default multiplier of 2.
+ *
+ * @param initialInterval the delay before the first reconnection attempt
+ * @param maxInterval the maximum delay between reconnection attempts
+ * @return a {@link BackoffPolicy} with a multiplier of 2.0
+ */
+ public static BackoffPolicy exponential(Duration initialInterval, Duration maxInterval) {
+ return new BackoffPolicy(initialInterval, maxInterval, 2.0);
+ }
+
+ /**
+ * Create an exponential backoff with a custom multiplier.
+ *
+ * @param initialInterval the delay before the first reconnection attempt
+ * @param maxInterval the maximum delay between reconnection attempts
+ * @param multiplier the multiplier applied after each attempt, must be >= 1.0
+ * @return a {@link BackoffPolicy} with the specified parameters
+ */
+ public static BackoffPolicy exponential(Duration initialInterval, Duration maxInterval, double multiplier) {
+ return new BackoffPolicy(initialInterval, maxInterval, multiplier);
+ }
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/BatchingPolicy.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/BatchingPolicy.java
new file mode 100644
index 0000000000000..13ef7cbc4bd12
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/BatchingPolicy.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.pulsar.client.api.v5.config;
+
+import java.time.Duration;
+
+/**
+ * Configuration for producer message batching.
+ *
+ *
When batching is enabled, the producer groups multiple messages into a single
+ * broker request to improve throughput. A batch is flushed when any of the
+ * configured thresholds is reached.
+ *
+ * @param enabled whether batching is enabled
+ * @param maxPublishDelay maximum time to wait before flushing a batch
+ * @param maxMessages maximum number of messages in a single batch
+ * @param maxSize maximum size of a single batch
+ */
+public record BatchingPolicy(
+ boolean enabled,
+ Duration maxPublishDelay,
+ int maxMessages,
+ MemorySize maxSize
+) {
+ public BatchingPolicy {
+ if (maxPublishDelay == null) {
+ maxPublishDelay = Duration.ofMillis(1);
+ }
+ if (maxMessages < 0) {
+ throw new IllegalArgumentException("maxMessages must be >= 0");
+ }
+ if (maxSize.bytes() < 0) {
+ throw new IllegalArgumentException("maxBytes must be >= 0");
+ }
+ }
+
+ private static final BatchingPolicy DISABLED =
+ new BatchingPolicy(false, Duration.ofMillis(1), 1000, MemorySize.ofKilobytes(128));
+ private static final BatchingPolicy DEFAULT =
+ new BatchingPolicy(true, Duration.ofMillis(1), 1000, MemorySize.ofKilobytes(128));
+
+ /**
+ * Batching disabled.
+ *
+ * @return a {@link BatchingPolicy} with batching disabled
+ */
+ public static BatchingPolicy ofDisabled() {
+ return DISABLED;
+ }
+
+ /**
+ * Batching enabled with default thresholds (1ms delay, 1000 messages, 128KB).
+ *
+ * @return a {@link BatchingPolicy} with default batching thresholds
+ */
+ public static BatchingPolicy ofDefault() {
+ return DEFAULT;
+ }
+
+ /**
+ * Batching enabled with custom thresholds.
+ *
+ * @param maxPublishDelay the maximum time to wait before flushing a batch
+ * @param maxMessages the maximum number of messages in a single batch
+ * @param maxSize the maximum size of a single batch
+ * @return a {@link BatchingPolicy} with batching enabled and the specified thresholds
+ */
+ public static BatchingPolicy of(Duration maxPublishDelay, int maxMessages, MemorySize maxSize) {
+ return new BatchingPolicy(true, maxPublishDelay, maxMessages, maxSize);
+ }
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/ChunkingPolicy.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/ChunkingPolicy.java
new file mode 100644
index 0000000000000..ff32a287a364e
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/ChunkingPolicy.java
@@ -0,0 +1,31 @@
+/*
+ * 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.pulsar.client.api.v5.config;
+
+/**
+ * Configuration for chunking large messages that exceed the broker's max message size.
+ *
+ * @param enabled whether chunking is enabled
+ * @param chunkSize maximum size of each chunk in bytes
+ */
+public record ChunkingPolicy(
+ boolean enabled,
+ int chunkSize
+) {
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/CompressionPolicy.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/CompressionPolicy.java
new file mode 100644
index 0000000000000..79c318211b34d
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/CompressionPolicy.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.pulsar.client.api.v5.config;
+
+/**
+ * Compression configuration for producer message payloads.
+ *
+ * @param type the compression codec to use
+ */
+public record CompressionPolicy(CompressionType type) {
+
+ /**
+ * No compression.
+ *
+ * @return a {@link CompressionPolicy} with compression disabled
+ */
+ public static CompressionPolicy disabled() {
+ return new CompressionPolicy(CompressionType.NONE);
+ }
+
+ /**
+ * Create a compression policy with the given codec.
+ *
+ * @param type the compression codec to use for message payloads
+ * @return a {@link CompressionPolicy} configured with the specified codec
+ */
+ public static CompressionPolicy of(CompressionType type) {
+ return new CompressionPolicy(type);
+ }
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/CompressionType.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/CompressionType.java
new file mode 100644
index 0000000000000..80716016a3139
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/CompressionType.java
@@ -0,0 +1,52 @@
+/*
+ * 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.pulsar.client.api.v5.config;
+
+/**
+ * Compression codec used for message payloads.
+ */
+public enum CompressionType {
+
+ /** No compression. Lowest CPU cost but highest bandwidth and storage usage. */
+ NONE,
+
+ /**
+ * LZ4 compression. Very fast compression and decompression with moderate compression ratio.
+ * A good general-purpose default for latency-sensitive and CPU-sensitive workloads.
+ */
+ LZ4,
+
+ /**
+ * ZLIB compression. Higher compression ratio than LZ4 but significantly slower.
+ * Prefer {@link #ZSTD} which achieves better compression ratios with faster performance.
+ */
+ ZLIB,
+
+ /**
+ * Zstandard compression. Best compression ratio of all options with decompression speed
+ * close to LZ4. Recommended for throughput and storage-optimized workloads.
+ */
+ ZSTD,
+
+ /**
+ * Snappy compression. Very fast with a compression profile similar to LZ4.
+ * Useful for compatibility with ecosystems that standardize on Snappy.
+ */
+ SNAPPY
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/ConnectionPolicy.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/ConnectionPolicy.java
new file mode 100644
index 0000000000000..b502dde731523
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/ConnectionPolicy.java
@@ -0,0 +1,217 @@
+/*
+ * 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.pulsar.client.api.v5.config;
+
+import java.time.Duration;
+import java.util.Objects;
+
+/**
+ * Connection-level settings for the Pulsar client.
+ *
+ *
Groups TCP connection timeout, connection pool sizing, keep-alive, idle timeout,
+ * TCP no-delay, I/O and callback threading, and proxy configuration.
+ */
+public record ConnectionPolicy(
+ Duration connectionTimeout,
+ int connectionsPerBroker,
+ boolean enableTcpNoDelay,
+ Duration keepAliveInterval,
+ Duration connectionMaxIdleTime,
+ int ioThreads,
+ int callbackThreads,
+ String proxyServiceUrl,
+ ProxyProtocol proxyProtocol,
+ BackoffPolicy connectionBackoff
+) {
+
+ /**
+ * Create a connection policy with the given parameters.
+ *
+ * @throws NullPointerException if {@code connectionTimeout}, {@code keepAliveInterval},
+ * {@code connectionMaxIdleTime}, or {@code connectionBackoff} is null
+ * @throws IllegalArgumentException if {@code connectionsPerBroker}, {@code ioThreads},
+ * or {@code callbackThreads} is less than 1
+ */
+ public ConnectionPolicy {
+ Objects.requireNonNull(connectionTimeout, "connectionTimeout must not be null");
+ Objects.requireNonNull(keepAliveInterval, "keepAliveInterval must not be null");
+ Objects.requireNonNull(connectionMaxIdleTime, "connectionMaxIdleTime must not be null");
+ Objects.requireNonNull(connectionBackoff, "connectionBackoff must not be null");
+ if (connectionsPerBroker < 1) {
+ throw new IllegalArgumentException("connectionsPerBroker must be >= 1");
+ }
+ if (ioThreads < 1) {
+ throw new IllegalArgumentException("ioThreads must be >= 1");
+ }
+ if (callbackThreads < 1) {
+ throw new IllegalArgumentException("callbackThreads must be >= 1");
+ }
+ }
+
+ /**
+ * Create a builder for constructing a {@link ConnectionPolicy}.
+ *
+ * @return a new builder with sensible defaults
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder for {@link ConnectionPolicy}.
+ */
+ public static final class Builder {
+ private Duration connectionTimeout = Duration.ofSeconds(10);
+ private int connectionsPerBroker = 1;
+ private boolean enableTcpNoDelay = true;
+ private Duration keepAliveInterval = Duration.ofSeconds(30);
+ private Duration connectionMaxIdleTime = Duration.ofMinutes(3);
+ private int ioThreads = 1;
+ private int callbackThreads = 1;
+ private String proxyServiceUrl;
+ private ProxyProtocol proxyProtocol;
+ private BackoffPolicy connectionBackoff =
+ BackoffPolicy.exponential(Duration.ofMillis(100), Duration.ofSeconds(60));
+
+ private Builder() {
+ }
+
+ /**
+ * Timeout for establishing a TCP connection to the broker.
+ *
+ * @param connectionTimeout the maximum duration to wait for a TCP connection
+ * @return this builder
+ */
+ public Builder connectionTimeout(Duration connectionTimeout) {
+ this.connectionTimeout = connectionTimeout;
+ return this;
+ }
+
+ /**
+ * Maximum number of TCP connections per broker.
+ *
+ * @param connectionsPerBroker the number of connections to maintain per broker
+ * @return this builder
+ */
+ public Builder connectionsPerBroker(int connectionsPerBroker) {
+ this.connectionsPerBroker = connectionsPerBroker;
+ return this;
+ }
+
+ /**
+ * Enable TCP no-delay (disable Nagle's algorithm). Default is {@code true}.
+ *
+ * @param enableTcpNoDelay {@code true} to enable TCP no-delay
+ * @return this builder
+ */
+ public Builder enableTcpNoDelay(boolean enableTcpNoDelay) {
+ this.enableTcpNoDelay = enableTcpNoDelay;
+ return this;
+ }
+
+ /**
+ * Interval for sending keep-alive probes on idle connections.
+ *
+ * @param keepAliveInterval the duration between keep-alive probes
+ * @return this builder
+ */
+ public Builder keepAliveInterval(Duration keepAliveInterval) {
+ this.keepAliveInterval = keepAliveInterval;
+ return this;
+ }
+
+ /**
+ * Maximum idle time before a connection is closed.
+ *
+ * @param connectionMaxIdleTime the maximum idle duration
+ * @return this builder
+ */
+ public Builder connectionMaxIdleTime(Duration connectionMaxIdleTime) {
+ this.connectionMaxIdleTime = connectionMaxIdleTime;
+ return this;
+ }
+
+ /**
+ * Number of I/O threads for managing connections and reading data.
+ *
+ * @param ioThreads the number of I/O threads
+ * @return this builder
+ */
+ public Builder ioThreads(int ioThreads) {
+ this.ioThreads = ioThreads;
+ return this;
+ }
+
+ /**
+ * Number of threads for message listener callbacks.
+ *
+ * @param callbackThreads the number of callback threads
+ * @return this builder
+ */
+ public Builder callbackThreads(int callbackThreads) {
+ this.callbackThreads = callbackThreads;
+ return this;
+ }
+
+ /**
+ * Connect through a proxy.
+ *
+ * @param proxyServiceUrl the URL of the proxy service
+ * @param proxyProtocol the protocol to use when connecting through the proxy
+ * @return this builder
+ */
+ public Builder proxy(String proxyServiceUrl, ProxyProtocol proxyProtocol) {
+ this.proxyServiceUrl = proxyServiceUrl;
+ this.proxyProtocol = proxyProtocol;
+ return this;
+ }
+
+ /**
+ * Backoff strategy for broker reconnection attempts.
+ *
+ * @param connectionBackoff the backoff policy to use when reconnecting to the broker
+ * @return this builder
+ * @see BackoffPolicy#exponential(Duration, Duration)
+ */
+ public Builder connectionBackoff(BackoffPolicy connectionBackoff) {
+ this.connectionBackoff = connectionBackoff;
+ return this;
+ }
+
+ /**
+ * Build the {@link ConnectionPolicy}.
+ *
+ * @return a new {@link ConnectionPolicy} instance
+ */
+ public ConnectionPolicy build() {
+ return new ConnectionPolicy(
+ connectionTimeout,
+ connectionsPerBroker,
+ enableTcpNoDelay,
+ keepAliveInterval,
+ connectionMaxIdleTime,
+ ioThreads,
+ callbackThreads,
+ proxyServiceUrl,
+ proxyProtocol,
+ connectionBackoff
+ );
+ }
+ }
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/DeadLetterPolicy.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/DeadLetterPolicy.java
new file mode 100644
index 0000000000000..cb4f34ad4a434
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/DeadLetterPolicy.java
@@ -0,0 +1,53 @@
+/*
+ * 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.pulsar.client.api.v5.config;
+
+/**
+ * Configuration for the dead letter queue mechanism.
+ *
+ *
When a message has been redelivered more than {@code maxRedeliverCount} times,
+ * it is moved to the dead letter topic instead of being redelivered again.
+ *
+ * @param maxRedeliverCount maximum number of redelivery attempts before sending to the dead letter topic
+ * @param retryLetterTopic custom topic for retry messages (nullable, auto-generated if null)
+ * @param deadLetterTopic custom topic for dead letter messages (nullable, auto-generated if null)
+ * @param initialSubscriptionName subscription name to create on the dead letter topic (nullable)
+ */
+public record DeadLetterPolicy(
+ int maxRedeliverCount,
+ String retryLetterTopic,
+ String deadLetterTopic,
+ String initialSubscriptionName
+) {
+ public DeadLetterPolicy {
+ if (maxRedeliverCount < 0) {
+ throw new IllegalArgumentException("maxRedeliverCount must be >= 0");
+ }
+ }
+
+ /**
+ * Create a dead letter policy with just a max redeliver count, using default topic names.
+ *
+ * @param maxRedeliverCount the maximum number of redelivery attempts before sending to the dead letter topic
+ * @return a {@link DeadLetterPolicy} with auto-generated topic names and no initial subscription
+ */
+ public static DeadLetterPolicy of(int maxRedeliverCount) {
+ return new DeadLetterPolicy(maxRedeliverCount, null, null, null);
+ }
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/EncryptionPolicy.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/EncryptionPolicy.java
new file mode 100644
index 0000000000000..d443274c6e7fd
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/EncryptionPolicy.java
@@ -0,0 +1,72 @@
+/*
+ * 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.pulsar.client.api.v5.config;
+
+import java.util.List;
+import java.util.Objects;
+import org.apache.pulsar.client.api.v5.auth.CryptoFailureAction;
+import org.apache.pulsar.client.api.v5.auth.CryptoKeyReader;
+
+/**
+ * End-to-end encryption configuration for producers and consumers.
+ *
+ *
For producers, supply a {@link CryptoKeyReader} and one or more encryption key names.
+ * For consumers/readers, supply a {@link CryptoKeyReader} and a {@link CryptoFailureAction}.
+ *
+ * @param keyReader the crypto key reader for loading encryption/decryption keys
+ * @param keyNames encryption key names (producer-side; empty list for consumer/reader)
+ * @param failureAction action to take when encryption or decryption fails
+ */
+public record EncryptionPolicy(
+ CryptoKeyReader keyReader,
+ List keyNames,
+ CryptoFailureAction failureAction
+) {
+ public EncryptionPolicy {
+ Objects.requireNonNull(keyReader, "keyReader must not be null");
+ if (keyNames == null) {
+ keyNames = List.of();
+ }
+ if (failureAction == null) {
+ failureAction = CryptoFailureAction.FAIL;
+ }
+ }
+
+ /**
+ * Create an encryption policy for producers.
+ *
+ * @param keyReader the crypto key reader for loading encryption keys
+ * @param keyNames one or more encryption key names to use
+ * @return an {@link EncryptionPolicy} configured for producer-side encryption
+ */
+ public static EncryptionPolicy forProducer(CryptoKeyReader keyReader, String... keyNames) {
+ return new EncryptionPolicy(keyReader, List.of(keyNames), CryptoFailureAction.FAIL);
+ }
+
+ /**
+ * Create an encryption policy for consumers/readers.
+ *
+ * @param keyReader the crypto key reader for loading decryption keys
+ * @param failureAction the action to take when decryption fails
+ * @return an {@link EncryptionPolicy} configured for consumer-side decryption
+ */
+ public static EncryptionPolicy forConsumer(CryptoKeyReader keyReader, CryptoFailureAction failureAction) {
+ return new EncryptionPolicy(keyReader, List.of(), failureAction);
+ }
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/MemorySize.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/MemorySize.java
new file mode 100644
index 0000000000000..8df6cbc4c523d
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/MemorySize.java
@@ -0,0 +1,101 @@
+/*
+ * 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.pulsar.client.api.v5.config;
+
+/**
+ * A type-safe representation of a memory size in bytes.
+ *
+ * Use the static factory methods to create instances from common units:
+ *
{@code
+ * MemorySize.ofMegabytes(64) // 64 MB
+ * MemorySize.ofGigabytes(1) // 1 GB
+ * MemorySize.ofKilobytes(512) // 512 KB
+ * }
+ *
+ * @param bytes the size in bytes
+ */
+public record MemorySize(long bytes) {
+
+ public MemorySize {
+ if (bytes < 0) {
+ throw new IllegalArgumentException("bytes must be >= 0");
+ }
+ }
+
+ private static final long KB = 1024;
+ private static final long MB = 1024 * KB;
+ private static final long GB = 1024 * MB;
+
+ /**
+ * Create a memory size from a number of bytes.
+ *
+ * @param bytes the size in bytes
+ * @return a {@link MemorySize} representing the specified number of bytes
+ */
+ public static MemorySize ofBytes(long bytes) {
+ return new MemorySize(bytes);
+ }
+
+ /**
+ * Create a memory size from a number of kilobytes.
+ *
+ * @param kb the size in kilobytes
+ * @return a {@link MemorySize} representing the specified number of kilobytes
+ */
+ public static MemorySize ofKilobytes(long kb) {
+ return new MemorySize(Math.multiplyExact(kb, KB));
+ }
+
+ /**
+ * Create a memory size from a number of megabytes.
+ *
+ * @param mb the size in megabytes
+ * @return a {@link MemorySize} representing the specified number of megabytes
+ */
+ public static MemorySize ofMegabytes(long mb) {
+ return new MemorySize(Math.multiplyExact(mb, MB));
+ }
+
+ /**
+ * Create a memory size from a number of gigabytes.
+ *
+ * @param gb the size in gigabytes
+ * @return a {@link MemorySize} representing the specified number of gigabytes
+ */
+ public static MemorySize ofGigabytes(long gb) {
+ return new MemorySize(Math.multiplyExact(gb, GB));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return a human-readable string representation using the largest whole unit (GB, MB, KB, or bytes)
+ */
+ @Override
+ public String toString() {
+ if (bytes >= GB && bytes % GB == 0) {
+ return bytes / GB + " GB";
+ } else if (bytes >= MB && bytes % MB == 0) {
+ return bytes / MB + " MB";
+ } else if (bytes >= KB && bytes % KB == 0) {
+ return bytes / KB + " KB";
+ }
+ return bytes + " bytes";
+ }
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/ProducerAccessMode.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/ProducerAccessMode.java
new file mode 100644
index 0000000000000..c4836990a350c
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/ProducerAccessMode.java
@@ -0,0 +1,47 @@
+/*
+ * 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.pulsar.client.api.v5.config;
+
+/**
+ * Access mode for a producer on a topic.
+ */
+public enum ProducerAccessMode {
+
+ /**
+ * Multiple producers can publish on the topic.
+ */
+ SHARED,
+
+ /**
+ * Only one producer is allowed on the topic. If another producer tries to connect, it will fail.
+ */
+ EXCLUSIVE,
+
+ /**
+ * Only one producer is allowed on the topic. If another producer tries to connect,
+ * the existing producer is fenced and disconnected.
+ */
+ EXCLUSIVE_WITH_FENCING,
+
+ /**
+ * Only one producer is allowed on the topic. If another producer is already connected,
+ * the new producer will wait until it can acquire exclusive access.
+ */
+ WAIT_FOR_EXCLUSIVE
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/ProxyProtocol.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/ProxyProtocol.java
new file mode 100644
index 0000000000000..a79fc01590bc7
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/ProxyProtocol.java
@@ -0,0 +1,26 @@
+/*
+ * 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.pulsar.client.api.v5.config;
+
+/**
+ * Protocol used when connecting through a proxy.
+ */
+public enum ProxyProtocol {
+ SNI
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/SubscriptionInitialPosition.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/SubscriptionInitialPosition.java
new file mode 100644
index 0000000000000..41fe100e271d7
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/SubscriptionInitialPosition.java
@@ -0,0 +1,35 @@
+/*
+ * 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.pulsar.client.api.v5.config;
+
+/**
+ * Initial position for a new subscription that has no existing cursor.
+ */
+public enum SubscriptionInitialPosition {
+
+ /**
+ * Start consuming from the latest message (the next message published after subscribing).
+ */
+ LATEST,
+
+ /**
+ * Start consuming from the earliest available message in the topic.
+ */
+ EARLIEST
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/TlsPolicy.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/TlsPolicy.java
new file mode 100644
index 0000000000000..d47b8f71f7319
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/TlsPolicy.java
@@ -0,0 +1,67 @@
+/*
+ * 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.pulsar.client.api.v5.config;
+
+/**
+ * TLS configuration for the Pulsar client connection.
+ *
+ * @param trustCertsFilePath path to the trusted CA certificate file (PEM format)
+ * @param keyFilePath path to the client private key file (PEM format), or {@code null}
+ * @param certificateFilePath path to the client certificate file (PEM format), or {@code null}
+ * @param allowInsecureConnection whether to allow connecting to brokers with untrusted certificates
+ * @param enableHostnameVerification whether to verify the broker hostname against the certificate
+ */
+public record TlsPolicy(
+ String trustCertsFilePath,
+ String keyFilePath,
+ String certificateFilePath,
+ boolean allowInsecureConnection,
+ boolean enableHostnameVerification
+) {
+ /**
+ * Create a TLS policy that trusts the given CA certificate and verifies hostnames.
+ *
+ * @param trustCertsFilePath the path to the trusted CA certificate file (PEM format)
+ * @return a {@link TlsPolicy} with hostname verification enabled and insecure connections disabled
+ */
+ public static TlsPolicy of(String trustCertsFilePath) {
+ return new TlsPolicy(trustCertsFilePath, null, null, false, true);
+ }
+
+ /**
+ * Create a TLS policy with mutual TLS (mTLS) authentication.
+ *
+ * @param trustCertsFilePath the path to the trusted CA certificate file (PEM format)
+ * @param keyFilePath the path to the client private key file (PEM format)
+ * @param certificateFilePath the path to the client certificate file (PEM format)
+ * @return a {@link TlsPolicy} configured for mutual TLS with hostname verification enabled
+ */
+ public static TlsPolicy ofMutualTls(String trustCertsFilePath, String keyFilePath, String certificateFilePath) {
+ return new TlsPolicy(trustCertsFilePath, keyFilePath, certificateFilePath, false, true);
+ }
+
+ /**
+ * Create an insecure TLS policy that accepts any certificate (for development only).
+ *
+ * @return a {@link TlsPolicy} with insecure connections allowed and hostname verification disabled
+ */
+ public static TlsPolicy ofInsecure() {
+ return new TlsPolicy(null, null, null, true, false);
+ }
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/TransactionPolicy.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/TransactionPolicy.java
new file mode 100644
index 0000000000000..228bdeed02054
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/TransactionPolicy.java
@@ -0,0 +1,30 @@
+/*
+ * 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.pulsar.client.api.v5.config;
+
+import java.time.Duration;
+
+/**
+ * Transaction configuration for the Pulsar client.
+ *
+ * @param timeout transaction timeout. If the transaction is not committed or aborted within this duration, it will
+ * be automatically aborted by the broker.
+ */
+public record TransactionPolicy(Duration timeout) {
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/package-info.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/package-info.java
new file mode 100644
index 0000000000000..afc47322a1c0b
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/package-info.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.
+ */
+
+/**
+ * Configuration value types and policy records.
+ *
+ * Immutable records used to group related configuration parameters in builder APIs,
+ * such as {@link org.apache.pulsar.client.api.v5.config.BatchingPolicy},
+ * {@link org.apache.pulsar.client.api.v5.config.BackoffPolicy},
+ * {@link org.apache.pulsar.client.api.v5.config.TlsPolicy}, and
+ * {@link org.apache.pulsar.client.api.v5.config.DeadLetterPolicy}.
+ */
+package org.apache.pulsar.client.api.v5.config;
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/internal/PulsarClientProvider.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/internal/PulsarClientProvider.java
new file mode 100644
index 0000000000000..faff56205d69a
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/internal/PulsarClientProvider.java
@@ -0,0 +1,123 @@
+/*
+ * 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.pulsar.client.api.v5.internal;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.function.Supplier;
+import org.apache.pulsar.client.api.v5.Checkpoint;
+import org.apache.pulsar.client.api.v5.MessageId;
+import org.apache.pulsar.client.api.v5.PulsarClientBuilder;
+import org.apache.pulsar.client.api.v5.PulsarClientException;
+import org.apache.pulsar.client.api.v5.auth.Authentication;
+import org.apache.pulsar.client.api.v5.schema.Schema;
+
+/**
+ * Service Provider Interface for the Pulsar client implementation.
+ *
+ *
The implementation module registers itself via {@code META-INF/services}
+ * using the standard Java {@link ServiceLoader} mechanism.
+ *
+ */
+public interface PulsarClientProvider {
+
+ // --- Client builder ---
+
+ PulsarClientBuilder newClientBuilder();
+
+ // --- MessageId ---
+
+ MessageId messageIdFromBytes(byte[] data) throws IOException;
+
+ MessageId earliestMessageId();
+
+ MessageId latestMessageId();
+
+ // --- Schemas ---
+
+ Schema bytesSchema();
+
+ Schema stringSchema();
+
+ Schema booleanSchema();
+
+ Schema byteSchema();
+
+ Schema shortSchema();
+
+ Schema intSchema();
+
+ Schema longSchema();
+
+ Schema floatSchema();
+
+ Schema doubleSchema();
+
+ Schema jsonSchema(Class pojo);
+
+ Schema avroSchema(Class pojo);
+
+ Schema protobufSchema(Class clazz);
+
+ Schema autoProduceBytesSchema();
+
+ // --- Checkpoint ---
+
+ Checkpoint checkpointFromBytes(byte[] data) throws IOException;
+
+ Checkpoint earliestCheckpoint();
+
+ Checkpoint latestCheckpoint();
+
+ Checkpoint checkpointAtTimestamp(Instant timestamp);
+
+ // --- Authentication ---
+
+ Authentication authenticationToken(String token);
+
+ Authentication authenticationToken(Supplier tokenSupplier);
+
+ Authentication authenticationTls(String certFilePath, String keyFilePath);
+
+ Authentication createAuthentication(String className, String params) throws PulsarClientException;
+
+ Authentication createAuthentication(String className, Map params) throws PulsarClientException;
+
+ // --- Singleton access ---
+
+ static PulsarClientProvider get() {
+ return Holder.INSTANCE;
+ }
+
+ /**
+ * Lazy initialization holder for the provider singleton.
+ */
+ final class Holder {
+ static final PulsarClientProvider INSTANCE = ServiceLoader.load(PulsarClientProvider.class)
+ .findFirst()
+ .orElseThrow(() -> new IllegalStateException(
+ "No PulsarClientProvider found on the classpath. "
+ + "Add pulsar-client to your dependencies."));
+
+ private Holder() {
+ }
+ }
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/internal/package-info.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/internal/package-info.java
new file mode 100644
index 0000000000000..da6dc6a8b0b8e
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/internal/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * Pulsar Client API v5.
+ */
+package org.apache.pulsar.client.api.v5.internal;
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/package-info.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/package-info.java
new file mode 100644
index 0000000000000..95b83146f85bf
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/package-info.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.
+ */
+
+/**
+ * Pulsar Client API v5.
+ *
+ * This is a redesigned client API that provides a cleaner, more modern interface for
+ * interacting with Apache Pulsar. Key design principles:
+ *
+ *
+ * - No partition concept -- topics are opaque; only per-key ordering is
+ * guaranteed when a key is specified.
+ * - Streaming vs queuing split -- {@link org.apache.pulsar.client.api.v5.StreamConsumer}
+ * provides ordered consumption with cumulative acknowledgment, while
+ * {@link org.apache.pulsar.client.api.v5.QueueConsumer} provides parallel unordered
+ * consumption with individual acknowledgment.
+ * - Managed subscriptions -- consumers use broker-managed position tracking
+ * with automatic redelivery and dead-letter support.
+ * - Checkpoint consumer -- {@link org.apache.pulsar.client.api.v5.CheckpointConsumer}
+ * provides unmanaged consumption with consistent {@link org.apache.pulsar.client.api.v5.Checkpoint}
+ * support, designed for connector frameworks (Flink, Spark).
+ * - Modern Java -- uses {@link java.time.Duration}, {@link java.util.Optional},
+ * {@link java.time.Instant}, records, and method-reference-friendly naming.
+ * - Transactions are first-class citizens, integrated directly into the
+ * producer and consumer APIs.
+ *
+ *
+ * Entry point: {@link org.apache.pulsar.client.api.v5.PulsarClient#builder()}.
+ */
+package org.apache.pulsar.client.api.v5;
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/schema/Schema.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/schema/Schema.java
new file mode 100644
index 0000000000000..da087a95b302a
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/schema/Schema.java
@@ -0,0 +1,207 @@
+/*
+ * 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.pulsar.client.api.v5.schema;
+
+import java.nio.ByteBuffer;
+import org.apache.pulsar.client.api.v5.internal.PulsarClientProvider;
+
+/**
+ * Defines how message values are serialized to bytes and deserialized from bytes.
+ *
+ * @param the type of the message value
+ */
+public interface Schema {
+
+ /**
+ * Encode a value to bytes.
+ *
+ * @param message the value to encode
+ * @return the serialized byte array representation of the value
+ */
+ byte[] encode(T message);
+
+ /**
+ * Decode bytes to a value.
+ *
+ * @param bytes the byte array to decode
+ * @return the deserialized value
+ */
+ T decode(byte[] bytes);
+
+ /**
+ * Decode bytes with a specific schema version. Useful for schema evolution.
+ *
+ * @param bytes the byte array to decode
+ * @param schemaVersion the schema version to use for decoding, or {@code null} for the latest version
+ * @return the deserialized value
+ */
+ default T decode(byte[] bytes, byte[] schemaVersion) {
+ return decode(bytes);
+ }
+
+ /**
+ * Decode from a ByteBuffer.
+ *
+ * @param data the {@link ByteBuffer} to decode, or {@code null}
+ * @return the deserialized value, or {@code null} if the input is {@code null}
+ */
+ default T decode(ByteBuffer data) {
+ if (data == null) {
+ return null;
+ }
+ byte[] bytes = new byte[data.remaining()];
+ data.get(bytes);
+ return decode(bytes);
+ }
+
+ /**
+ * The schema descriptor for broker-side negotiation.
+ *
+ * @return the {@link SchemaInfo} describing this schema's type and definition
+ */
+ SchemaInfo schemaInfo();
+
+ // --- Built-in schema factories ---
+
+ /**
+ * Get a schema for raw byte arrays (no serialization).
+ *
+ * @return a {@link Schema} for byte arrays
+ */
+ static Schema bytes() {
+ return PulsarClientProvider.get().bytesSchema();
+ }
+
+ /**
+ * Get a schema for UTF-8 strings.
+ *
+ * @return a {@link Schema} for {@link String} values
+ */
+ static Schema string() {
+ return PulsarClientProvider.get().stringSchema();
+ }
+
+ /**
+ * Get a schema for boolean values.
+ *
+ * @return a {@link Schema} for {@link Boolean} values
+ */
+ static Schema bool() {
+ return PulsarClientProvider.get().booleanSchema();
+ }
+
+ /**
+ * Get a schema for 8-bit signed integers.
+ *
+ * @return a {@link Schema} for {@link Byte} values
+ */
+ static Schema int8() {
+ return PulsarClientProvider.get().byteSchema();
+ }
+
+ /**
+ * Get a schema for 16-bit signed integers.
+ *
+ * @return a {@link Schema} for {@link Short} values
+ */
+ static Schema int16() {
+ return PulsarClientProvider.get().shortSchema();
+ }
+
+ /**
+ * Get a schema for 32-bit signed integers.
+ *
+ * @return a {@link Schema} for {@link Integer} values
+ */
+ static Schema int32() {
+ return PulsarClientProvider.get().intSchema();
+ }
+
+ /**
+ * Get a schema for 64-bit signed integers.
+ *
+ * @return a {@link Schema} for {@link Long} values
+ */
+ static Schema int64() {
+ return PulsarClientProvider.get().longSchema();
+ }
+
+ /**
+ * Get a schema for 32-bit floating point numbers.
+ *
+ * @return a {@link Schema} for {@link Float} values
+ */
+ static Schema float32() {
+ return PulsarClientProvider.get().floatSchema();
+ }
+
+ /**
+ * Get a schema for 64-bit floating point numbers.
+ *
+ * @return a {@link Schema} for {@link Double} values
+ */
+ static Schema float64() {
+ return PulsarClientProvider.get().doubleSchema();
+ }
+
+ /**
+ * Get a JSON schema for a POJO class.
+ *
+ * @param the POJO type
+ * @param pojo the class of the POJO to serialize and deserialize as JSON
+ * @return a {@link Schema} that encodes and decodes the POJO using JSON
+ */
+ static Schema json(Class pojo) {
+ return PulsarClientProvider.get().jsonSchema(pojo);
+ }
+
+ /**
+ * Get an Avro schema for a POJO class.
+ *
+ * @param the POJO type
+ * @param pojo the class of the POJO to serialize and deserialize using Avro
+ * @return a {@link Schema} that encodes and decodes the POJO using Avro
+ */
+ static Schema avro(Class pojo) {
+ return PulsarClientProvider.get().avroSchema(pojo);
+ }
+
+ /**
+ * Get a Protobuf schema for a generated message class.
+ *
+ * @param the Protobuf message type
+ * @param clazz the Protobuf generated message class
+ * @return a {@link Schema} that encodes and decodes the Protobuf message
+ */
+ static Schema protobuf(Class clazz) {
+ return PulsarClientProvider.get().protobufSchema(clazz);
+ }
+
+ /**
+ * Get a schema that auto-detects the topic schema and produces raw bytes accordingly.
+ *
+ * This schema validates that the bytes being produced are compatible with the
+ * schema configured on the topic.
+ *
+ * @return a {@link Schema} for producing raw bytes with automatic schema validation
+ */
+ static Schema autoProduceBytes() {
+ return PulsarClientProvider.get().autoProduceBytesSchema();
+ }
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/schema/SchemaInfo.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/schema/SchemaInfo.java
new file mode 100644
index 0000000000000..26b875f91b80f
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/schema/SchemaInfo.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.pulsar.client.api.v5.schema;
+
+import java.util.Map;
+
+/**
+ * Describes a schema for broker-side schema negotiation and compatibility checks.
+ */
+public interface SchemaInfo {
+
+ /**
+ * The name of the schema.
+ *
+ * @return the schema name
+ */
+ String name();
+
+ /**
+ * The type of the schema.
+ *
+ * @return the {@link SchemaType} indicating the serialization format
+ */
+ SchemaType type();
+
+ /**
+ * The raw schema definition bytes (e.g., Avro schema JSON, Protobuf descriptor).
+ *
+ * @return the schema definition as a byte array
+ */
+ byte[] schema();
+
+ /**
+ * Additional properties associated with the schema.
+ *
+ * @return an unmodifiable map of schema property key-value pairs
+ */
+ Map properties();
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/schema/SchemaType.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/schema/SchemaType.java
new file mode 100644
index 0000000000000..f80e9349ee79d
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/schema/SchemaType.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.pulsar.client.api.v5.schema;
+
+/**
+ * Types of schema supported by Pulsar.
+ */
+public enum SchemaType {
+ NONE,
+ BYTES,
+ STRING,
+ BOOLEAN,
+ INT8,
+ INT16,
+ INT32,
+ INT64,
+ FLOAT,
+ DOUBLE,
+ JSON,
+ AVRO,
+ PROTOBUF,
+ PROTOBUF_NATIVE,
+ KEY_VALUE,
+ AUTO_CONSUME,
+ AUTO_PUBLISH
+}
diff --git a/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/schema/package-info.java b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/schema/package-info.java
new file mode 100644
index 0000000000000..827fd8f438a1d
--- /dev/null
+++ b/pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/schema/package-info.java
@@ -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.
+ */
+
+/**
+ * Schema definitions for message serialization and deserialization.
+ *
+ * The {@link org.apache.pulsar.client.api.v5.schema.Schema} interface provides static
+ * factories for built-in types (STRING, JSON, AVRO, PROTOBUF, etc.) and is the entry
+ * point for all schema-related operations.
+ */
+package org.apache.pulsar.client.api.v5.schema;
diff --git a/pulsar-client-api-v5/src/test/java/org/apache/pulsar/client/api/v5/Examples.java b/pulsar-client-api-v5/src/test/java/org/apache/pulsar/client/api/v5/Examples.java
new file mode 100644
index 0000000000000..22de52c7cb2a5
--- /dev/null
+++ b/pulsar-client-api-v5/src/test/java/org/apache/pulsar/client/api/v5/Examples.java
@@ -0,0 +1,538 @@
+/*
+ * 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.pulsar.client.api.v5;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.concurrent.CompletableFuture;
+import org.apache.pulsar.client.api.v5.async.AsyncProducer;
+import org.apache.pulsar.client.api.v5.async.AsyncQueueConsumer;
+import org.apache.pulsar.client.api.v5.async.AsyncStreamConsumer;
+import org.apache.pulsar.client.api.v5.auth.AuthenticationFactory;
+import org.apache.pulsar.client.api.v5.config.BackoffPolicy;
+import org.apache.pulsar.client.api.v5.config.BatchingPolicy;
+import org.apache.pulsar.client.api.v5.config.CompressionPolicy;
+import org.apache.pulsar.client.api.v5.config.CompressionType;
+import org.apache.pulsar.client.api.v5.config.ConnectionPolicy;
+import org.apache.pulsar.client.api.v5.config.DeadLetterPolicy;
+import org.apache.pulsar.client.api.v5.config.MemorySize;
+import org.apache.pulsar.client.api.v5.config.SubscriptionInitialPosition;
+import org.apache.pulsar.client.api.v5.config.TlsPolicy;
+import org.apache.pulsar.client.api.v5.schema.Schema;
+
+/**
+ * Usage examples for the Pulsar v5 client API.
+ *
+ *
These are compile-checked examples that demonstrate the API surface. They are not
+ * runnable without a real Pulsar cluster and SPI implementation.
+ */
+@SuppressWarnings("unused")
+public class Examples {
+
+ // ==================================================================================
+ // 1. Client creation
+ // ==================================================================================
+
+ /** Minimal client — connect to localhost with defaults. */
+ void minimalClient() throws Exception {
+ try (var client = PulsarClient.builder()
+ .serviceUrl("pulsar://localhost:6650")
+ .build()) {
+ // use client...
+ }
+ }
+
+ /** Production client — TLS, auth, tuned connection pool. */
+ void productionClient() throws Exception {
+ try (var client = PulsarClient.builder()
+ .serviceUrl("pulsar+ssl://pulsar.example.com:6651")
+ .authentication(AuthenticationFactory.token("eyJhbGci..."))
+ .tlsPolicy(TlsPolicy.of("/etc/pulsar/ca.pem"))
+ .operationTimeout(Duration.ofSeconds(30))
+ .connectionPolicy(ConnectionPolicy.builder()
+ .connectionTimeout(Duration.ofSeconds(10))
+ .connectionsPerBroker(2)
+ .connectionBackoff(BackoffPolicy.exponential(
+ Duration.ofMillis(100), Duration.ofSeconds(30)))
+ .build())
+ .memoryLimit(MemorySize.ofMegabytes(64))
+ .build()) {
+ // use client...
+ }
+ }
+
+ // ==================================================================================
+ // 2. Producing messages (synchronous)
+ // ==================================================================================
+
+ /** Simple produce — send strings to a topic. */
+ void simpleProducer(PulsarClient client) throws Exception {
+ try (var producer = client.newProducer(Schema.string())
+ .topic("my-topic")
+ .create()) {
+
+ // Simple send
+ producer.newMessage().value("Hello Pulsar!").send();
+
+ // Send with key and properties
+ producer.newMessage()
+ .key("user-123")
+ .value("order placed")
+ .property("orderId", "A-100")
+ .eventTime(Instant.now())
+ .send();
+ }
+ }
+
+ /** High-throughput producer — batching + compression. */
+ void highThroughputProducer(PulsarClient client) throws Exception {
+ try (var producer = client.newProducer(Schema.json(SensorReading.class))
+ .topic("sensor-data")
+ .compressionPolicy(CompressionPolicy.of(CompressionType.ZSTD))
+ .batchingPolicy(BatchingPolicy.of(
+ Duration.ofMillis(10), 5000, MemorySize.ofMegabytes(1)))
+ .create()) {
+
+ for (int i = 0; i < 100_000; i++) {
+ producer.newMessage()
+ .key("sensor-" + (i % 16))
+ .value(new SensorReading("sensor-" + i, 22.5 + i * 0.01))
+ .send();
+ }
+ }
+ }
+
+ // ==================================================================================
+ // 3. Producing messages (asynchronous)
+ // ==================================================================================
+
+ /** Async producer — fire-and-forget with flush. */
+ void asyncProducer(PulsarClient client) throws Exception {
+ try (var producer = client.newProducer(Schema.string())
+ .topic("events")
+ .create()) {
+
+ AsyncProducer async = producer.async();
+
+ // Fire off many messages
+ CompletableFuture>[] futures = new CompletableFuture[1000];
+ for (int i = 0; i < 1000; i++) {
+ futures[i] = async.newMessage()
+ .key("key-" + (i % 10))
+ .value("event-" + i)
+ .send();
+ }
+
+ // Wait for all to complete
+ CompletableFuture.allOf(futures).join();
+
+ // Or flush to ensure everything is persisted
+ async.flush().join();
+ }
+ }
+
+ /** Async producer — pipeline with per-message callback. */
+ void asyncProducerWithCallbacks(PulsarClient client) throws Exception {
+ try (var producer = client.newProducer(Schema.string())
+ .topic("events")
+ .create()) {
+
+ for (int i = 0; i < 100; i++) {
+ final int idx = i;
+ producer.async().newMessage()
+ .value("event-" + i)
+ .send()
+ .thenAccept(msgId ->
+ System.out.printf("Message %d sent: %s%n", idx, msgId))
+ .exceptionally(ex -> {
+ System.err.printf("Message %d failed: %s%n", idx, ex.getMessage());
+ return null;
+ });
+ }
+ }
+ }
+
+ // ==================================================================================
+ // 4. Stream consumer (ordered, cumulative ack)
+ // ==================================================================================
+
+ /** Simple stream consumer — process messages in order. */
+ void streamConsumer(PulsarClient client) throws Exception {
+ try (var consumer = client.newStreamConsumer(Schema.string())
+ .topic("my-topic")
+ .subscriptionName("my-sub")
+ .subscriptionInitialPosition(SubscriptionInitialPosition.EARLIEST)
+ .subscribe()) {
+
+ while (true) {
+ Message msg = consumer.receive(Duration.ofSeconds(5));
+ if (msg == null) {
+ continue; // timeout, no message
+ }
+
+ System.out.printf("[%s] key=%s value=%s%n",
+ msg.publishTime(), msg.key().orElse("(none)"), msg.value());
+
+ // Cumulative ack — everything up to this message
+ consumer.acknowledgeCumulative(msg.id());
+ }
+ }
+ }
+
+ /** Batch receive — process messages in chunks. */
+ void streamConsumerBatchReceive(PulsarClient client) throws Exception {
+ try (var consumer = client.newStreamConsumer(Schema.json(SensorReading.class))
+ .topic("sensor-data")
+ .subscriptionName("analytics")
+ .subscribe()) {
+
+ while (true) {
+ Messages batch = consumer.receiveMulti(100, Duration.ofSeconds(1));
+
+ // Process the batch
+ for (var msg : batch) {
+ processSensorReading(msg.value());
+ }
+
+ // Ack up to the last message in the batch
+ consumer.acknowledgeCumulative(batch.lastId());
+ }
+ }
+ }
+
+ /** Async stream consumer — non-blocking receive loop. */
+ void asyncStreamConsumer(PulsarClient client) throws Exception {
+ try (var consumer = client.newStreamConsumer(Schema.string())
+ .topic("my-topic")
+ .subscriptionName("my-sub")
+ .subscribe()) {
+
+ AsyncStreamConsumer async = consumer.async();
+
+ // Recursive async receive chain
+ receiveNext(async);
+
+ // Keep the application alive...
+ Thread.sleep(60_000);
+ }
+ }
+
+ private void receiveNext(AsyncStreamConsumer async) {
+ async.receive().thenAccept(msg -> {
+ System.out.println("Received: " + msg.value());
+ async.acknowledgeCumulative(msg.id());
+
+ // Schedule next receive
+ receiveNext(async);
+ });
+ }
+
+ // ==================================================================================
+ // 5. Queue consumer (unordered, individual ack)
+ // ==================================================================================
+
+ /** Simple queue consumer — parallel processing with individual ack. */
+ void queueConsumer(PulsarClient client) throws Exception {
+ try (var consumer = client.newQueueConsumer(Schema.json(Order.class))
+ .topic("orders")
+ .subscriptionName("order-processor")
+ .subscribe()) {
+
+ while (true) {
+ Message msg = consumer.receive(Duration.ofSeconds(10));
+ if (msg == null) {
+ continue;
+ }
+
+ try {
+ processOrder(msg.value());
+ consumer.acknowledge(msg.id());
+ } catch (Exception e) {
+ // Trigger redelivery after backoff
+ consumer.negativeAcknowledge(msg.id());
+ }
+ }
+ }
+ }
+
+ /** Queue consumer with dead letter policy. */
+ void queueConsumerWithDLQ(PulsarClient client) throws Exception {
+ try (var consumer = client.newQueueConsumer(Schema.json(Order.class))
+ .topic("orders")
+ .subscriptionName("order-processor")
+ .ackTimeout(Duration.ofSeconds(30))
+ .negativeAckRedeliveryBackoff(
+ BackoffPolicy.exponential(Duration.ofSeconds(1), Duration.ofMinutes(5)))
+ .deadLetterPolicy(DeadLetterPolicy.of(5))
+ .subscribe()) {
+
+ while (true) {
+ Message msg = consumer.receive();
+ try {
+ processOrder(msg.value());
+ consumer.acknowledge(msg.id());
+ } catch (Exception e) {
+ consumer.negativeAcknowledge(msg.id());
+ // After 5 redeliveries → moves to dead letter topic automatically
+ }
+ }
+ }
+ }
+
+ /** Async queue consumer — high-throughput parallel processing. */
+ void asyncQueueConsumer(PulsarClient client) throws Exception {
+ try (var consumer = client.newQueueConsumer(Schema.json(Order.class))
+ .topic("orders")
+ .subscriptionName("parallel-processor")
+ .subscribe()) {
+
+ AsyncQueueConsumer async = consumer.async();
+
+ // Launch 10 concurrent receive loops
+ for (int i = 0; i < 10; i++) {
+ processQueueMessages(async);
+ }
+
+ Thread.sleep(60_000);
+ }
+ }
+
+ private void processQueueMessages(AsyncQueueConsumer async) {
+ async.receive().thenAccept(msg -> {
+ try {
+ processOrder(msg.value());
+ async.acknowledge(msg.id());
+ } catch (Exception e) {
+ async.negativeAcknowledge(msg.id());
+ }
+ processQueueMessages(async); // loop
+ });
+ }
+
+ // ==================================================================================
+ // 6. Transactions (exactly-once)
+ // ==================================================================================
+
+ /** Consume-transform-produce within a transaction. */
+ void transactionalProcessing(PulsarClient client) throws Exception {
+ try (var consumer = client.newQueueConsumer(Schema.json(Order.class))
+ .topic("raw-orders")
+ .subscriptionName("enricher")
+ .subscribe();
+ var producer = client.newProducer(Schema.json(EnrichedOrder.class))
+ .topic("enriched-orders")
+ .create()) {
+
+ while (true) {
+ Message msg = consumer.receive(Duration.ofSeconds(10));
+ if (msg == null) {
+ continue;
+ }
+
+ // Start transaction
+ Transaction txn = client.newTransaction();
+
+ try {
+ // Produce enriched order within the transaction
+ EnrichedOrder enriched = enrich(msg.value());
+ producer.newMessage()
+ .transaction(txn)
+ .value(enriched)
+ .send();
+
+ // Ack the source message within the same transaction
+ consumer.acknowledge(msg.id(), txn);
+
+ // Commit atomically — both the produce and ack
+ txn.commit();
+ } catch (Exception e) {
+ txn.abort();
+ }
+ }
+ }
+ }
+
+ // ==================================================================================
+ // 7. Delayed delivery
+ // ==================================================================================
+
+ /** Schedule messages for future delivery. */
+ void delayedDelivery(PulsarClient client) throws Exception {
+ try (var producer = client.newProducer(Schema.json(Reminder.class))
+ .topic("reminders")
+ .create()) {
+
+ // Deliver after a delay
+ producer.newMessage()
+ .value(new Reminder("Check status"))
+ .deliverAfter(Duration.ofMinutes(30))
+ .send();
+
+ // Deliver at a specific time
+ producer.newMessage()
+ .value(new Reminder("Daily report"))
+ .deliverAt(Instant.parse("2025-12-01T09:00:00Z"))
+ .send();
+ }
+ }
+
+ // ==================================================================================
+ // 8. Multi-topic queue consumer with pattern
+ // ==================================================================================
+
+ /** Subscribe to all topics matching a pattern. */
+ void patternSubscription(PulsarClient client) throws Exception {
+ try (var consumer = client.newQueueConsumer(Schema.string())
+ .topicsPattern("persistent://public/default/events-.*")
+ .subscriptionName("all-events")
+ .patternAutoDiscoveryPeriod(Duration.ofMinutes(1))
+ .subscribe()) {
+
+ while (true) {
+ Message msg = consumer.receive();
+ System.out.printf("Topic: %s, Value: %s%n", msg.topic(), msg.value());
+ consumer.acknowledge(msg.id());
+ }
+ }
+ }
+
+ // ==================================================================================
+ // 9. Mixing sync and async on the same resource
+ // ==================================================================================
+
+ /** Use sync for setup, async for data plane. */
+ void mixedSyncAsync(PulsarClient client) throws Exception {
+ // Sync creation
+ try (var producer = client.newProducer(Schema.string())
+ .topic("my-topic")
+ .create()) {
+
+ // Sync send for the important first message
+ MessageId first = producer.newMessage()
+ .key("init")
+ .value("system started")
+ .send();
+ System.out.println("First message: " + first);
+
+ // Switch to async for the hot path
+ AsyncProducer async = producer.async();
+ for (int i = 0; i < 10_000; i++) {
+ async.newMessage()
+ .value("data-" + i)
+ .send();
+ }
+ async.flush().join();
+ } // sync close waits for everything
+ }
+
+ // ==================================================================================
+ // 10. Checkpoint consumer (unmanaged, for connectors)
+ // ==================================================================================
+
+ /** Checkpoint consumer — read and checkpoint for external state management. */
+ void checkpointConsumer(PulsarClient client) throws Exception {
+ try (var consumer = client.newCheckpointConsumer(Schema.json(SensorReading.class))
+ .topic("sensor-data")
+ .startPosition(Checkpoint.earliest())
+ .create()) {
+
+ while (true) {
+ Messages